docs(modules): settle Phase 5 — the Integration Kit, and the sidecar rule

Phase 4 closed; this is the plan for Phase 5, the Integration Kit, settled with
the org lead on six decisions.

MODULE_SYSTEM.md
- §2.11.1 (new): Phase 5's shape — measured starting state, the six decisions,
  the four slices, the acceptance mechanism.
- §2.11: layer 2 rewritten. The sidecar is the default and only path to a game,
  and it is a non-blocking dumb forwarder that persists before it forwards. A
  game that already ships a remote-control surface gets a thin sidecar, not none.
- §2.10: a MODULE_API_VERSION bump now carries a pass over the kit.
- Part 3: decisions 28–33.
- The Phase 5 line in Part 2's phase list: STARTED 2026-08-12.

MODULE_API.md
- §2.7 gains one prohibition: a module does not open a connection to a game
  server from the website process — a game socket, RCON, a query port, an
  engine admin API — with the three reasons it exists (the website is the
  internet-facing process, the sidecar owns the durable copy, neither side can
  stall the other). Normative prose with no CI behind it, stated so that every
  second module does not re-decide it.
- §1.1: the 1.4.0 entry. No member changed; minor deliberately.

modules/rust-dryrun.md
- §2 "Talking to the game" rewritten from "No sidecar" to a thin RCON sidecar,
  with a dated Correction note recording what the exercise originally concluded
  and why it was overruled. Corrected in view rather than silently rewritten.

Pairs with website#… (the 1.4.0 bump, into edge); either order.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-12 09:38:07 -05:00
parent 91ac28e11d
commit 89aff5c26b
3 changed files with 169 additions and 22 deletions

View File

@@ -111,19 +111,33 @@ model gets wrong, and it is worth writing down for whoever builds this.
### Talking to the game
**No sidecar.** Rust ships RCON over WebSocket, so the module dials the server directly with the
token an admin saved, encrypted at rest through `ctx.secretBox`.
**A thin sidecar.** Rust ships RCON over WebSocket, so `rust-link` is small: it holds the RCON
connection to each server with the token an admin saved, and presents the website the same shape
`uo-link` does — a bearer-authed HTTP + WebSocket API in front of a SQLite store. The module talks
only to it, never to a game server.
This is the sharpest test of whether the module system's boundary is drawn in the right place, and it
passes: core has no opinion about how a module reaches its game. What core owns is that the module
never blocks a request on it, that its secrets are encrypted, and that a game being down degrades to
a page saying so. The *shard-dials-out* invariant that shapes
[`../link/PLAN.md`](../link/PLAN.md) is a property of ServUO — a game engine with no remote-control
surface, whose plugin must not stall on a socket — not of the platform. A game that ships RCON
already answers the question the sidecar exists to answer.
The store is the reason it exists even though the game is already remote-controllable. RCON is a
live channel with no memory: what it tells you while nobody is listening is gone. So the sidecar
appends every kill, wipe and chat line, keeps the latest snapshot of each server's state, and
answers the website's reads from disk — a website that is down, restarting or mid-deploy loses
nothing, and a leaderboard renders the last thing the server said rather than an error. It also
keeps the RCON token, the reconnect loop and the per-server fan-out out of an Express process, where
a stalled socket is a stalled request handler.
The Integration Kit ([`MODULE_SYSTEM.md`](../website/MODULE_SYSTEM.md) §2.11) should say this
plainly, or its second reader will build a sidecar they did not need.
**`wipe_id` makes the durable copy load-bearing rather than a nicety.** A wipe is the moment the
game forgets; the sidecar is the only thing that remembers the shape of the map that just ended.
> **Correction, 2026-08-12.** This section originally concluded **"No sidecar"** — the module dialling
> RCON directly — and offered it as evidence that core has no opinion about how a module reaches its
> game. That was overruled by the org lead when Phase 5 (§2.11.1 d3/d4) settled the kit's stance, and
> the prohibition is now contract: [`../website/MODULE_API.md`](../website/MODULE_API.md) §2.7, as of
> `MODULE_API_VERSION` 1.4.0, a module does not open a connection to a game server from the website
> process. The original reasoning was not wrong about *ServUO* — the shard-dials-out invariant in
> [`../link/PLAN.md`](../link/PLAN.md) really is a property of an engine with no remote-control
> surface — but it mistook that for the whole reason a sidecar exists. The other reason is durability:
> the website is not the right place to hold a game connection, because it is the process most likely
> to be restarted and the one facing the internet. The finding is left in view rather than edited out;
> what a dry run concluded is worth more than a tidy document.
## 3. The client half