Environment Variables
Startup validation, required variables, and secure configuration for NIN Connect.
Overview
NIN Connect validates all security-critical environment variables at startup using a Zod schema. If any required variable is missing or malformed, the application crashes immediately rather than running in a degraded or insecure state.
This eliminates an entire class of bugs where a missing encryption key or database URL silently causes failures at runtime.
Validation Schema
The validation lives in lib/env.ts and runs when the module is first imported:
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().min(1),
ENCRYPTION_KEY: z.string().length(64, "Must be 64-char hex (32 bytes)"),
NEXT_PUBLIC_APP_URL: z.string().url(),
FIREBASE_ADMIN_PROJECT_ID: z.string().min(1),
FIREBASE_ADMIN_CLIENT_EMAIL: z.string().min(1),
FIREBASE_ADMIN_PRIVATE_KEY: z.string().min(1),
});
export const env = envSchema.parse(process.env);All server-side modules import from env instead of reading process.env directly. This ensures:
- No
undefinedvalues slip through ENCRYPTION_KEYis exactly 64 hex characters (32 bytes)NEXT_PUBLIC_APP_URLis a valid URL- Firebase credentials are present before the Admin SDK initializes
No Fallback Keys
Previous versions used a fallback value for HMAC signing when ENCRYPTION_KEY was not set:
// BEFORE (insecure)
const secret = process.env.ENCRYPTION_KEY ?? "fallback-dev-key";This meant the app could run with a publicly known signing key in production. The fallback has been removed. The sign() function now reads from the validated env.ENCRYPTION_KEY, which is guaranteed to exist and be the correct length.
Required Variables
| Variable | Purpose | Format |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | postgresql://... |
ENCRYPTION_KEY | AES-256-GCM encryption and HMAC signing | 64-char hex string |
NEXT_PUBLIC_APP_URL | Base URL for OAuth callbacks and connect pages | https://your-domain.com |
FIREBASE_ADMIN_PROJECT_ID | Firebase Admin SDK project | String |
FIREBASE_ADMIN_CLIENT_EMAIL | Firebase Admin SDK service account | Email string |
FIREBASE_ADMIN_PRIVATE_KEY | Firebase Admin SDK private key | PEM string |
Generating an Encryption Key
openssl rand -hex 32Or using Node.js:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".env.example
The repository includes a .env.example file with placeholder values for all required variables. Copy it and fill in real values:
cp .env.example .env.localThe .gitignore excludes all .env* files except .env.example, so real secrets are never committed.