Capsule: deployment & hardening
Use when changing server bind/config, the build pipeline, or the input-hardening surface.
Read first
- docs/design/hardening.md — full hardening section
- docs/design/client-stress-tests.md — bounded report ingestion
and the
RTS_RECORD_STRESS_TESTSpersistence gate
Run / build
cd server && cargo run # serves client + /ws on RTS_ADDR; open the printed URL
cd server && cargo run --release # fast build
cd server && cargo build && cargo clippy
node scripts/check-wiki.mjs # wiki routes, generated stats, and catalog parity
node scripts/check-crate-boundaries.mjs
No JS build step (plain ES modules + PixiJS from CDN). The client is served from ../client
relative to the server crate, so cargo run from server/ is the whole dev loop. Release Docker
builds do have a prediction-WASM generation step: they run scripts/build-sim-wasm.sh inside the
builder image and fail if client/vendor/sim-wasm/rts_sim_wasm.js or
rts_sim_wasm_bg.wasm is missing.
Invariants
- Clients are untrusted. Validate and bound every wire input:
- ordinary command unit lists deduped + capped (
MAX_UNITS_PER_COMMAND); lab command-limit bypass lists use the larger boundedLAB_MAX_UNITS_PER_COMMAND - WebSocket frames size-limited
- placement coords range/overflow-checked
- ordinary command unit lists deduped + capped (
- No panics on tick or network paths. Stale ids = no-op. Use
checked_*arithmetic on anything derived from client input. Debug builds have overflow checks on (a badBuildcoord can panic incargo runbut silently wrap in--release). Keep placement mathchecked_*. - Keep the room task alive: handle errors, don’t propagate panics out of message handlers.
rts-serveris the only crate that may own Axum/Tokio WebSocket/static-file serving.- Missing static asset URLs must not fall back to the SPA shell. App routes may fall back to
index.html, but/vendor,/src,/assets, and root asset files should return 404 when the requested file is absent. - Deploy drain is bounded. SIGTERM/Ctrl-C starts the 295s app drain inside Fly’s 300s stop
window: natural match drain, forced aborted finalization for eligible live matches, tracked
match-history/replay write wait, then WebSocket/Axum slack. Beta/mainline validation should check
logs for forced-finalization success/failure,
match recorded outcome=aborted replay=true, and any write-wait timeout before treating an interrupted deploy as healthy. /wikiis server-rendered and read-only. It may serve only allowlisted Markdown fromdocs/contextanddocs/design;/wiki/statsmust be generated fromrts-rulesdefinitions and faction catalogs, not scraped from client config or rendered docs.
Cross-capsule triggers
- Touching the wire surface → protocol.md.
- Touching sim correctness → server-sim.md.