# Environment and secrets

Keep Node, Worker and native configuration aligned without exposing credentials.

| 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 [#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 [#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](/docs/development/runbooks#tenant-statements-fail-after-a-database-or-driver-change)). | 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-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 [#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 [#key-files]

- [`packages/env/src/server.ts`](https://github.com/CarlosZiegler/autofolio/blob/main/packages/env/src/server.ts)
- [`packages/env/src/web.ts`](https://github.com/CarlosZiegler/autofolio/blob/main/packages/env/src/web.ts)
- [`packages/env/src/runtime-flags.ts`](https://github.com/CarlosZiegler/autofolio/blob/main/packages/env/src/runtime-flags.ts)
- [`packages/env/src/native.ts`](https://github.com/CarlosZiegler/autofolio/blob/main/packages/env/src/native.ts)
- [`apps/web/wrangler.jsonc`](https://github.com/CarlosZiegler/autofolio/blob/main/apps/web/wrangler.jsonc)

## Select the runtime [#select-the-runtime]

<Tabs id="environment-runtime">
<Tab title="Node development">

```sh
# Reads apps/web/.env through the Node configuration path.
pnpm dev:web
```

Use the direct development database URL. This runtime does not emulate Worker sockets.

</Tab>
<Tab title="Built Worker">

```sh
pnpm --filter web build
pnpm --filter web exec wrangler dev -c dist/server/wrangler.json --local --port 8790
```

Supply local Worker variables and an isolated Hyperdrive connection override. This exercises the built workerd artifact.

</Tab>
</Tabs>

