Reference
Environment variables
The complete reference of environment variables Valcraven reads, whether each is required, and where it's set.
This is the full list of environment variables the template reads. The authoritative source for local dev is apps/web/.env.example — copy it to .env and fill in what you need. For production on Cloudflare Workers, non-secret config lives in apps/web/wrangler.toml under [vars], and secrets are set with wrangler secret put.
Almost everything is optional — Valcraven boots with just an auth secret and a database. Add variables as you turn features on.
| Value | Meaning |
|---|
| Config | Non-secret. Set in wrangler.toml [vars] for prod (and .env for local dev). |
| Secret | Sensitive. Set with wrangler secret put <NAME> for prod (and .env for local dev). |
| Binding | Provided automatically by a Cloudflare binding on Workers — no env var needed in prod. |
On local dev everything is read from .env regardless of the prod placement above. Client-exposed variables (prefixed NEXT_PUBLIC_) are inlined at build time — rebuild after changing them.
| Variable | Required? | Where set | Description |
|---|
BETTER_AUTH_SECRET | Yes | Secret | Signing secret for Better Auth sessions. Generate with openssl rand -base64 32. |
BETTER_AUTH_URL | Yes | Config | The app's origin. Better Auth uses it as its base URL and a trusted origin. Local dev: http://localhost:3000. In wrangler.toml [vars] for prod. |
APP_URL | Recommended | Config | App base URL used in emails and Stripe redirects. |
APP_NAME | No (default Valcraven) | Config | Display name used in emails and MFA enrollment. |
ADMIN_EMAILS | No | Config | Comma-separated emails auto-promoted to admin on DB init and signup (so admin survives a DB reset). |
| Variable | Required? | Where set | Description |
|---|
DATABASE_DRIVER | On Cloudflare: d1 | Config | Selects the DB backend. Set to d1 in prod [vars]. |
DATABASE_PATH | Node/SQLite dev | Config | Path to the local SQLite file (default ./data/valcraven.db). Ignored when DATABASE_URL is set. |
DATABASE_URL | For Postgres | Secret | Postgres connection string. When set, the app uses Postgres and DATABASE_PATH is ignored. |
In production, the database is Cloudflare D1, wired through the DB binding in wrangler.toml — not a connection string.
All optional — configure only the providers you want. See Getting Started → OAuth setup and the /configure-sso skill.
| Variable | Required? | Where set | Description |
|---|
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | For Google login | Secret | Google OAuth credentials. |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | For GitHub login | Secret | GitHub OAuth credentials. Needs a separate OAuth app per environment (GitHub allows one callback URL per app). |
APPLE_CLIENT_ID / APPLE_CLIENT_SECRET | For Apple login | Secret | Services ID + a short-lived signing JWT. Apple is prod-only in practice; the JWT expires (~6 months) and must be regenerated. |
FACEBOOK_CLIENT_ID / FACEBOOK_CLIENT_SECRET | For Facebook login | Secret | Meta/Facebook Login credentials. |
Transactional email goes through Cloudflare Email Service. On Workers it uses the EMAIL binding automatically; outside Workers it falls back to the REST API, and if nothing is configured, emails are logged to the console (dev mode).
| Variable | Required? | Where set | Description |
|---|
EMAIL | — | Binding | The send_email binding used on Workers — no config needed in prod. |
EMAIL_FROM | For real sends | Secret | The From address (e.g. Valcraven <noreply@yourdomain.com>). If unset it falls back to a placeholder and email silently fails — set it in prod. |
CLOUDFLARE_ACCOUNT_ID | Non-Workers email / flags / AI | Config | Cloudflare account id. Shared across email, flags, and AI features. In prod [vars]. |
CF_EMAIL_API_TOKEN | Non-Workers email | Secret | REST API token for Email Service outside Workers (Docker, VPS). |
EMAIL_TRACKING_SECRET | No (falls back to BETTER_AUTH_SECRET) | Secret | Signs email open/click tracking tokens. Set a dedicated value in prod. |
CF_ANALYTICS_API_TOKEN | For delivery poller / AI usage | Secret | Cloudflare API token. Zone Analytics: Read for the email delivery poller; Account Analytics: Read for the AI usage dashboard (one token can hold both). |
CLOUDFLARE_ZONE_ID | For delivery poller | Secret | Zone id of the domain you send email from. |
Feature flags use Cloudflare Flagship via the FLAGS binding on Workers, or the OpenFeature SDK fallback elsewhere. See Feature flags.
| Variable | Required? | Where set | Description |
|---|
FLAGS | — | Binding | The [[flagship]] binding used on Workers. Ships commented out in wrangler.toml (Flagship is private beta) — until you uncomment it with a real app_id, flag evaluation uses the OpenFeature SDK fallback. |
FLAGSHIP_APP_ID | Non-Workers flags | Config | OpenFeature SDK app id for the fallback path. |
CF_API_TOKEN | Non-Workers flags | Secret | Token with the Flagship Evaluate permission. |
FLAG_<NAME> | No | Config | Local dev per-flag override (e.g. FLAG_MAINTENANCE_MODE=true). Flags default off in dev. |
All optional — set them to enable billing.
| Variable | Required? | Where set | Description |
|---|
STRIPE_SECRET_KEY | For billing | Secret | Stripe API secret key. |
STRIPE_PRICE_ID | For subscriptions | Config | Recurring subscription price id. |
STRIPE_LIFETIME_PRICE_ID | For lifetime deal | Config | One-time lifetime price id. |
STRIPE_WEBHOOK_SECRET | For webhook sync | Secret | Verifies POST /api/stripe/webhook signatures. |
Chat runs through the realtime Worker (apps/realtime) over WebSockets. The agent can run BYOK Claude or Cloudflare Workers AI open models. See AI chat.
| Variable | Required? | Where set | Description |
|---|
ANTHROPIC_API_KEY | For BYOK Claude | Secret | Anthropic API key. Can also be set (encrypted) at runtime from /admin/ai-models. |
CLOUDFLARE_API_TOKEN | Local Workers-AI dev | Secret | An "Edit Cloudflare Workers" token (+ Workers AI: Read) so wrangler dev can proxy the AI binding. Not needed if you wrangler login. |
CHAT_MODEL | No (defaults to Sonnet) | Config | Default chat model id. |
CHAT_MAX_TOKENS | No | Config | Max tokens per response. |
AI_GATEWAY_ID | No | Config | Cloudflare AI Gateway slug for usage/cost analytics and caching. In prod [vars]. |
AI_GATEWAY_TOKEN | For an authenticated gateway | Secret | Sent as cf-aig-authorization when the gateway requires it. |
PROVIDER_KEY_KEK | No (falls back to BETTER_AUTH_SECRET) | Secret | Key-encryption key for admin-set provider keys. Set a dedicated value in prod. |
OPENAI_API_KEY / GOOGLE_API_KEY | No | Secret | Optional keys for other providers. |
The web app reaches the realtime Worker through the REALTIME service binding. Server-to-server calls without a user cookie use a shared secret. See Realtime.
| Variable | Required? | Where set | Description |
|---|
REALTIME | — | Binding | Service binding to the realtime Worker. |
AGENTS_INTERNAL_SECRET | For live realtime push | Secret | Shared secret for server-to-server calls. Must match the same value on the realtime Worker. When unset, live push is skipped (notifications still persist). |
REALTIME_ORIGIN | No | Config | Origin of the realtime Worker's /realtime/* routes. Defaults to APP_URL, then the wrangler-dev port. |
| Variable | Required? | Where set | Description |
|---|
ELEVENLABS_API_KEY | For TTS | Secret | ElevenLabs API key. Unset = voice is text-only. |
ELEVENLABS_VOICE_ID | No | Config | Default TTS voice id. |
| Variable | Required? | Where set | Description |
|---|
WAITLIST_MODE | No | Config | Gate signups behind invite codes. See Waitlist. |
NEXT_PUBLIC_WAITLIST_MODE | No | Config | Client mirror of the above (build-time inlined). |
API_KEYS_ENABLED | No | Config | Server flag enabling per-user API-key auth and the management routes. See API keys. |
NEXT_PUBLIC_API_KEYS_ENABLED | No | Config | Shows the API-keys card in /settings (build-time inlined). Enable together with the server flag. |
| Variable | Required? | Where set | Description |
|---|
ANALYTICS_PROVIDER | No | Config | posthog, plausible, console, or none. |
POSTHOG_API_KEY / POSTHOG_HOST | For PostHog | Config | PostHog project key and host. |
PLAUSIBLE_DOMAIN / PLAUSIBLE_HOST | For Plausible | Config | Plausible domain and host. |
| Variable | Required? | Where set | Description |
|---|
LOOPS_API_KEY | No | Secret | Activates the Loops.so drip engine when set. |
| Variable | Required? | Where set | Description |
|---|
NEXT_PUBLIC_SENTRY_DSN | No | Config | Sentry DSN (build-time inlined). |
SENTRY_AUTH_TOKEN | For source-map upload | Secret | Sentry token for uploading source maps. |
Scheduled jobs run via an in-process scheduler in Node dev and via Workers Cron Triggers in prod. See Deployment.
| Variable | Required? | Where set | Description |
|---|
ENABLE_CRON | Local Node dev | Config | Runs the in-process setInterval scheduler. Off by default. |
CRON_SECRET | For prod cron & internal endpoints | Secret | Authorizes /api/cron and internal server-to-server endpoints. Without it those return 503 (harmless no-ops). |
AUDIT_LOG_RETENTION_DAYS | No (default 90) | Config | Days before audit-log entries are pruned. |
HEALTH_ALERT_EMAIL | No (falls back to ADMIN_EMAILS) | Config | Recipients alerted on a health failure transition. |
HEALTH_ALERT_WEBHOOK_URL | No | Config | URL POSTed the probe JSON on a failure transition (Slack/PagerDuty/etc.). |
HEALTH_HEARTBEAT_URL | No | Config | Dead-man's-switch URL pinged on each healthy snapshot. |
APP_VERSION | No | Config | Version string surfaced by /api/health. Falls back to package.json. |
The Expo app in apps/mobile reads a single variable from apps/mobile/.env (its own file, separate from the web app). See The Expo app.
| Variable | Required? | Where set | Description |
|---|
EXPO_PUBLIC_API_URL | Yes (mobile) | apps/mobile/.env | Base URL of the Valcraven backend the app authenticates against. Must be reachable from the device (default http://localhost:3013). |
Several features that need an env var locally are provided by a binding in production, configured in apps/web/wrangler.toml rather than as an env var:
| Binding | Backs |
|---|
DB | Cloudflare D1 (database) |
STORAGE | Cloudflare R2 (file/object storage) |
EMAIL | Cloudflare Email Service |
FLAGS | Cloudflare Flagship (feature flags) — commented out by default (private beta) |
REALTIME | Service binding to the realtime Worker |
CAMPAIGN_QUEUE | Cloudflare Queue (campaign send) |
See Deployment → Secrets & env for the full deploy-time setup.