1. Every action is a signed record
A move, a resignation, a draw offer, a takeback, an abort, a flag: each is a record. A record says who signed it, its number in that player's chain (seq), the signer's clock (at), and one byte saying what it is (op). For a move, that byte is the move: its index among the legal moves (see Move code).
The player signs it with an Ed25519 key that lives on the device and was certified by the server. On the wire, a move is about 70 bytes, 64 of them signature.
2. Two links tie every record down
Each record points at two things it cannot change afterwards:
- prev: the signer's own previous record (the genesis, for the first one). So every record vouches for the signer's entire history.
- seen: the newest record of the opponent the signer had applied. So every record also proves what the signer knew when it signed.
The signature covers a 77-byte header that holds the hash of everything else. Because the header commits to prev and seen, one signed header (a head) commits to the whole game up to that point: both players' records, in order. That is the trick the rest of the protocol leans on.
3. One number, one record
If two different signed records with the same seq ever meet (on the opponent's phone, at the server, in an uploaded report), that is a fork, and the signer loses the game. It is the only thing a player is ever punished for. Everything else that is wrong (a bad signature, an illegal move code, a draw acceptance with no offer) just makes that record void; the game goes on.
That one rule is why "send one move to the opponent and another to the server" cannot work. The two copies cannot both be valid, and each side holds a signed copy.
4. The server only witnesses
A server is optional. When there is one, players anchor their head with it: they send the newest signed header, and the server signs a receipt saying "these records existed at server time T". The server never stands in front of a move: when it carries one too, it is just one more link in the race, so it adds no delay. Depending on the game, anchors are:
- a timestamped backup (offline games),
- evidence of who was there and did not answer (hybrid games),
- the official clock (online games).
Read more in Anchors and receipts.
5. Anyone can replay the game
Both devices, the server and any later checker run the same function over the same records in the same canonical order, and get the same board, the same clocks and the same result. Nothing is negotiated. After the game, either player uploads it; the server merges both uploads (a report can only add records, never remove them) and saves the result.
The vocabulary
| Word | Means |
|---|---|
| record | one signed action: a move, a command, an ACK, a verdict |
| genesis | the three signed messages that start a game (CHALLENGE, ACCEPT, START); its hash names the game |
| header / head | the 77 signed bytes of a record; a head is that header plus its signature, about 98 bytes |
| ACK | "I applied your record at my time t": a clock sample, no effect on the board |
| heartbeat | "at my time t, my newest record was seq n", signed, sent with the pings; a record that contradicts it is a fork |
| fork | two different signed headers for one (signer, seq): the signer loses |
| anchor / receipt | a head sent to the server / the server's signed answer with its time |
| link | one path between the players: Bluetooth, a LAN, iroh or the server's session; every record races on all open links |
| arbiter | how much the server referees: none, checkpoints or full |
| rules profile | the game's policy numbers (allowances, budgets, what leaving costs), signed into the genesis |