All config is read and zod-validated once at process startup (src/config/env.ts) — the process refuses to boot on a missing or malformed value, rather than failing on the first request that needs it.

Infrastructure

required
Postgres connection string. Must point at a database with the vector extension enabled (CREATE EXTENSION vector;) — see Architecture → Vector Index.
required
Used for OTP codes, rate limits, idempotency-adjacent short-lived tokens (validation_token, link_token, proof_token), and liveness lockouts.

External dependencies

required
Base URL of the face-engine service (/v1/embed, /v1/compare, /v1/liveness, /v1/health, /v1/version).
required
Base URL of the nin_bvn_simulator (/api/{nin,bvn}/{number}/validate, /photo).
default:"console"
console is the only implementation today — logs the OTP code instead of sending SMS. See src/clients/OTPClient.ts.

Auth

required
Signs access tokens. Refresh tokens are opaque UUIDs stored server-side in Session, not JWTs.
default:"900"
default:"2592000"

Matching & liveness

default:"0.4"
Kept equal to face-engine’s own COMPARE_THRESHOLD default so a decision doesn’t silently drift depending on whether the comparison happened inside face-engine (/v1/compare) or inside this service (cosine similarity against a stored vector, for self-verify and cross-registry checks).
default:"stub"
stub (always passes, loud startup warning) or heuristic (calls face-engine’s real /v1/liveness — itself a passive heuristic, not certified PAD). See src/clients/LivenessClient.ts.

Token TTLs

default:"300"
Single-use, short-expiry by design — the API’s guarantee that a passing verify-face actually happened before link is allowed to complete.
default:"300"
How long a passing NIN capture stays reusable for a BVN verify-face in the same session.
default:"120"
Gates PATCH/DELETE /users/{id} — requires a fresh passing /verify/self.
default:"60"
Server-enforced lockout after a liveness failure, on both /identify and /verify/self.

Other

default:"arcface-w600k_r50-v1"
Recorded against every canonical embedding written. GET /models also queries face-engine’s own /v1/version and surfaces both.
required
Keys the AES-256-GCM encryption used to store NIN/BVN plaintext at rest (src/lib/piiCrypto.ts) — a KMS-backed key is a flagged production-readiness item, not this dev default.

Local port remapping

docker-compose.yml maps Postgres to host port 5433 and Redis to 6380, not the standard 5432/6379 — this avoids clashing with a locally-installed Postgres/Redis that may already own those ports. Adjust DATABASE_URL/REDIS_URL to match if you change the compose file.