feat: ingest protocol 2, and keep the record a wipe cannot erase #3
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/phase-3-protocol-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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_statsandrust_gather_totals, per player per wipe. All-time is those rows SUMmed, not a second set of counters that can disagree with them. That is R12's "per-wipe detail plus all-time rollups" in one table rather than two.rust_events, a 30-day window of raw frames for the killfeed. Losing last month's individual deaths costs a scroll-back; losing last month's totals costs a player their history.rust_presence, replaced wholesale from the board. Storing a board as history is the mistake the wire'stypefield exists to prevent, and it would be a poor return for the sidecar's trouble to make it here after it went out of its way not to make it there.The feed is a cursor, not a socket
Core runs Node 20, where a global
WebSocketis still behind a flag — so a socket means takingws, against a release that asserts it declares no runtime dependencies (D5).The deciding argument is the other one: 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 nobody planned. What it costs is seconds of latency on a killfeed.
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 invisible and permanent — so the code fails in the visible direction. A server with no cursor starts at the sidecar's current end: replaying a fortnight of deaths into stats for wipes the site never saw is not a catch-up, it is inventing a history it was not present for.
catalogue.jsis a security boundary, default-denyProtocol 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 — an operator chasing ban evasion needs them — and 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; core's own shard fan-out filters on the serving side for the same reason. A kind this build has never heard of is not public, and a test holds the list against
PROTOCOL.md§8.4 so adding a kind to the protocol without classifying it fails a build.The handlers make it structural too:
events.recenttakes the viewer explicitly, so leaking an IP address from the public route takes adding an argument rather than forgetting one.Also
PROTOCOL_VERSION→ 2 in the same change as the emitters, though this module consumes none of the new frames yet: the sidecar refuses a mismatched client with a409, 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.New public routes:
/servers/:id/events,/leaderboard,/wipes,/online.95 server tests, 20 client tests, every guard green, and
routes.manifest.jsonregenerated against a real core at the pinned ref — 10 routes, all documented, none of core's moved.Spec:
docs/rust-link/PROTOCOL.md§8 (RunicGateway/docs#255).🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4