| 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. |
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 setup to populate or repair configuration and pnpm 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