← Back to blog2026-07-06

What does Advanced encrypted dealing hide from the server? Mental Poker from source

The Mental Poker implementation targets server-blind hole cards, but ADR-090 safety-cages the customer path today: H5 cannot enable it and server create/attach fail closed. This post explains both the design and the current release boundary.

Separate it from normal dealing first

BluffKing has two fairness mechanisms that are easy to blur together: the default verifiable dealing, and the now-available Advanced encrypted dealing (ADR-101, trusted-single-operator scope).

Update 2026-07-25 (ADR-101): Advanced encrypted dealing is now available: this hand's unshown hole cards are not held in plaintext by the server. It ships under an explicit trusted-single-operator model — real, cryptographically-sound card-secrecy, but not trustless-against-the-operator settlement. Required disclosure: if any player drops, the whole table visibly downgrades to standard (server-visible) dealing; this secrecy holds only for that hand in this mode; practice chips only. Default tables are still server-visible commit-reveal dealing.

Default verifiable dealing answers this question: after a hand starts, can the server secretly swap the seed, swap the deck, or add a dramatic card by storyline? Its boundary is commit-reveal: commit to random material before dealing, reveal it after the hand, and let users recompute the deck order.

The now-available Advanced encrypted implementation answers a stronger design question: can the server itself see unrevealed hole cards while the hand is live? In code this path is called engine_blind. It is built on Mental Poker's real-crypto core: the server no longer advances the hand from a plaintext deck, but coordinates clients as they run encrypted shuffling, dealing, board opens, and showdown reveals. (A separate best-effort Mental Poker transcript mode runs on normal server-visible tables; it is not server-blind.)

Short version: normal mode proves "the cards were not changed afterward"; Advanced encrypted dealing moves the boundary to "unrevealed hole cards do not enter server plaintext state."

So this post avoids empty "more secure" language and follows the source path instead: which HTTP request marks a room engine_blind, how WebSocket attach carries that flag into the session task, which branch avoids the normal plaintext deck, what the Mental Poker coordinator can and cannot see, and how persistence, review, and Coach preserve that boundary afterward.

The engineering question is narrow: is there any code path that gives the server plaintext folded or pre-showdown unrevealed hole cards in an Advanced encrypted hand?

Open-source scope: The public mirror at github.com/CisaSettle/bluffking lets you inspect the Mental Poker crypto core, the poker rules engine, and related verifiable components. Production room orchestration and H5 integration are not part of that public-mirror promise, so the post only names source paths where they clarify the flow instead of front-loading a file list.

Who can enable it today

All-human, no-AI rooms, enabled by the host. The eligibility boundary is unchanged: at least 2 humans, no bots (ADR-041/068). A server-run bot must know its own seat's hole cards to act, so AI rooms remain ineligible. Default tables still deal server-visible commit-reveal. Once enabled, this card-secrecy covers only that hand in this mode; if any player drops, the whole table visibly downgrades to standard (server-visible) dealing.

ScenarioAdvanced encrypted?Reason
Default normal tableNoThe server sees cards for dealing, review, and Coach; commit-reveal can still verify that cards were not changed.
AI / bot tableNoThe server runs bots and must read each bot's own hole cards.
All-human, no AIYes — the host can enable itShipped under the trusted-single-operator scope; this hand's unshown hole cards are not held in plaintext by the server (practice chips).
Engine-blind room attach & routingYesCreate → WebSocket attach → coordinator now route engine-blind end to end.

Now open: create → attach → coordinator route engine-blind

Advanced encrypted dealing is not a local switch the browser can secretly flip, but it is now open server-side. Once the host of an all-human, no-AI room turns it on, three stages route it engine-blind end to end:

  1. HTTP create: the host's POST /api/lobby/create-room carries engine_blind: true; handlers/lobby.rs checks eligibility (all-human, no AI) and persists an engine_blind room — no more 503.
  2. WebSocket attach: GET /ws?session_id=... carries the room class into the session task and spawns the live protocol that coordinates the hand, instead of closing the connection.
  3. Runtime routing: the constant ENGINE_BLIND_RELEASE_AVAILABLE = true (no env override), so the session task runs the blind-dealing path engine_blind_routes_blind_coordinatorrun_engine_blind_live_handrun_mp_deal_blind.

The key split is: HTTP records what kind of room was created; WebSocket runs the live protocol for the hand. The backend session task still advances the hand. The browser does not decide dealing by itself — that is unchanged before and after the un-cage.

The engine-blind session task avoids the normal plaintext deck path

Normal tables use the standard server dealing path: the server creates or derives a deck, the engine advances from plaintext cards, and the completed hand is persisted with full review history.

The engine-blind implementation does not enter that path. When the session task sees engine_blind=true, it delegates the hand to run_engine_blind_live_hand. That coordinator then calls run_mp_deal_blind for DKG, encrypted deck construction, verifiable re-encryption shuffle, and the final ciphertext deck commit.

