Admin console
Owner-only tooling for schema verification, SQL patching, backup repair and storage cleanup. Admin mode is off by default and every route is disabled until you explicitly enable it.
Admin mode is a privileged surfaceIt grants row-level-security bypass through the service-role key, and /__admin/apply-sql can execute arbitrary SQL against your database. Enable it on a machine you control, and leave ENABLE_ADMIN_MODE=false otherwise.
Enabling admin mode #
Two conditions must both hold. This is deliberate — a stray ENABLE_ADMIN_MODE=true on an install with no service key must not half-open the console.
# .env ENABLE_ADMIN_MODE=true SUPABASE_SERVICE_ROLE_KEY=<your-service-role-key> # required ADMIN_SECRET=<long-random-string> # how you unlock ADMIN_EMAIL=you@example.com # optional: unlock via Supabase login
Internally this resolves to a single flag:
const ADMIN_MODE_READY = ENABLE_ADMIN_MODE && !!SUPA_SERVICE_KEY && !!ADMIN_COOKIE_SECRET;
Without ADMIN_SECRET, unlock does not persistWhen ADMIN_SECRET is unset the cookie signing key becomes crypto.randomBytes(32) — fresh on every boot. The console still works, but every restart invalidates your session. Set it if you use admin mode regularly.
Unlocking #
Visit /__admin/login. Two paths are accepted:
| Method | How it works |
|---|---|
ADMIN_SECRET | Type the secret. On success you receive a signed iso_admin cookie at Path=/. |
| Supabase session | Click Use Supabase Login. The existing browser access token is verified server-side, then checked against ADMIN_EMAIL/ADMIN_EMAILS or an admin row in user_roles. |
The cookie is scoped to Path=/ rather than /__admin so that non-prefixed protected routes — notably /api/update-now — can also authorise against it.
Schema verification #
/__admin/verify
admin
An in-server test suite, roughly 800 lines, run on demand. It checks the live project rather than asserting against the migration files, so it catches drift the SQL cannot see.
| Category | What it asserts |
|---|---|
| Table schema | Every expected table and column exists. |
| RPC functional | Each community function is callable and returns the expected shape. |
| RLS recursion safety | No membership policy recurses infinitely. |
| Edge-function interceptors | Leaderboard, analytics and finish-session return the intended 502 rather than fabricated data. |
| Server endpoints | Local routes respond with the expected status. |
| Auth pipeline | Signup, login and bootstrap smoke test. |
| Storage buckets | All four buckets exist with the correct visibility. Checks group-icons and study-material too — both were missing while the app uploaded to them, and the old check verified notes, which nothing used. |
| Local integration drift | Served bundles match what the patcher expects. |
Failures link to the fixWhen a check fails the page surfaces a bar linking to /__admin/patch, because most schema failures are resolved by applying the cumulative patch.
SQL patch manager #
/__admin/patch
admin
/__admin/apply-sql
admin
Serves community-patch-v6.sql with two ways to apply it: a one-click button that proxies through the Management API, or copy-paste into the Supabase SQL editor.
The proxy exists because api.supabase.com does not permit cross-origin browser requests. Your PAT is sent to your own local server, which forwards it — it is never stored.
/__admin/schema
admin
Downloads isotope-complete.sql, the authoritative fresh-install schema.
Backup diagnostics #
/__admin/sync
admin
/__admin/sync/repair-user-backup
admin
Shows, per user, which stored backup would win the comparison ladder and why. Repair supports a dry run, so you can see the outcome before writing anything.
Dry run firstdry_run: true reports exactly what would change without touching storage. There is no undo for a backup overwrite, so use it.
Storage console #
/__admin/storage
admin
/__admin/storage/cleanup-preview
admin
/__admin/storage/cleanup-apply
admin
Lists stored objects per user and identifies prunable older revisions. Preview and apply are separate endpoints — apply additionally requires confirm: true.
Role management #
/__admin/roles
admin
/__admin/roles
admin
/__admin/roles
admin
Grants and revokes admin roles in user_roles. A user holding an admin role can unlock the console with their Supabase login, without knowing ADMIN_SECRET.
Browser proof #
/__admin/browser-proof
admin
/__admin/browser-proof-result
run token
Some behaviour cannot be asserted server-side: whether the service worker actually registered, whether IndexedDB persisted, whether a real login round-trips. Browser proof opens a page that performs those checks in a real browser and reports back.
browser-proof-result is the one admin route exempt from cookie auth — it is authorised by a per-run token instead, because the reporting page may run in a context without the admin cookie.
Update endpoint #
/api/update-status
none to call
/api/update-now
admin cookie or loopback
Note this pair is not under /__admin/ and does not require admin mode. It accepts an admin cookie or a loopback connection, so the in-app Update button works on an ordinary install. Full detail in API reference.
If you expose the server #
The server binds 0.0.0.0 so a phone on your network can reach it. That also means anything on the network can. Before doing that:
- Leave
ENABLE_ADMIN_MODE=falseunless actively using the console. - Set a long random
ADMIN_SECRETif you do enable it. - Know that
POST /__errorsis unauthenticated and appends to a log file, capped at 1 MB per request. - Prefer an SSH tunnel over exposing the port directly.