Operators
Environment variables
The core deployment contract is validated through @repo/env (Zod-validated via @t3-oss/env-nextjs). Provider packages also validate their selected provider's credentials. Boot fails fast if a required production variable is missing—you'll see a clear build error before traffic reaches a broken instance.
.env.local.example in the repo root is the canonical reference. This page mirrors it.
Required for any working deploy
| Variable | Description |
|---|---|
TURSO_DATABASE_URL | libSQL/Turso connection URL. libsql://... for Turso Cloud or file:./local.db for a local SQLite file. DATABASE_URL is a URL-only compatibility fallback. |
TURSO_AUTH_TOKEN | Turso auth token. Empty or unset for local file-based DBs. |
BETTER_AUTH_SECRET | 32+ random bytes (e.g. openssl rand -base64 32). Used to sign session cookies + magic links. |
NEXT_PUBLIC_SAAS_URL | Same as above; the NEXT_PUBLIC_ prefix exposes it to client-side code for cross-origin redirects. |
NEXT_PUBLIC_MARKETING_URL | Public URL of the marketing site (e.g. https://brandbanta.com). |
INNGEST_SIGNING_KEY | From the Inngest Cloud dashboard. Used by Vercel to verify Inngest's incoming function calls. |
INNGEST_EVENT_KEY | From the Inngest Cloud dashboard. Used by our code to send events to Inngest. |
CRON_SECRET | A 32+ character random token; Vercel Crons automatically pass it via Authorization: Bearer ... to every /api/cron/* invocation. |
OPENROUTER_API_KEY | The platform's shared OpenRouter key for free-tier scans. Without this, free-tier workspaces can't run scans at all (BYOK orgs are unaffected). |
KEYRING_MASTER_KEY | 64-character hex key (32 bytes). AES-256-GCM key used to seal BYOK API keys at rest. Never rotate without re-encrypting existing rows — see below. |
If any of these are missing, the saas app fails to start.
Email delivery (required for verification emails)
| Variable | Description |
|---|---|
MAIL_PROVIDER | One of resend, plunk, nodemailer, postmark, or mailgun. Production rejects console. |
MAIL_FROM | Verified sender address (e.g. BrandBanta <hello@brandbanta.com>). |
| Provider key(s) | For example RESEND_API_KEY when using Resend. The production validator requires the selected provider's complete credentials. |
Without these, sign-up still works in dev (verification is bypassed) but production users can't verify their email.
Observability (strongly recommended for production)
| Variable | Description |
|---|---|
SENTRY_DSN | From the Sentry project settings. When unset, the SDK becomes a no-op — no errors crash. |
SENTRY_AUTH_TOKEN | Personal Access Token with Read & Write permission on Project + Release scopes. Used at build time to upload source maps. |
SENTRY_ORG | Your Sentry org slug. |
SENTRY_PROJECT | Your Sentry project slug. |
NEXT_PUBLIC_WEB_VITALS_URL | Optional endpoint to receive Web Vitals beacons. Leave unset to skip. |
Storage (required for file uploads — avatars, brand logos)
| Variable | Description |
|---|---|
S3_ENDPOINT | S3 endpoint URL (e.g. https://<account>.r2.cloudflarestorage.com for Cloudflare R2). |
S3_REGION | Region (e.g. auto for R2, us-east-1 for AWS). |
S3_ACCESS_KEY_ID | Access key. |
S3_SECRET_ACCESS_KEY | Secret key. |
NEXT_PUBLIC_AVATARS_BUCKET_NAME | Bucket for user and organization avatars. Defaults to avatars. |
Payments (parked behind feature flag until launch)
| Variable | Description |
|---|---|
NEXT_PUBLIC_BILLING_ENABLED | true to enable Stripe-based billing UI. Defaults to false. |
STRIPE_SECRET_KEY | From Stripe dashboard. |
STRIPE_WEBHOOK_SECRET | From Stripe webhook endpoint config. |
PRICE_ID_PRO_MONTHLY | Provider price identifier mapped to the Pro tier. |
PRICE_ID_PRO_YEARLY | Provider yearly price identifier mapped to the Pro tier. |
Leave the Stripe variables unset until you're ready to launch billing — the rest of the app works without them.
Optional / feature-gated
| Variable | Description |
|---|---|
TAVILY_API_KEY | Enables web-search grounding in the AI alias suggester. Without it, suggester falls back to LLM-only. |
CSP_REPORT_ONLY | Production enforces CSP by default. Set true only for a time-bounded diagnostic rollback to Report-Only mode. |
ENABLE_API_DOCS | true to expose /api/docs (OpenAPI Scalar UI) in production. Defaults to false; dev always shows it. |
OPENTELEMETRY_API_VERSION_PIN | Internal pin to 1.9.0 in pnpm-workspace.yaml. Don't override unless you've audited the Sentry/Drizzle/Better-Auth peer tree. |
Notes on rotation
BETTER_AUTH_SECRET— rotating invalidates all active sessions; users get logged out. Safe but disruptive.KEYRING_MASTER_KEY— DO NOT rotate without re-encrypting existing rows inorg_api_credential. There's no rotation script today; a rotation is a manual job: decrypt every row with the old key, re-encrypt with the new key, swap the env var. Document this in your ops runbook if you ever need to rotate (e.g. suspected compromise).CRON_SECRET— rotate by setting the new value in both Vercel env andvercel.json, deploy, then revoke the old. Brief overlap window is fine.INNGEST_SIGNING_KEY— rotate via Inngest Cloud dashboard; follow their docs to avoid mid-flight event signing breaks.
See also
- Deploying BrandBanta — full first-deploy checklist
- Cron jobs — what each scheduled task does
.env.local.examplein the repo — canonical list with inline comments