Skip to content

Environment variables

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.

VarRequiredDefaultPurpose
DATABASE_URLyesPostgres connection string. Neon hostnames route to the HTTP driver; anything else routes to postgres-js.
SADIE_DB_DRIVERnoautoForce driver. neon-http or postgres. Use when the hostname does not disambiguate.
SADIE_DB_POOL_MAXno5Max pool size for the postgres-js driver. Ignored by neon-http.
VarRequiredDefaultPurpose
APP_PORTno3000Port for next dev.

Yjs y-websocket runs out-of-process at scripts/collab-server.mjs.

VarRequiredDefaultPurpose
NEXT_PUBLIC_COLLAB_WS_URLnows://127.0.0.1:1234Client-side websocket URL. Public, embedded in the browser bundle.
COLLAB_PORTno1234Server bind port.
COLLAB_HOSTno127.0.0.1Server 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.

VarRequiredDefaultPurpose
X_BEARER_TOKENnoX 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_STREAMno0Set to 1 on X API Pro+ tiers to enable the Filtered Stream path.
VarRequiredDefaultPurpose
SADIE_CRON_SECRETonly if using schedulerShared 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'))".
VarRequiredDefaultPurpose
SADIE_ENCRYPTION_KEYyes in productionauto-generated in dev32-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.

VarRequiredDefaultPurpose
OPENAI_API_KEYone of theseOpenAI key.
ANTHROPIC_API_KEYone of theseAnthropic 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.

VarRequiredDefaultPurpose
AI_FRONTIER_SMALL_PROVIDERnoanthropicVendor for tier1 (frontier small).
AI_FRONTIER_SMALL_MODELnoclaude-haiku-4-5-20251001Model name.
AI_FRONTIER_WORKHORSE_PROVIDERnoanthropicVendor for tier2 (workhorse synthesis).
AI_FRONTIER_WORKHORSE_MODELnoclaude-sonnet-4-6Model name.
AI_FRONTIER_REASONING_PROVIDERnoanthropicVendor for tier3 (deliberate reasoning).
AI_FRONTIER_REASONING_MODELnoclaude-opus-4-7Model name.
AI_FRONTIER_EMBEDDINGS_PROVIDERnoopenaiEmbeddings vendor.
AI_FRONTIER_EMBEDDINGS_MODELnotext-embedding-3-smallEmbedding model.
VarRequiredDefaultPurpose
SADIE_ALLOW_LOCAL_AI_STUBno1Set 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:

Terminal window
pnpm verify:health

That spawns next dev, bootstraps a session, and curls Today, Chats, Studio, Memory, Feeds, Access, and Settings.