derive app origins from the environment

Each app origin was declared three times — destructured from process.env, listed in
APP_ORIGINS, then given a rule — so adding an app meant editing the same file in three
places and remembering all three. Two apps in and the list was already wrong: OffTail had
been added to .env as OFF_SCALE_ORIGIN, which nothing read, so it did nothing.

Any `OFFICER_<APP>_ORIGIN` in the environment is now an app origin, and its rule comes
from its own name: an app may reach /api/auth (it has to sign in) plus the one feature it
is named for, so OFFICER_VAULT_ORIGIN gets /api/vault without being told. Adding an app is
adding an env var.

Two things stay hand-written because they are not that convention:

- OWNER_ONLY_APPS ('APP') — the main app is the whole platform rather than one feature of
  it, so it keeps full access but owner-only. A different kind of rule, not a path scope.
- APP_SCOPE_OVERRIDES ('TAIL' → /api/vpn) — the one app whose name and API surface differ.

NON_OWNER_PATHS also stays, and is worth not mistaking for the music app's rule even
though the value matches: it is the ACCOUNT backstop, applied whatever Origin a caller
claims or omits, and it is the airtight half of that pair.

Also renames MUSIC_APP_ORIGIN to OFFICER_MUSIC_ORIGIN (the other three already had the
shape) and drops the dead OFF_SCALE_ORIGIN. Env and code changed together; .env on this
host is updated, so this needs a restart, not a migration.

Verified the derived rules against the live .env: APP → superAdminOnly, MUSIC →
auth+music, VAULT → auth+vault, TAIL → auth+vpn — identical to what was hardcoded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 08:55:17 +00:00
co-authored by Claude Opus 5
parent 44a9124ef5
commit 4598e53c89
2 changed files with 42 additions and 28 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ honoServer.use(
);
// Scoped-origin gate: restrict app origins (e.g. the music app) to their allowed path prefixes
// (/api/auth + /api/music). No-ops for the main web origin and while MUSIC_APP_ORIGIN is unset.
// (/api/auth + /api/music). No-ops for the main web origin and while OFFICER_MUSIC_ORIGIN is unset.
honoServer.use(originScopeMiddleware);
honoServer.get('/api', (ctx) => ctx.json({ officerAPI: 'ok' }));