CoronaPoker verifies the shuffle collectively so a host cannot peek
In many ways, the poker game we always deserved.
At a glance
- What is it?
- A desktop Texas Hold'em game for private home tables where the deck is shuffled under a commutative Mental Poker protocol that every human in the hand re-checks, so an untrusted host cannot read hole cards. Around that sit signed betting actions, a hash chain per hand and SQLite crash recovery.
- Who is it for?
- CoronaPoker fits private home games among people who do not fully trust the machine hosting the table, which is the one situation its cryptographic design actually addresses. It does not fit anyone wanting accounts, a central server or tournament structures, since hosting is peer to peer and there is no third party hand log.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- tonikelope/coronapoker22 ★ · +0
| Repository | 2026-08-21 | 2026-11-14 |
|---|---|---|
| tonikelope/coronapoker | 22 | 22 |
0 Stars in the last 85 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The deck is shuffled by the ring, not by the host
Every card in a hand is shuffled and locked collectively by every human member of that hand's cryptographic ring, through a commutative Mental Poker protocol using the SRA construction, with a zero knowledge verifiable shuffle that each ring member re-checks independently. The stated goal is that a malicious host cannot silently peek at, duplicate or relocate a card in a completed deal.
Pocket cards stay sealed end to end until showdown, while community cards unlock per street. The claim in the README is specific about what this buys: no single participant, not even the host, can learn another player's hole cards or the board before its legitimate reveal. That is a stronger statement than encrypting traffic, because it holds even when the machine running the table is the adversary.
The implementation is built from scratch rather than borrowed, over Ristretto255, with DLEQ chained dealing and a Bayer-Groth shuffle.
Identity keys and non-blocking key changes
Each human carries a persistent Ed25519 keypair stored locally under restricted ACLs, POSIX 0600 on Unix and owner-only on Windows. Betting actions, community and showdown reveals, straddle decisions, Rabbit requests, seat draw commitments and closing receipts each use distinct signed domains, so a signature captured for one kind of message does not validate as another.
Trust on first use is the model: the first key seen for a nick is recorded, and a later different key is accepted as `CHANGED`. That transition replaces the stored key, increments the session count and clears the out-of-band verification flag without blocking admission. The project says key changes are logged, and GDX also displays an in-window warning when the previous identity was verified out of band; an unverified key change has no popup. Comparing the identity identicon or fingerprint out of band is left to the players, and only matters when key continuity matters.
A hash chain over each hand makes the action history non-deniable
A hash chain ratchets over the course of a hand, `H_{t+1} = SHA-256(record || sig)`, which commits every peer to the exact action history. The chain's closing state folds in the hand's settlement, recording who put in how much and who was paid how much.
Before a durable close, every peer enforces exact-cent conservation with the identity `paid + closing carry = contributed + opening carry`, then exchanges signed receipts with the human ring members currently expected. Departures are handled by exclusion rather than negotiation: someone who has left cannot sign a future close, so they are simply not part of the expected set.
The failure behaviour is what makes the check meaningful. A conservation error, a divergent or missing expected receipt, or an invalid-signature flag stops the SQLite close and stops hand advancement, and the open state is preserved for recovery rather than being discarded.
Invalid actions become synthetic folds that block the close
The response to a hostile or broken action is a deterministic decision rather than a disconnect. Structural SRA failures, forged community reveals and unsafe early cascade requests trigger immediate lockdown or MISDEAL.
An invalid signed betting `ACTION` is handled differently, and deliberately quietly: it is neutralised as a deterministic synthetic fold, recorded in the hand flags, and the result is that receipt consensus rejects the durable close. The player does not receive an error dialog, but the hand cannot be settled either, which means a forged action leaves a trace that blocks progress.
Every one of these responses is logged with its precise reason, which is what makes the sequence auditable after the fact rather than only observable in the moment. The distinction between the two paths is worth noting: a structural cryptography failure escalates immediately, while an invalid action only blocks settlement.
Traffic is encrypted, and the recovery reader whitelists what it deserializes
Application traffic between a client and the host, after the handshake, is encrypted with AES-256-CBC plus HMAC-SHA256 over keys negotiated through ECDH key exchange. A network observer sees opaque authenticated frames rather than game state, chat or betting actions.
The layer that interests security reviewers more is the recovery payload reader. It installs a strict `ObjectInputFilter` whitelist permitting HashMap, String and numeric boxes only, with a 10 MB cap and a 20 level depth limit. That is the specific defense against a malicious host supplying arbitrary Java deserialization types through a recovery payload, which is the one place where an attacker who controls the host could otherwise choose the classes your process instantiates.
A table password is available on top of channel encryption, which controls who joins rather than what an observer on the network can read.
Crash recovery checkpoints the whole hand to SQLite, including the crypto fossil
Every hand is checkpointed to a local SQLite database, holding the per-action history, balances, dealer and blind levels, and a crypto fossil with the full cascaded deck and the keys needed to re-derive your hole cards. A game resumes from the exact stop point after a crash, power loss or reboot, for host and clients alike.
The commit boundaries are as much of the point as the checkpoint. Hand creation commits the hand row and its complete unique balance roster atomically, and publishes the generated id only after that commit succeeds. Normal close and MISDEAL also commit metadata and the exact final or refunded roster atomically, with any failed row rolling the operation back and preventing advancement.
An interrupted hand keeps its durable hand number. If showdown had already completed, recovery advances to the next hand number on both the table and the game log, so the numbering stays consistent across a crash rather than repeating or skipping.
Hosting a table means opening a port on your own router
There is no central game server, no account system and no third-party hand log. Clients connect to the table host, which relays traffic between the machines in the game. The listening port defaults to 7234 and is configurable per game.
When you host, the game attempts automatic UPnP port mapping on your router and cleans the mapping up when the table closes. Manual port forwarding is the documented fallback, which means a host behind a router that blocks UPnP is responsible for the mapping themselves.
Reconnection is two staged. A dropped player holds their seat for a 45 second base grace window, and once the peer's reauthenticated reconnect intent reaches the host that window extends to 80 seconds, giving a flaky link a second chance before the table asks whether to remove the player. A player invited during recovery watches the in-progress hand as a passive spectator, with no cards dealt and no actions requested, and joins normally on the next hand.
Table features cover the small details that break long home games
A recent-server list persists the history of past tables and is browsed with the up and down arrow keys in the Join dialog, so reconnecting to a table you have played before does not require remembering an address. Per-peer link telemetry tracks round-trip latency and reconnection count per seat and broadcasts it, which is how a bad connection surfaces before it becomes someone's fault for stalling a hand.
Chat carries a client-side throttle of 0.5 seconds minimum between messages, applied to the sender's input rather than enforced at the host. It is a rate limit on input, not a moderation system.
On the table itself the implementation is rules-correct No-Limit Hold'em aimed at private home games rather than tournament structures, with 2 to 10 players in any mix of humans and bots, and all-in situations and side pots supported with correct handling. The project ships as downloadable releases, with recent tags at v25.15, v25.16 and v25.17, and the source is Java under GPL-3.0.
Editorial conclusion
CoronaPoker fits private home games among people who do not fully trust the machine hosting the table, which is the one situation its cryptographic design actually addresses. It does not fit anyone wanting accounts, a central server or tournament structures, since hosting is peer to peer and there is no third party hand log. Before trusting a session, confirm your peers are the humans you expect: a nick whose key changes later is accepted without a prompt, so compare fingerprints out of band when identity continuity matters. The cryptographic spec lives in docs/SECURITY.md rather than the README, and releases ship as downloadable builds rather than a library you depend on.
Frequently asked questions
Can a CoronaPoker host see my hole cards?
No participant, including the host, can learn another player's hole cards or the board before its legitimate reveal. Cards are shuffled and locked collectively by every human in the hand's cryptographic ring through a commutative Mental Poker protocol, and each ring member independently re-checks the verifiable shuffle.
What does it mean to fold in CoronaPoker?
Fold is an ordinary Hold'em action handled by the signed action path: betting actions use distinct signed domains so a fold cannot be replayed as some other message. An invalid signed action is neutralised as a deterministic synthetic fold, which is recorded in the hand flags and makes receipt consensus reject the durable close.
Where is CoronaPoker's full cryptography specification?
In docs/SECURITY.md, which holds the cascade flow, key schedule, HandStateChain seed, receipt format and recovery fossil layout. The README keeps only the summary of the zero trust deck protocol, Ed25519 identities, the per-hand hash chain and the AES-256-CBC plus HMAC-SHA256 channels.
Does CoronaPoker run on a central server with accounts?
No. It is peer-hosted with no external central game server, no accounts and no third-party hand log. One player hosts the table and relays traffic between the machines in the game, and hosting opens a listening port, 7234 by default, through UPnP or manual forwarding.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tonikelope-coronapoker)Star history chart for your README
[](https://hysenlabs.com/projects/tonikelope-coronapoker)Was this useful?