Every environment variable Sadie reads, grouped by what it controls. .env.example is the canonical source; this page mirrors it and adds required/optional annotations.
| Var | Required | Default | Purpose |
|---|
DATABASE_URL | yes | | Postgres connection string. Neon hostnames route to the HTTP driver; anything else routes to postgres-js. |
SADIE_DB_DRIVER | no | auto | Force driver. neon-http or postgres. Use when the hostname does not disambiguate. |
SADIE_DB_POOL_MAX | no | 5 | Max pool size for the postgres-js driver. Ignored by neon-http. |
| Var | Required | Default | Purpose |
|---|
APP_PORT | no | 3000 | Port for next dev. |
Yjs y-websocket runs out-of-process at scripts/collab-server.mjs.
| Var | Required | Default | Purpose |
|---|
NEXT_PUBLIC_COLLAB_WS_URL | no | ws://127.0.0.1:1234 | Client-side websocket URL. Public, embedded in the browser bundle. |
COLLAB_PORT | no | 1234 | Server bind port. |
COLLAB_HOST | no | 127.0.0.1 | Server bind host. |
Solo users can skip Studio multiplayer entirely. Local drafts persist via y-indexeddb with no server.
Posting is out of scope. The adapter exposes Recent Search, Filtered Stream, and Post Counts only.
| Var | Required | Default | Purpose |
|---|
X_BEARER_TOKEN | no | | X API v2 bearer. When set, Feeds uses the real API. When empty, falls back to local mock fixtures with a “mock adapter · read-only” badge. |
X_USE_FILTERED_STREAM | no | 0 | Set to 1 on X API Pro+ tiers to enable the Filtered Stream path. |
| Var | Required | Default | Purpose |
|---|
SADIE_CRON_SECRET | only if using scheduler | | Shared bearer token that authenticates the scheduler against /api/compile/run and /api/feeds/refresh-all. Generate with node -e "console.log(require('crypto').randomBytes(32).toString('hex'))". |
| Var | Required | Default | Purpose |
|---|
SADIE_ENCRYPTION_KEY | yes in production | auto-generated in dev | 32-byte key as hex (64 chars) or base64. Encrypts per-user API keys stored in sadie_settings.payload.userApiKeys. In development, auto-generated and appended to .env.local on first use. In production, set explicitly or the app refuses to boot. Rotating invalidates previously-encrypted keys. See Encryption. |
At least one provider key is needed for real chat. Missing all of them falls back to the local deterministic stub in development only.
| Var | Required | Default | Purpose |
|---|
OPENAI_API_KEY | one of these | | OpenAI key. |
ANTHROPIC_API_KEY | one of these | | Anthropic key. Preferred when both are set. |
All optional. When a raw vendor key is present without tier vars, the router picks sensible defaults from DEFAULT_MODELS in packages/ai/src/model-router.ts: Haiku for tier0/tier1, Sonnet for tier2, Opus for tier3, OpenAI text-embedding-3-small for embeddings. See Model routing for the task-class to tier table.
| Var | Required | Default | Purpose |
|---|
AI_FRONTIER_SMALL_PROVIDER | no | anthropic | Vendor for tier1 (frontier small). |
AI_FRONTIER_SMALL_MODEL | no | claude-haiku-4-5-20251001 | Model name. |
AI_FRONTIER_WORKHORSE_PROVIDER | no | anthropic | Vendor for tier2 (workhorse synthesis). |
AI_FRONTIER_WORKHORSE_MODEL | no | claude-sonnet-4-6 | Model name. |
AI_FRONTIER_REASONING_PROVIDER | no | anthropic | Vendor for tier3 (deliberate reasoning). |
AI_FRONTIER_REASONING_MODEL | no | claude-opus-4-7 | Model name. |
AI_FRONTIER_EMBEDDINGS_PROVIDER | no | openai | Embeddings vendor. |
AI_FRONTIER_EMBEDDINGS_MODEL | no | text-embedding-3-small | Embedding model. |
| Var | Required | Default | Purpose |
|---|
SADIE_ALLOW_LOCAL_AI_STUB | no | 1 | Set to 0 to disable the local deterministic LLM stub even in non-production. Useful when testing provider fallbacks. |
After filling .env.local, confirm the app boots and every canonical route 200s:
That spawns next dev, bootstraps a session, and curls Today, Chats, Studio, Memory, Feeds, Access, and Settings.