Infisical environment & secrets guide (apps/web)
Infisical environment & secrets guide (apps/web)
Secrets never live in apps/web/.env. That file is empty on purpose. All
runtime secrets come from Infisical and are injected into the process by the
Infisical CLI. The workspace is configured in .infisical.json
(workspaceId 66410021-9c0a-4075-803b-f9286f21145f).
apps/web/.env.exampleis documentation only. It is the catalogue of
every variable the web app understands, with a comment per key. Do not copy
it into a real.envand paste secret values there; nothing loads it at
runtime. Read it to learn what a key means, never to source its value.To run anything that needs secrets (the dev server, scripts, one-off tasks),
wrap the command in the Infisical CLI so it fetches thedevenvironment and
exports the variables into the child process:# canonical dev server (defined as a package.json script) bun run dev:infisical # = infisical run --env=dev -- vite dev # any other secret-needing command uses the same wrapper: infisical run --env=dev -- <command>apps/web/.envis intentionally empty. Do not hand-fill it. If you need a
value locally that Infisical does not hold, set it in your shell or pass it
inline rather than committing it.The config loader (
src/server/config.ts) resolves each key as
infisicalSnapshot[key] ?? process.env[key], trimmed, so a value present in
Infisical wins over a same-named shell var. An unconfigured Infisical (no
INFISICAL_CLIENT_ID/SECRET/PROJECT_ID) is a supported no-op and everything
falls back to plain env vars.Infisical’s
devenvironment’sR2_*andSESSION_SECRETare intentionally
kept in sync with the team’s local Garage setup (see
.agents/docs/home-worker-local-run.md) rather than pointing at real
Cloudflare R2 — a deliberate choice while the team is small (2 devs).
Anyone expectingdev:infisicalto hit real R2 needs to know this changed;
reverting means resettingR2_ENDPOINT/R2_ACCESS_KEY_ID/
R2_SECRET_ACCESS_KEY/R2_BUCKETin Infisical dev back to real R2 values
and removingR2_REGION/R2_FORCE_PATH_STYLE.The CLI reuses your
infisical loginsession. Runinfisical loginonce if a
wrapped command errors with an auth/credentials message.
Infisical folder scheme (1 level, no nesting)
/(root) — app-runtime secrets consumed by apps/web viacfg.get/
process.env/import.meta.env./stripe— Stripe keys + pricing (STRIPE_*,*_PRICE_CENTS). The key set
matches across dev and prod; only values differ (test vs live ids)./infra— prod only, deploy/infra-only service config NOT consumed by
apps/web at runtime (Caddy’sCLOUDFLARE_API_TOKEN+MCP_SQLITE_API_KEY,
Phoenix server configPHOENIX_SECRET/PHOENIX_ENABLE_AUTH/
PHOENIX_ENABLE_MCP_SERVER). Synced by the VPS deploy into service-specific
env files. Do not create/infrain dev.
Intentional dev/prod parity gaps — do NOT “fix” these
These keys exist in one env only because they only make sense there. Forcing
parity breaks things:
| Key | In | Why |
|---|---|---|
R2_REGION, R2_FORCE_PATH_STYLE |
dev only | dev points at local Garage; prod at real R2 (see above). |
DATABASE_URL |
prod only | There is no Turso today; the prod DB is a local libSQL/SQLite file (see data-model.md). |
INTERNAL_BASE_URL |
prod only | RunPod completion webhook callback; dev doesn’t dispatch to real RunPod. |
PHOENIX_SECRET, PHOENIX_ENABLE_AUTH, PHOENIX_ENABLE_MCP_SERVER |
prod only | Phoenix server config, read by deploy/phoenix/phoenix.env, not the app. Dev Phoenix runs without auth. |
PHOENIX_API_KEY, PHOENIX_COLLECTOR_ENDPOINT |
prod only | app-side OTel export target; dev defaults to http://localhost:6006 with no auth. |
POSTHOG_API_KEY, POSTHOG_PERSONAL_API_KEY |
prod only | optional; empty in dev = server-side analytics disabled locally. |
RUNPOD_ENDPOINT_ID_SMALL_MUSIC, RUNPOD_ENDPOINT_ID_SMALL_SFX |
— | endpoint ids; deliberately absent from the config-audit manifest while no job requests a small variant. |
bun run buildhard-fails on a missing required env var. The build
script runsscripts/audit-build-env.tsfirst, which reuses the same
CONFIG_KEYSmanifest as the runtime audit and exits 1 (printing each gap
and its impact) beforevite buildruns. The runtime audit stays non-fatal
at boot on purpose (a downed server is its own incident); the build is where
a missing secret turns into a failed deploy instead of a broken prod. Run it
with dev secrets viamise run build:web
(=cd apps/web && infisical run --env=dev -- bun run build).