All checks were successful
PR checks / checks (pull_request) Successful in 1m13s
Twenty pages completing the tree section 10 planned: Modules (8), Architecture
(5) and Reference (7). Four decisions, D38-D41, recorded in PLAN.md section 10.
D39 is the one that shaped the phase. Section 1 forbids re-specifying a
contract, and a Reference section is exactly where that rule is most tempting to
break, so the line is drawn at names: every environment variable, config key,
installer command, visibility rung and canonical document is listed with one
terse line saying what it is FOR, while shapes, semantics and every "why" stay
in the canonical document.
That is only safe because the names are checked. checkReference.mjs compares six
enumerations against the repositories that own them, over the Gitea API, as set
comparisons in BOTH directions -- and the second direction is the one that earns
its keep, because a reference page does not usually rot by describing something
that vanished, it rots by quietly not mentioning what was added since.
The check went green on its first run, which is the least trustworthy possible
outcome, so it was verified by breaking it: seven mutations, all caught. The one
worth keeping is the visibility ladder REORDERED with its membership unchanged
-- it is a security boundary, and a set comparison alone would have passed it.
D41 turns plannedSidebar from a checklist into a checked invariant, and finding
out why was the phase's first defect: it had already drifted, because phase 7
added the Content page under D37 and never updated the list. Nothing failed,
because nothing read it. checkSidebar.mjs now asserts the two trees agree on
groups, labels and order -- order because the order of Getting started IS the
installation path.
Two more things the writing found. PLAN.md's page count was wrong and had been
since section 10 was written ("roughly 38, 37 planned" for a tree of forty).
And module.json's `mounts` and the SPA's paths are different mechanisms that no
single document stated plainly -- module-uo declares admin: ["/shard",
"/uo-link"] while its screen lives at /admin/uo/link, because API routes are
deliberately NOT namespaced while SPA routes are. That is precisely the
distinction the installer got wrong in v0.1.0, and it now has a named home.
D40: the docs link to /architecture/'s drawn diagrams rather than importing
them. Those components carry marketing chrome and depend on diagram.css, which
Starlight does not load; the docs use text diagrams, which paste into an issue.
npm run verify green: 40 pages across 5 groups agree with plannedSidebar, 2390
internal links resolve, 123 repository links point at a branch, 19 facts, 59
quickstart checks, 22 reference enumerations, astro check 0 errors, 36 tests.
Co-Authored-By: Claude <noreply@anthropic.com>
86 lines
3.5 KiB
Plaintext
86 lines
3.5 KiB
Plaintext
---
|
|
title: HTTP API
|
|
description: How the site's API is organised, where the live specification is, and the gate each tier sits behind.
|
|
---
|
|
|
|
import { Aside } from '@astrojs/starlight/components';
|
|
|
|
The site's backend API is **OpenAPI 3.0**, and the specification is generated from the routes
|
|
themselves rather than maintained beside them.
|
|
|
|
<Aside type="note" title="Your own deployment serves the authoritative copy">
|
|
Every route, parameter and response shape is at **`/api/docs`** on your site, generated from
|
|
the code that is actually running — including any module you have installed.
|
|
|
|
That is the copy to trust. This page is a map of how it is organised; it does not restate
|
|
the routes, and a reference section that tried to would be wrong within a week.
|
|
</Aside>
|
|
|
|
## The tiers
|
|
|
|
Every route lives under `/api/v1/<tier>/`, and **the tier decides the gate**.
|
|
|
|
| Tier | Routes | Sits behind |
|
|
|---|---|---|
|
|
| `admin` | ~93 | `noindex`, `isLoggedIn`, `requireRole('admin','editor','moderator')` |
|
|
| `auth` | ~38 | Public by necessity; heavily rate-limited and bot-scored |
|
|
| `player` | ~24 | `noindex`, `requireAuth` — role-agnostic self-service |
|
|
| `public` | ~19 | Nothing, by design |
|
|
| `settings` | 2 | `requireAuth` + `noindex`, no role gate |
|
|
|
|
Plus two outside the versioned surface: **`/api/health`** and **`/api/csp-report`**.
|
|
|
|
Those two are deliberately not under `/api/v1`. A browser learns the CSP report path from the
|
|
policy header rather than from a client build, so it is not part of the versioned client
|
|
contract.
|
|
|
|
## Two things the tier table implies
|
|
|
|
**`player` is role-agnostic.** It is self-service for whoever is signed in, gated on
|
|
`requireAuth` alone and never on "is not staff". Staff are a *superset* of players — an
|
|
administrator has characters too, and a `player` route that excluded them would 403 an admin
|
|
off their own account.
|
|
|
|
**A module's routes inherit their tier's gate** and add their own on top. A module never
|
|
re-implements the tier gate; see [The module
|
|
API](/docs/modules/the-module-api/#registerroutes-and-the-tier-gate).
|
|
|
|
## Authentication
|
|
|
|
Three ways in, [one session model](/docs/architecture/authentication-architecture/):
|
|
|
|
- **Cookie** — `httpOnly` JWT, for the browser.
|
|
- **Bearer** — short access tokens plus rotated, hashed, revocable refresh tokens, for the
|
|
native app.
|
|
- **SSO** — OAuth2/OIDC with PKCE, and **link-only**: an external identity must already be
|
|
attached to an existing account.
|
|
|
|
Admin roles are **re-validated against the database on every request**, so a demoted user
|
|
loses access immediately rather than at token expiry.
|
|
|
|
## The sidecar's API is a different thing
|
|
|
|
The uo-link sidecar exposes its own small REST and WebSocket surface, reached **only** by the
|
|
website's backend. It carries `X-UOLink-Version` and answers `409` on a protocol mismatch.
|
|
|
|
It is not part of this API and is not served from your site. See [The
|
|
bridge](/docs/architecture/the-bridge/).
|
|
|
|
## Keeping the spec current
|
|
|
|
For contributors: the specification is generated from `#swagger.*` annotations next to each
|
|
route, and the output is committed.
|
|
|
|
```bash
|
|
cd website/server && npm run swagger
|
|
```
|
|
|
|
A route that is not in the specification is not finished. Modules publish their own
|
|
fragment, at a fixed filename in the bundle root, so a module's routes appear in the same
|
|
documentation as core's.
|
|
|
|
## Canonical document
|
|
|
|
[`BACKEND_DESIGN.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/BACKEND_DESIGN.md)
|
|
§4 is the API contract, including §4.0's authoritative route list.
|