Who we defend against
Runs a modified client. Can sign anything with its own key, delay, reorder or drop its messages, and lie about its own clock.
Can delay, drop, duplicate or replay messages. Cannot forge a signature, and cannot fake pings on a direct link: every frame there is sealed.
Could try to drop or reorder receipts, or fudge times. Its receipts form a hash chain, and it never carries the moves between players.
Out of scope: a player using an engine (that is fair-play detection, a different problem), and two accounts of one person farming rating against each other (that is collusion, the same online or off).
The one punishable act
Every cheating strategy that involves telling different parties different stories needs two signed records for one sequence number, because every record pins down its predecessor and what it had seen. So these all reduce to a fork:
- one move to the opponent, another to the server,
- two different moves to the opponent (to fork the board),
- a report that differs from what the opponent holds or the anchors saw,
- pre-anchoring several candidate moves.
A heartbeat is a signed statement of the same kind ("at my time t, my newest record was seq n"), so a record that contradicts it is a fork too. That is what stops a player who stayed silent past its flag from reporting a move dated in time.
Void, not punished
Everything else that is wrong only makes that one record void. It changes nothing and the game goes on after it:
- a bad signature (forged or corrupted),
- an illegal or out-of-range move code, or a wrong
legalcount, - a command at a point where it is not allowed (accepting a draw nobody offered, a move out of turn),
- a record that claims to have seen something the opponent never signed.
This keeps bugs and bad links from turning into losses. Only the act an honest client cannot commit is punished.
What a chain can prove
| Peer clocks (offline, hybrid) | Anchored clocks (online) | |
|---|---|---|
| every move, and that it was legal | yes | yes |
| mate, stalemate, threefold, fifty moves, insufficient material | yes | yes |
| resignation, draw agreement, abort, takeback | yes | yes |
| a player forking the chain | yes | yes |
| a record existed at time T | only if anchored | yes |
| a flag | yes, from both devices' timestamps | yes |
| how long a player really thought | within J per move; more is caught live | bounded by the lag quota |
| a link outage | paused up to a budget, then canceled | the disconnected clock runs |
| who went away | offline: nobody can tell. Hybrid: only when checkpoints prove it (the player was there, held the record and did not answer), else as offline | the server sees it |
Cryptographic choices
- Ed25519 (RFC 8032) for everything signed: records, genesis, certificates, receipts, verdicts, heartbeats, link hellos. 64-byte signatures, 128-bit security, deterministic nonces.
- ZIP-215 verification rules. Ed25519 libraries disagree on edge cases (non-canonical points, small-order components). Here that could split a game: a fork one device can prove and the other cannot. ZIP-215 fixes exactly which signatures are valid; every Pawnshake library uses it and passes its 196 test cases.
- Sealed direct links. X25519 for a fresh key per session, HKDF-SHA256 bound to the genesis and both hellos, ChaCha20-Poly1305 on every frame. Pings are unsigned, so this is what keeps a stranger on the radio path from faking them. See Links.
- Domain separation. Every signed message starts with its own magic tag, so a signature for one kind of message never verifies as another.
- Identity by header hash. Signature bytes are malleable for a dishonest signer, so records are identified by the hash of their header. A re-signed duplicate is not a fork.
- Keys stay put. Device keys are created on the device and never exported. Secure enclaves only sign P-256, and would not stop a modified client anyway, since that client holds its own key.
Every trick we know of, with a picture, is in the attack gallery.