The module half of the read path. Seven tables, an ingest cursor, four public routes, and one file whose only job is deciding who may see what. **The record and the window are different things.** `rust_player_wipe_stats` and `rust_gather_totals` are permanent and per-wipe, so all-time is those rows SUMmed rather than a second set of counters that can disagree with them — that is R12's "per-wipe detail plus all-time rollups" in one table instead of two. `rust_events` is a bounded 30-day window of raw frames for the killfeed, and `rust_presence` is a board: replaced wholesale, never appended. **The feed is a cursor, not a socket, and the header says why.** Core runs Node 20, where a global WebSocket is still behind a flag, so a socket means taking `ws` — against a release that asserts it has no runtime dependencies (D5). The deciding argument is the other one though: a socket needs a cursor anyway, for whatever it missed while the module was restarting, and the catch-up path is the one that has to be right. A cursor alone is one mechanism exercised every five seconds rather than two where the second only runs after an outage. **The cursor advances after the batch, never before.** A crash between the two re-reads events already counted, which inflates a total; the other order loses them silently and for ever. One is visible and bounded, the other is invisible and permanent, so the code fails in the visible direction. A server with no cursor starts at the sidecar's current END rather than at zero — replaying a fortnight of deaths into stats for wipes the site never saw is not a catch-up. **`catalogue.js` is a security boundary, default-deny.** Protocol 2 carries IP addresses (login attempts, approvals, bans), one player's report about another, and the grid reference of somebody's base. They are stored, because an operator chasing ban evasion needs them; they are not served below the admin tier. The allowlist lives here rather than as a field on the wire, because a boundary declared by the sender is one a compromised or merely out-of-date game host can widen — the same reason core's own shard fan-out filters on the serving side. A kind this build has never heard of is not public, and a test holds the list against PROTOCOL.md §8.4 so that adding a kind to the protocol without classifying it fails a build. `PROTOCOL_VERSION` goes to 2 here in the same change as the emitters, though this module consumes none of the new frames yet: the sidecar refuses a mismatched client with a 409, so a module left on 1 would stop being able to read the board it has been reading all along. A constant that lags the deployment is an outage with a version number on it. 95 server tests, 20 client tests, every guard green, and `routes.manifest.json` regenerated against a real core at the pinned ref: 10 routes, all documented, none of core's moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
31 lines
1.5 KiB
SQL
31 lines
1.5 KiB
SQL
-- ── The teardown ──────────────────────────────────────────────────────────
|
|
--
|
|
-- Destructive, and run ONLY by an explicit admin purge (MODULE_API.md §2.6).
|
|
-- Nothing on the boot path executes this file, and uninstalling the module does
|
|
-- not either: removing an operator's data is a second decision they make on
|
|
-- purpose, offered inside the uninstall flow and confirmed separately.
|
|
--
|
|
-- It exists because `schema.sql` does. A module that can create tables and
|
|
-- cannot drop them leaves an operator with orphaned data and no supported way to
|
|
-- remove it, so core refuses to load a module that declares one without the
|
|
-- other.
|
|
--
|
|
-- **Drop in the reverse of creation order**, which this file depends on:
|
|
-- `rust_server_state` carries a foreign key into `rust_servers`, so dropping the
|
|
-- parent first fails on the constraint — and a purge that fails halfway leaves
|
|
-- exactly the orphaned data it exists to remove.
|
|
--
|
|
-- What does NOT belong here: rows written into core's tables. Core prunes what
|
|
-- it knows this module registered, because it is the side that knows which
|
|
-- registrant owned what.
|
|
|
|
DROP TABLE IF EXISTS rust_ingest_cursor;
|
|
DROP TABLE IF EXISTS rust_presence;
|
|
DROP TABLE IF EXISTS rust_events;
|
|
DROP TABLE IF EXISTS rust_gather_totals;
|
|
DROP TABLE IF EXISTS rust_player_wipe_stats;
|
|
DROP TABLE IF EXISTS rust_players;
|
|
DROP TABLE IF EXISTS rust_wipes;
|
|
DROP TABLE IF EXISTS rust_server_state;
|
|
DROP TABLE IF EXISTS rust_servers;
|