Anti-cheat
Defense in depth. Each layer is independent; a game's validator + anti_cheat config picks how
strict to be (none | basic | strict) — so it stays config-driven.
- Server-authoritative outcomes — the client never decides or sends the prize. For
probabilityandcollect_itemsthe server picks the result; tampering the response is meaningless because the server already wrote it. - Single-use sessions —
Startmints a session bound toplayer_id+game_id, stored in Redis with a TTL and an opaque secret.Playrejects unknown / expired / already-consumed sessions and marks the session consumed (one play per session). - Signed payload proof (
strict) —Startreturns a per-session HMAC secret; the client signs its payload (and, for action games, a compact replay[{t, x}]).Playverifies the HMAC and that the replay is internally consistent (event count == score, timestamps monotonic, spacing ≥ threshold). Blocks hand-crafted requests from DevTools. - Validator registry (per game):
basic— session + rules.time_and_score_range— min/max play duration, theoretical score ceiling.drop_plan— every claimeddrop_idmust exist in the server-generated sequence, respectmax_catchable, and pass timing sanity. Unknown / duplicate / over-catch →CHEAT_DETECTED.
- Server-generated seeds — for
collect_items, the server generates the drop sequence inStartand stores it;Playvalidates caught ids against it. The client can't invent drops. - Turn + rate enforcement — eligibility (max plays per user/day, campaign window) checked
inside the Play transaction; Redis token-bucket rate limits per player/IP; idempotency keys
stop a winning
Playfrom being replayed. - Leaderboard anti-cheat — score ceiling, statistical outlier (z-score), play-velocity, and
multi-account detection move suspicious entries to a
flaggedstate.finalizeonly awards un-flagged ranks; admins candisqualify/adjust. - Audit & observability — every play/claim/redeem writes immutable history with the inputs,
chosen outcome, validator verdict, and
trace_id.
Adding a check
A new anti-cheat rule is a new Validator registered by name — the engine core is never touched.