Skip to content

Stop documenting the workspace anon key as a browser build arg - #19

Open
schlunsen wants to merge 1 commit into
mainfrom
fix/no-anon-key-in-browser-bundles
Open

Stop documenting the workspace anon key as a browser build arg#19
schlunsen wants to merge 1 commit into
mainfrom
fix/no-anon-key-in-browser-bundles

Conversation

@schlunsen

Copy link
Copy Markdown
Collaborator

Summary

The skill instructed apps to pass the workspace Supabase anon key as VITE_SUPABASE_ANON_KEY through --build-arg, described as the approach for "frontend-only apps using the anon key". Both halves of that are wrong on n0.

A build arg feeding a VITE_* variable is not a secret. Vite inlines it into the JS bundle, so it is served to every visitor in plain text and readable with View Source.

The anon key here is workspace-wide, not per-app. Vanilla Supabase scopes its anon key to a single project, which is what makes the usual "anon key in the browser + RLS" advice sound. On n0, one key spans every app's app_* schema and every member's private u_* schema on the instance — so the upstream reasoning does not carry over and the blast radius is the whole workspace. This has already produced one incident, where data in a private u_* schema was published via a grant to anon.

The rule is: the database is never queried directly from the browser, always through a backend. app_data: true already gives an app server-side N0_APP_SUPABASE_* credentials at runtime, scoped to its own schema, which is the supported path.

Changes

  • Replace "Using the Workspace Supabase (simpler alternative)" with "🚫 Never put the workspace anon key in a browser bundle", stating the rule and why the upstream reasoning does not transfer.
  • Drop VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY from the Dockerfile and CI workflow templates, leaving only genuinely public build args, plus a note that a build arg is not a secret.
  • Flag it at the two other places the key was reachable:
    • the App Data env table, where "Anon key" sits right next to a VITE_-shaped temptation — clarified that it is only Kong's gateway header, not what authorises the app;
    • the quick reference, which told you to fetch workspace credentials into .env and run app migrations via POST /workspaces/{id}/supabase/sql/. That endpoint is workspace-admin-only, runs as supabase_admin (a Postgres superuser) against the shared public schema, and is not replayed on a fresh deploy. App schemas belong in committed migrations/*.sql.

Verification

I scanned the deployed apps on Moon before writing this: 34 apps enumerated, 6 publicly reachable (the rest sit behind n0 SSO), all 6 deep-scanned including nested chunk imports — no anon keys found in any bundle. So this documents a trap nobody has stepped in yet rather than cleaning up after a leak. The private-schema incident referenced above came through the agent SQL tool, not through a bundle.

No corresponding change is needed in README.md — it does not mention the pattern.

The skill told apps to pass the workspace Supabase anon key as
`VITE_SUPABASE_ANON_KEY` via `--build-arg`, for "frontend-only apps
using the anon key". Both halves of that are wrong on n0.

A `--build-arg` feeding a `VITE_*` variable is not a secret: Vite inlines
it into the JS bundle, so it ships to every visitor in plain text.

And the anon key here is workspace-wide, not per-app. Vanilla Supabase
scopes its anon key to a single project, which is what makes the usual
"anon key in the browser + RLS" advice sound; on n0 one key spans every
app's `app_*` schema and every member's private `u_*` schema on the
instance. The upstream reasoning does not carry over, and the blast
radius is the whole workspace. This has already produced one incident,
where data in a private schema was published via a grant to `anon`.

Replace the section with the rule instead — the database is never
queried from the browser, always through a backend — and drop the anon
key from the Dockerfile and CI templates, leaving only genuinely public
build args. Also flag it at the two other places it was reachable: the
App Data env table, and the quick-reference that told you to fetch
workspace credentials and run app migrations through
`/workspaces/{id}/supabase/sql/` (admin-only, superuser, `public`
schema, and not replayed on a fresh deploy). App schemas come from
committed `migrations/*.sql`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant