| Surface | Local source | Visibility |
|---|---|---|
| Node scripts and web development | apps/web/.env | Server variables stay server-only |
| Local workerd | apps/web/.dev.vars and Wrangler bindings | Worker configuration |
| Expo | apps/mobile/.env | EXPO_PUBLIC_ values are bundled into the app |
| Production/preview | Worker secrets, bindings and workflow inputs | Server secrets must not become build-time public values |
packages/env validates each boundary separately. Web public configuration includes VITE_SITE_URL; native uses EXPO_PUBLIC_SERVER_URL. Never put credentials under public prefixes. Do not paste complete environment files into logs, issues or documentation.
Database and storage
Node uses DATABASE_URL. Production workerd uses Hyperdrive; its connection string belongs to the current invocation. Keep Hyperdrive caching disabled. Local built-Worker tests can override the Hyperdrive connection string with an isolated password-bearing local URL.
R2 credentials, bucket configuration and files gateway settings must describe the same environment. Public file URLs are an application concern; private objects still require authorization through the gateway.
Runtime switches
Four deployment switches are declared in packages/env/src/runtime-flags.ts and read on every call, never cached, so changing one on the Worker takes effect without a code change. Unset and empty mean the default. A value that does not parse never becomes a different setting: the reader keeps the safe behavior and logs [env] <KEY>=<value> is invalid; … once per isolate.
| Variable | Default | Accepted values | Behavior | Invalid value |
|---|---|---|---|---|
DB_TENANT_PIPELINE | pipelined | 0, 1 | 0 is the kill switch: tenant statements fall back to explicit BEGIN/COMMIT (runbook). | Pipelining stays on, and the value is logged. |
TENANCY_PROBE | off | 0, 1 | 1 mounts tenancy.probe for the RLS preview proof. Previews set it; the production deploy refuses it. | The probe stays unmounted, and the value is logged. |
TRUSTED_PROXY_HEADER | unset: the socket address is the client IP | a header name, such as x-forwarded-for | On Node behind your own proxy, the header the proxy appends the client to. Cloudflare Workers always use cf-connecting-ip and ignore it. | Proxy headers are ignored (the socket address is used), and the value is logged. |
TRUSTED_PROXIES | 1 when TRUSTED_PROXY_HEADER is set | a positive integer | How many trusted proxies append to that header; the client is that many entries from the right. | The proxy header is ignored instead of falling back to one hop, and the value is logged. |
Dev seed sign-in
DEV_SEED_LOGIN=true in apps/web/.env mounts POST /api/auth/dev/seed-sign-in on pnpm --filter web dev. The mobile dev link autofolio://dev/<board>?lang=pt-BR|en-US|de-DE (Maestro flows, QA) calls it: it creates the demo User demo-<pt|en|de>@example.com once, loads handoff/data/seed.json into its personal Organization with the idempotent db:seed:autofolio seed and signs the app in. Off by default; pnpm test:mobile:server (a disposable database) turns it on. It is honoured only with NODE_ENV=development: a production build or runtime never mounts it, and the path answers 404.
Optional providers
Payments require provider selection and provider credentials, plus their visibility flags. AI requires provider configuration and ai.assistant. Email chooses an available transport in a defined order. A flag alone cannot configure an external account.
Run pnpm run setup to populate or repair configuration and pnpm run doctor to diagnose it. Keep environment examples descriptive; real values belong in ignored local files or the intended secrets store.
Key files
packages/env/src/server.tspackages/env/src/web.tspackages/env/src/runtime-flags.tspackages/env/src/native.tsapps/web/wrangler.jsonc