lib/env.ts at boot. A failure throws a message naming the field rather than surfacing later as a confusing runtime error.
Required
AUTH_SECRET is more load-bearing than it looks. It signs sessions and derives the encryption key for stored TOTP secrets via HKDF.Rotating it invalidates every session and makes every stored 2FA secret undecryptable — enrolled users must fall back to a recovery code and re-enrol. Not a routine operation.To rotate without dropping sessions, @auth/core accepts an array and tries each in order: secret: [newSecret, previousSecret] in auth.config.ts, newest first.Optional
Everything below is optional. Each degrades a specific feature rather than breaking the app.Authentication providers
Providers are added conditionally, so an unset id removes the button rather than producing a broken one.
RESEND_FROM must be email@example.com or Name <email@example.com>. Checked at boot, because a malformed sender otherwise surfaces as a 422 the first time someone resets a password.
Object storage
Any S3-compatible service: AWS S3, Cloudflare R2, Backblaze B2, MinIO.
Payments
The API key without the IPN secret is the dangerous half-configuration: addresses get handed out and money arrives, but every callback that would credit it fails signature verification.Boot fails if the key is set without the other two, so this cannot be discovered at deposit time.
Signal engine
A relative or half-typed URL fails at
fetch time and looks identical to the engine being down — one is a config typo and the other is an outage, so the URL is validated at boot.
Assistant
Status page
Scheme is validated rather than just parsed. A
javascript: URL parses perfectly well and would put a script sink in the footer of every page — only http and https pass.The status page is hosted off this infrastructure on purpose: a status page served by the app it reports on says nothing during the outage that matters most.Media
Cross-field rules
Production checklist
-
AUTH_SECRETgenerated fresh, not copied from development -
DATABASE_URLpoints at production with connection limits set - S3 bucket configured — not the local fallback
-
NOWPAYMENTS_IPN_SECRETset alongside the API key -
RESEND_API_KEYand a verifiedRESEND_FROM -
AUTH_URLset to the real origin -
STATUS_PAGE_URLpointing at a monitor hosted elsewhere