docs(website): record the uo-link client's config-decrypt contract and the dashboard's mixed tier
Two corrections found by a live smoke test of all 200 routes at every access
level (website PR: fix/uolink-client-throw-and-sitemode-gate).
ARCHITECTURE.md: the "uoLinkClient never throws" invariant was true of the HTTP
call but not of resolving the config, which decrypts the stored auth token and
throws when the ciphertext can't be authenticated (SECRET_ENC_KEY rotated, or a
DB dump restored under a different key). Spell out that this is now handled
inside the client, reported as { ok: false, error: 'uo-link config unreadable' }
with a distinct ERROR log, and that GET /admin/uo-link/config keeps working —
it is the screen an admin needs to re-enter the token and recover.
BACKEND_DESIGN.md: GET /dashboard is staff-wide while PUT /site-mode on the
same screen is adminOnly — the one place a single screen spans two tiers. Note
that the client must gate that control itself rather than relying on the route
gate that admitted the user to the page.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -100,7 +100,13 @@ flowchart TB
|
|||||||
talks to it. Every backend→sidecar call carries `Authorization: Bearer <token>` and an
|
talks to it. Every backend→sidecar call carries `Authorization: Bearer <token>` and an
|
||||||
`X-UOLink-Version` header (a protocol mismatch fails fast with `409`). The REST client
|
`X-UOLink-Version` header (a protocol mismatch fails fast with `409`). The REST client
|
||||||
(`uoLinkClient.js`) never throws — every call returns `{ ok, data, status }` — so the site degrades
|
(`uoLinkClient.js`) never throws — every call returns `{ ok, data, status }` — so the site degrades
|
||||||
gracefully when the shard is down.
|
gracefully when the shard is down. That guarantee covers **reading the config too**: resolving the
|
||||||
|
admin-managed config decrypts the stored auth token, which throws when the ciphertext can't be
|
||||||
|
authenticated (`SECRET_ENC_KEY` rotated, or a DB dump restored under a different key). This is
|
||||||
|
handled inside the client and reported as `{ ok: false, status: 0, error: 'uo-link config
|
||||||
|
unreadable' }` plus a distinct `ERROR`-level log, so a wrong key degrades the shard surface to
|
||||||
|
"unavailable" instead of 500ing it — and `GET /admin/uo-link/config` keeps working, which is the
|
||||||
|
screen an admin needs to re-enter the token and recover.
|
||||||
- **Two ways in from the sidecar.** Live game events arrive over an outbound **WebSocket** and are
|
- **Two ways in from the sidecar.** Live game events arrive over an outbound **WebSocket** and are
|
||||||
routed by the `shardIngest.js` dispatcher (state-changing kinds update `shard_*` tables, notable
|
routed by the `shardIngest.js` dispatcher (state-changing kinds update `shard_*` tables, notable
|
||||||
kinds append to `shard_events`, high-frequency kinds only update state). Point-in-time reads and
|
kinds append to `shard_events`, high-frequency kinds only update state). Point-in-time reads and
|
||||||
|
|||||||
@@ -561,7 +561,13 @@ Public content GETs pass through the **siteMode** gate (§5).
|
|||||||
the whole gate. The ops/config capabilities — `uo-link`, `email`, `discord-bot`, `settings`, and
|
the whole gate. The ops/config capabilities — `uo-link`, `email`, `discord-bot`, `settings`, and
|
||||||
`PUT /site-mode` — are `adminOnly`; `shard` is the one mixed prefix, where self-service account
|
`PUT /site-mode` — are `adminOnly`; `shard` is the one mixed prefix, where self-service account
|
||||||
linking carries no extra gate and the in-game staff operations carry `modAccess`. There is no residual
|
linking carries no extra gate and the in-game staff operations carry `modAccess`. There is no residual
|
||||||
file: every admin route is declared in a capability router. The URLs below are unaffected by which
|
file: every admin route is declared in a capability router.
|
||||||
|
|
||||||
|
`GET /dashboard` and `PUT /site-mode` are the one place where a **single screen spans two tiers**: the
|
||||||
|
dashboard is staff-wide, but the site-mode toggle on it is `adminOnly`. The client must therefore gate
|
||||||
|
that control on its own (`Dashboard.jsx` renders it only for `role === 'admin'`) rather than relying on
|
||||||
|
the route gate that admitted them to the page — the same rule the sidebar follows, so a non-admin is
|
||||||
|
never shown a control that would 403. The URLs below are unaffected by which
|
||||||
file a route sits in — that is the property the route manifest freezes.
|
file a route sits in — that is the property the route manifest freezes.
|
||||||
| Method | Path | Purpose |
|
| Method | Path | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
Reference in New Issue
Block a user