Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,34 @@ Required GitHub secrets:
- `NETLIFY_AUTH_TOKEN` (gates deploy)
- `NETLIFY_SITE_ID` (gates deploy)

These three are the *only* env values that live in GitHub repo secrets.
Everything else needed by the build or runtime — `DATABASE_URL`,
`JWT_SECRET`, `SITE_URL`, `GOOGLE_*`, `RESULTS_IMPORT_API_KEY`, and the
`VITE_*` family — lives in the Netlify project env. The deploy job
shells out to `netlify deploy --prod --build`, which loads those values
into the Vite build and into the Functions runtime.

If `NETLIFY_AUTH_TOKEN` or `NETLIFY_SITE_ID` is missing, the deploy
job is skipped cleanly; the workflow stays green, and Netlify's own
git auto-deploy (still enabled as a fallback) takes over.
job is skipped cleanly; the workflow stays green. Netlify's git
auto-deploy is disabled (`stop_builds: true`), so a skipped deploy
means no production update — never let the workflow stay green and
silent in that state.

#### `VITE_*` env vars must not be marked secret in Netlify

Vite reads env vars at build time only. Netlify's "secret" flag hides
the value from the build environment (it is only injected into
Functions at request time). Marking a `VITE_*` value as secret is
therefore a foot-gun: the build silently emits an empty string and
the deployed bundle ships with that feature broken — analytics, the
PostHog client, the Google client id check, etc.

Rule: every `VITE_*` env var in Netlify must be a regular env var.
Backend-only secrets (`DATABASE_URL`, `JWT_SECRET`,
`GOOGLE_CLIENT_SECRET`, `RESULTS_IMPORT_API_KEY`) should stay marked
secret. If you ever need to audit, run
`netlify env:list --context production --plain | grep '^VITE_'` —
any masked value (`****`) is a misconfiguration.

## 4. API And Data Safety

Expand Down
10 changes: 10 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,16 @@ Notes:
- Source of truth for both frontend (`VITE_*`) and backend runtime env vars is Netlify's project env, not GitHub Actions secrets. GitHub secrets only carry the values needed to authenticate the deploy itself (`NETLIFY_AUTH_TOKEN`, `NETLIFY_SITE_ID`) plus `PRODUCTION_DATABASE_URL` for the migrate job.
- Netlify's git auto-deploy is disabled (`stop_builds: true`) so the only deploy path is GitHub Actions. `[build.environment] NODE_VERSION = "24.1.0"` is still pinned in `netlify.toml` for defence in depth if auto-deploys are ever re-enabled.

#### `VITE_*` env vars must not be marked secret

Netlify lets you mark an env var as containing secret values. Secret env vars are never exposed during a build — they only reach Functions at request time. That breaks any `VITE_*` value, because Vite *only* reads env at build time. A silent regression we hit during this work: the bundle shipped with empty `VITE_POSTHOG_KEY`, `VITE_ANALYTICS_ENABLED`, `VITE_ANALYTICS_PROVIDER`, and `VITE_POSTHOG_HOST` even though all four were set on the Netlify project, because all four had been flagged secret. PostHog analytics were effectively dead in prod with no error.

Operational rules:

- Any env var prefixed `VITE_` must be a regular (non-secret) Netlify env var. They are meant to be in the public browser bundle by design (PostHog uses a public client-side key, analytics flag is a boolean, etc.).
- Backend-only secrets (`DATABASE_URL`, `JWT_SECRET`, `GOOGLE_CLIENT_SECRET`, `RESULTS_IMPORT_API_KEY`) stay marked secret. They are read from `process.env` by the Lambda at request time and never need to appear in a build.
- Quick check during CI debugging: `netlify env:list --context production --plain | grep '^VITE_'`. If any value is masked with `****`, that variable is incorrectly flagged secret.

### Destructive Migrations And The Deploy Race

CI's `migrate-production` job and the deploy step are sequenced in the
Expand Down
Loading