web.rs adds the website-facing HTTP surface (axum): GET /health and GET /ws.
Every shard event is broadcast to all connected WebSocket clients as a JSON text
frame. main.rs's event loop now broadcast::sends each event after logging it.
A lagging client is warned and kept live (misses events) rather than stalling
the others; a dead-but-not-closed socket is caught by a 30s server ping. The feed
is live-only -- no replay -- since history belongs to REST + SQLite. This side may
be exposed beyond loopback (it is the gatekeeper); it defaults to 127.0.0.1:8080
and wants auth before going public.
Verified end to end: a WebSocket client connected to /ws, received ws.hello, then
live pong events relayed from the shard through the shard-link -> broadcast -> ws
path -- the same path a login, sale, or IDOC alert will take to a browser.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First cut of the Rust sidecar, in sidecar/. It is the TCP listener the shard
dials out to; that asymmetry is what keeps the game unreachable from the website.
shard.rs: serve() binds 127.0.0.1:7788 and accepts shard connections in a loop,
re-accepting on disconnect. Each connection splits read/write: the reader parses
newline-JSON into ShardEvent { kind, value } and forwards over an mpsc; the writer
drains a command mpsc. ShardHandle::send posts to whichever shard is connected and
drops with a warning if none is -- a website query during an outage should fail
fast, not queue; live events that must survive an outage are buffered by the shard.
main.rs wires it up, logs events by kind, and runs a 15s heartbeat ping to
exercise the command path. Later phases fan events out to a WebSocket broadcaster
and SQLite, and turn REST calls into shard commands.
Verified against the live shard: the sidecar received the shard's server.hello
(parsed, fields intact), round-tripped its heartbeat ping -> pong, and after a
sidecar restart the shard reconnected on its own and re-sent hello. Tokio + serde;
axum/sqlx/tungstenite come with the WS and REST phases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>