Skip to content
kibitz

end-to-end encrypted · six games · chat

Pull up a chair, play a friend.

kibitz is chess, backgammon, checkers, reversi, Connect Four, and provably-fair Battleship — with a chat alongside — over one end-to-end-encrypted session. You get a code phrase like lion-42-maple, a share link, and a QR; the first friend to join sits down as your opponent, everyone after that kibitzes. The relay that carries it all can never read a move or a message.

kibitz

what's on the table

Six games, one encrypted session.

Games start on demand from a picker and run side by side — play a chess game and a backgammon game at once, chatting the whole time. Every rules engine is pure Go, compiled to WebAssembly, and runs identically on both screens.

A chess game in the kibitz app

Chess

The full ruleset — castling, en passant, promotion, draws and resignation — via the maintained corentings/chess engine. Kibitzers watch and heckle in chat.

A backgammon game in the kibitz app

Backgammon

With a commit-reveal dice roll: each roll is committed then revealed, so neither side — nor a spectator — can doctor the dice. Forced moves and bear-off, all validated both sides.

A checkers game in the kibitz app

Checkers

American draughts with forced captures — the engine walks every capture chain, so a jump you're owed is a jump you must take.

A reversi game in the kibitz app

Reversi

Othello, flips and all. Legal moves and passes are computed by the shared engine, never asserted — so both boards always agree on the count.

A Connect Four game in the kibitz app

Connect Four

Drop, connect, gloat. A quick one between the longer games — with the disc-drop animation and a hover ghost showing where your piece will land.

A Battleship game in the kibitz app, showing hits and misses

Battleship

Provably fair: each cell of your fleet is a cryptographic commitment made before the first shot. "Sunk" becomes a public derivation — there's no message left to lie in. No hidden server; nobody cheats.

how it's private

The relay is blind.

Your code phrase never reaches the server — it seeds a PAKE handshake between you and each friend. From that, everyone shares a group key; every move, dice roll, and chat line is sealed with XChaCha20-Poly1305 before it leaves the tab. The relay only forwards opaque frames between participants: it sees connection counts and a session id that's a one-way hash of the phrase, and nothing else. Self-host the one-binary relay or use the hosted one — either way it can't read a thing.

// the phrase is the secret — it never
// travels; the session id is a hash
sessionID = SHA-256("kibitz/v1" ∥ phrase)[:16]

// PAKE → pairwise key → group key
key  = HKDF(pake.SharedKey())
seal = XChaCha20Poly1305(key, frame,
         AD: sessionID ∥ version ∥ sender)
# what the relay can see
session   a1b2c3…  (hash, not the phrase)
members   2 players + 3 kibitzers
frames    ▓▓▓ opaque ciphertext ▓▓▓

# what it can never see
moves · dice · chat · names   ✗ sealed

Games sync both-sides-validate — each client runs the same deterministic engine and checks a state hash, so there's no authoritative server to trust or to breach. Read the full threat model ↗.