# Environment variables The single reference for every variable the frontend reads. Code resolves instance URLs in exactly one place — `src/lib/app/state/instance/resolve.ts` — and the tables below describe that behaviour. If you change a default or add a variable, update this file; `Dockerfile`, `.github/README.md` and `.env.development` only point here. `PUBLIC_*` variables are exposed to the browser bundle by SvelteKit. Anything without the prefix is server-only. All are read at **runtime** (via `$env/dynamic/*`), so the same image can be deployed to several environments. ## Backend instance For cookie-bearing requests outside `/api/proxy/[...path]`, the hook validates the session through `/api/me`. Proxy requests skip that preflight and relay the opaque cookie to the requested backend endpoint. An authenticated browser's 401 response expires the client session for that cookie generation, shows a persistent re-login prompt, and revalidates the root session data (`invalidate('app:session')`) once the router is idle; anonymous 200 responses from OptionalAuth endpoints cannot signal expiration. | Variable | Read by | Required | Description | | ------------------------------ | --------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `PUBLIC_INSTANCE_URL` | browser, server | **yes in production** | The Coves backend as reachable from the browser (e.g. `https://coves.social`). The server refuses to boot in production without it, even if `PUBLIC_INTERNAL_INSTANCE` is set, because the browser can only ever see this value. In dev the OAuth cookie is scoped to this host, so `hooks.server.ts` redirects any other hostname to it (RFC 8252 requires `127.0.0.1`, not `localhost`). The frontend must be served on this URL's origin (in production, the same origin as `ORIGIN`) for markdown links to its own `/profile/` or `/u/` pages to open in-app: a link opens in-app only when its origin equals this one. If the origins differ, those links keep their external href and never open a different identity. | | `PUBLIC_INTERNAL_INSTANCE` | server | no | Server-only shortcut to the backend for `hooks.server.ts` (`/api/me` validation) and the `/api/proxy` upstream — e.g. `http://appview:8080` on a Docker network, or `http://127.0.0.1:8081` in dev to skip the Caddy loop. Falls back to `PUBLIC_INSTANCE_URL`. | | `ALLOW_HTTP_INTERNAL_INSTANCE` | server | no | `"true"` to let the production proxy talk plaintext `http://` **only** to the origin of `PUBLIC_INTERNAL_INSTANCE` (which must then carry an explicit `http://` scheme). Any other `http://` target is still rejected with 400. | | `PUBLIC_INSTANCE_DOMAIN` | browser, server | no | The domain communities hosted by this instance are "local" to — the `origin` the AppView reports for them (`gaming@coves.social`). Community URLs drop the `@origin` suffix (`/c/gaming`) exactly when the origin equals this value; every other origin is linked as `/c/name@origin`. Defaults to the hostname of `PUBLIC_INSTANCE_URL`, which matches production (the AppView is served from the instance domain, its `did:web`). Set it when the two differ, e.g. in development where the AppView is reached at `127.0.0.1` but communities carry the configured instance domain. | | `PUBLIC_LOCK_TO_INSTANCE` | browser, server | no (default `true`) | When `true`, login is pinned to `PUBLIC_INSTANCE_URL`: the login UI hides the instance field and `POST /api/auth/login` rejects any other origin with 403. Set `false` to allow arbitrary instances. | Resolution precedence: - **Browser** → `PUBLIC_INSTANCE_URL`. - **Server** (SSR fetches, hooks, all proxy requests) → `PUBLIC_INTERNAL_INSTANCE`, else `PUBLIC_INSTANCE_URL`. - **Proxy** → the operator-configured upstream above, normalised to `https://` when it has no scheme. Session account data never selects the destination. The proxy relays the opaque session cookie as a Bearer credential and lets the requested backend endpoint validate it; the hook skips `/api/me` for proxy requests. - Neither set → hooks and proxy return a hard configuration error; nothing ever falls back to a third-party host. ## Deployment (adapter-node) | Variable | Required | Description | | ------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `ORIGIN` | **yes in production** | Public URL of this frontend (e.g. `https://coves.social`). adapter-node needs it for correct origin / form-action checks. Its scheme also decides whether requests count as https, which gates the CSP `upgrade-insecure-requests` directive. Production startup fails when it is unset (builds and development are exempt) — without it adapter-node trusts the client `Host` header for `event.url` and origin checks. | | `CSP_VIDEO_ORIGINS` | no | Origins `