docs(website): finalize the API plan — in-place router split, no /api/v2 #48

Merged
whitlocktech merged 1 commits from docs/api-plan-final into main 2026-07-27 19:41:13 +00:00
Member

What & why

Finalizes the API v2 plan after a review pass. Three of the four pieces it described are not being built; what survives is the work that is actually justified.

  • Auth merge removed, deferred with triggers. httpOnly + SameSite is the stronger model; auth/session.service.js already unifies cookie and bearer; the SSO / PKCE / pending-TOTP transaction cookies survive any merge (so "cookies are gone" was never true); and it dragged the admin-SSE fetch + ReadableStream rewrite along as a dependency for no user-visible payoff. Recorded as deferred behind trigger conditions, with the two specs a revival must write first (refresh-token reuse detection, rollback procedure).
  • No parallel /api/v2. The URL surface is already grouped by capability — /admin 110 routes across 18 prefixes, /auth 42, /public 24, /player 20 — so each capability router mounts at the prefix it already owns and every URL stays byte-identical. No dual mount, no per-route migration, no v1 retirement. The SPA (BASE = /api/v1), the Discord bot (SITE_PUBLIC_URL), and the Android app are all untouched. API_V2_SKELETON.md is marked superseded, body unedited, kept as the recipe if a versioned API is ever forced.
  • /api/mobile facade + app-version floor deferred. Nothing threatens the app when no URL moves and nothing is deleted; a facade would be ~70 permanently maintained delegate routes solving a problem that doesn't exist. Revival note recorded: start from the one-line alias mount apiRouter.use('/mobile', v1Router), hand-written delegates only where the mobile contract genuinely diverges. The M11 milestone is dropped from android/PLAN.md.
  • New PR 0 — a generated route manifest. "Every URL is unchanged" now has a mechanical proof instead of a reviewer's eyeball: a generator that introspects the live Express stack, a committed manifest, and a CI git diff --exit-code. The baseline website/api-route-inventory.json (199 API routes + 2 internal) is committed here, and PR 0's generator must reproduce it byte-for-byte.
  • CSP promoted to a standalone phase with the old plan's ordering contradiction fixed (report-only for one release, then enforce). Verified delta against server/src/app.js is just form-action 'self' (absent today) and frame-ancestors 'self' → 'none'.
  • Split sequenced as five grouped PRs (admin ×4, then public/player), each gated on a zero-line manifest diff.

How it was tested