The engine side also does not start from a plaintext Deck. It uses a blind hand structure: hole-card positions are opaque, board cards are injected only after verified threshold opens, and showdown injects only legal hole-card reveals. That boundary leaves no room for an implementation that first gives the server a complete plaintext deck and then merely pretends not to look.

What the engine-blind implementation is designed to hide

The core of the engine-blind implementation is not "hide cards in frontend variables." It is a cryptographic access structure.

  • Each human client participates in DKG and holds its own secret share.
  • The deck is ElGamal ciphertext, passed through verifiable re-encryption shuffle by the participants.
  • The server coordinates, relays messages, verifies proofs, and maintains session state, but does not hold any player's secret share.
  • Opening a card needs n-of-n decryption contributions. Public board cards are public by poker rules, so each street opens them to everyone.
  • Hole cards open only in two places: locally to the owner when dealt, and at showdown when players still eligible for the pot reveal by rule.
player client share x_i player client share x_i player client share x_i server coordinator no share; relays messages, verifies proofs ElGamal ciphertext deck Hole card x₂ x₃ x₁ owner combines locally Board card x₁ x₂ x₃ P₁ P₂ P₃ opens to everyone Folded, never shown down never revealed

Engine-blind design: who can combine decryption shares for each card class. The coordinator relays shares but holds no share and never combines a hole index.

So the server can see public board cards, actions, chip changes, and hole cards that were already revealed at showdown. For folded or not-yet-showdown hole cards, it holds ciphertext, verifiable proofs, and the relayed non-owner decryption shares — but never the owner's share and never a combine path, so it cannot reconstruct plaintext.

Why this mode disables AI, Coach, live equity, and full history

This is not product restraint. It is the natural cost of the same security boundary.

FeatureBehavior in Advanced encrypted dealingReason
AI / botDisabledA server bot must read its own hole cards, which conflicts with server-blind dealing.
Coach / solver inputDisabledThe server does not have plaintext unrevealed holes to feed into Coach.
Live equity / hand strengthDisabled or not server-computedThese calculations depend on the hero's plaintext hole cards.
Full plaintext historyNot savedFolded or unrevealed hole cards should not appear in server history.

In persistence, hands.engine_blind=true is the authority marker; dealing_provider records mental_poker_engine_blind; deck_seed does not store a plaintext replay seed; seats_log uses empty sentinels for hole cards; and each player's row-level hole_cards can only come from a legal showdown reveal. coach.rs checks this marker before reading cards and refuses analysis directly. One current limit: the signed engine-blind transcript is not persisted with the hand, so this record preserves secrecy but is not yet an independently replayable proof artifact.

Disconnects, timeouts, and why this is not trustless-against-the-operator

The implementation needs every participant online and cooperating. The upside of n-of-n is a strong confidentiality boundary; the cost is weaker liveness: without a required share, some board opens or showdown reveals cannot complete. The downgrade path is this mode's stability design, not a defect.

The important rule is: never silently downgrade. For classified honest drops — a player goes offline — this mode voids the hand by rule and returns the chips. Then the server broadcasts mp_mode_downgraded, and the UI shows "Switched to standard mode — the server can see cards"; from the next hand the table deals server-visible, back on normal verifiable dealing. Detected malicious behavior or cryptographic failure does not open an escape hatch where the server switches the current hand back to visible cards and keeps dealing — it goes through the same void / teardown path. So downgrade is not a secret card swap, and it does not convert the current hand into a normal hand. It is a visible, sticky mode switch that takes effect from the next hand.

The unresolved case — the reason this is not trustless-against-the-operator (a disclosed residual, ADR-101 §8) — is selective delivery: every action travels through the single coordinator, so an ACK from that same coordinator cannot distinguish a reveal withholder from an honest player whose reveal the coordinator censored. Because the two are indistinguishable, this ADR changes no settlement code: any missing reveal that cannot be proven a malicious withhold is voided (stakes returned, protecting honest players), at the cost of a bounded, soft refund residual (at most once per table, practice chips, pre-reveal the dropper usually doesn't know if it helps). Fully removing that ambiguity requires independently authenticated action delivery or pre-outcome reveal escrow — the ADR-090 v2 exit, separate from this un-cage.

What cards can the server see?

Current product answer: default tables are server-visible (the server needs card data to deal, persist history, and run Coach); but on a hand where Advanced encrypted dealing is on, the server does not hold your unshown hole-card plaintext — folded hands that never show down included. Ordinary players and unauthorized APIs never receive other players' unrevealed hole cards in any mode.

Required disclosure: this secrecy holds only for that hand in this mode; if any player drops, the whole table visibly downgrades to standard (server-visible) dealing; card-secrecy relies on the operator running the audited open-source build — the running binary is not remotely verifiable; practice chips only. This is not trustless-against-the-operator: a compromised or malicious operator could still censor delivery or manipulate settlement (a disclosed residual, ADR-101 §8).

Boundary: this is not a third-party security audit or an "absolute security" promise.

The reusable rule: pick the access structure first, then let it dictate the feature list and the failure behavior — and route every plaintext sink (create, attach, routing, persistence, analysis) explicitly, downgrading visibly on a drop, never silently.