Docs-only, so verification was against the code the plan describes:

  • Route inventory and the committed baseline generated by walking app._router.stack from a bare require('src/app.js') — confirmed it neither listens nor connects to the DB, and yields exactly the 110 /api/v1/admin routes that admin.routes.js defines. The manifest is scoped to /api/** + /.well-known/** + the internal app, because the SPA catch-all and /brand mounts are filesystem-conditional and would otherwise make it depend on whether CI built the client.
  • CSP delta re-read from server/src/app.js; admin.routes.js line/route counts confirmed; the app's hardcoded api/v1/… path count (69) re-counted from the android-app sources.
  • All relative links in the changed files resolve.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a Co-Authored-By trailer.

License

  • I agree that my contribution is licensed under this project's license (GNU GPL v3.0 or later), and I have the right to contribute it.
## What & why Finalizes the API v2 plan after a review pass. Three of the four pieces it described are not being built; what survives is the work that is actually justified. - **Auth merge removed, deferred with triggers.** httpOnly + SameSite is the stronger model; `auth/session.service.js` already unifies cookie and bearer; the SSO / PKCE / pending-TOTP transaction cookies survive any merge (so "cookies are gone" was never true); and it dragged the admin-SSE `fetch` + `ReadableStream` rewrite along as a dependency for no user-visible payoff. Recorded as deferred behind trigger conditions, with the two specs a revival must write first (refresh-token reuse detection, rollback procedure). - **No parallel `/api/v2`.** The URL surface is already grouped by capability — `/admin` 110 routes across 18 prefixes, `/auth` 42, `/public` 24, `/player` 20 — so each capability router mounts at the prefix it already owns and every URL stays byte-identical. No dual mount, no per-route migration, no v1 retirement. The SPA (`BASE = /api/v1`), the Discord bot (`SITE_PUBLIC_URL`), and the Android app are all untouched. `API_V2_SKELETON.md` is marked **superseded**, body unedited, kept as the recipe if a versioned API is ever forced. - **`/api/mobile` facade + app-version floor deferred.** Nothing threatens the app when no URL moves and nothing is deleted; a facade would be ~70 permanently maintained delegate routes solving a problem that doesn't exist. Revival note recorded: start from the one-line alias mount `apiRouter.use('/mobile', v1Router)`, hand-written delegates only where the mobile contract genuinely diverges. The M11 milestone is dropped from `android/PLAN.md`. - **New PR 0 — a generated route manifest.** "Every URL is unchanged" now has a mechanical proof instead of a reviewer's eyeball: a generator that introspects the live Express stack, a committed manifest, and a CI `git diff --exit-code`. The baseline `website/api-route-inventory.json` (**199 API routes + 2 internal**) is committed here, and PR 0's generator must reproduce it byte-for-byte. - **CSP** promoted to a standalone phase with the old plan's ordering contradiction fixed (report-only for one release, then enforce). Verified delta against `server/src/app.js` is just `form-action 'self'` (absent today) and `frame-ancestors 'self' → 'none'`. - Split sequenced as **five grouped PRs** (admin ×4, then public/player), each gated on a zero-line manifest diff. ## How it was tested Docs-only, so verification was against the code the plan describes: - Route inventory and the committed baseline generated by walking `app._router.stack` from a bare `require('src/app.js')` — confirmed it neither listens nor connects to the DB, and yields exactly the 110 `/api/v1/admin` routes that `admin.routes.js` defines. The manifest is scoped to `/api/**` + `/.well-known/**` + the internal app, because the SPA catch-all and `/brand` mounts are filesystem-conditional and would otherwise make it depend on whether CI built the client. - CSP delta re-read from `server/src/app.js`; `admin.routes.js` line/route counts confirmed; the app's hardcoded `api/v1/…` path count (69) re-counted from the `android-app` sources. - All relative links in the changed files resolve. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. <!-- docs-only; no build --> - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-07-27 19:32:14 +00:00
The API v2 plan is revised down to the work that is actually justified: a CSP
hardening pass and an in-place domain split of the monolithic route wiring.

- Auth merge (httpOnly cookies -> bearer + rotating refresh for every client) is
  removed and re-filed as deferred behind trigger conditions. httpOnly+SameSite
  is the stronger model, session.service.js already unifies cookie and bearer,
  the SSO/PKCE transaction cookies survive any merge, and it dragged the admin
  SSE fetch/ReadableStream rewrite along as a dependency for no user-visible
  payoff. A revival must first spec refresh-token reuse detection and a rollback
  procedure.
- No parallel /api/v2. The URL surface is already grouped by capability, so each
  new router file mounts at the prefix it already owns and every URL stays
  byte-identical. No dual mount, no per-route migration, no v1 retirement; the
  SPA, Discord bot, and Android app are all untouched. API_V2_SKELETON.md is
  marked superseded (kept as the recipe if a versioned API is ever forced).
- The /api/mobile facade and app-version floor are deferred with the revival note
  that it starts as a one-line alias mount, not ~70 hand-written delegates. The
  M11 milestone is dropped from android/PLAN.md.
- Adds PR 0: a generated route manifest, so "every URL is unchanged" is proved by
  a zero-line diff rather than asserted in review. The baseline
  api-route-inventory.json (199 API routes + 2 internal) is committed here and is
  what PR 0's generator must reproduce byte-for-byte.
- Split sequenced as five grouped PRs; CSP fixed to report-only first, then
  enforce (the old plan contradicted itself), with the verified delta being just
  form-action 'self' and frame-ancestors 'none'.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech approved these changes 2026-07-27 19:41:06 +00:00
whitlocktech merged commit 40cd9375d7 into main 2026-07-27 19:41:13 +00:00
whitlocktech deleted branch docs/api-plan-final 2026-07-27 19:41:13 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/docs#48
No description provided.