DealSession
Drives a cryptographically fair card deal over a Seam, as an op-based CRDT.
Each card is a CardState; players sequentially encrypt the deck (SRA layers compose because the cipher is commutative), then non-quorum players strip their layer so only the visibility quorum can read each card. A partial multi-member quorum (1 < |quorum| < N) additionally runs per-member reveal tracks (QuorumTrack) so each member — and no one else — ends up able to decrypt (see strip). State converges via the CardOp stream broadcast over the seam: each op carries the base sets it was computed against and is folded in via CardState.merge, so convergence holds under per-sender FIFO delivery even when ops from different senders interleave differently at different peers (cross-sender transport reorder).
Single-local-writer assumption. The shuffle and strip helpers read the base ciphertext from state outside the _state.update block, run crypto, then commit. If the incoming-op collector races to apply a remote op to the same card in that window, the local op was computed against a stale base and its merged ciphertext can lose the remote layer while the metadata claims it. This is safe under the current model where writes to a card are serialized by the protocol (one shuffler/stripper at a time); concurrent local shuffle/strip while remote ops mutate the same card is a production hazard that needs an "await my turn" gate (deferred).
Constructors
Functions
Set visibility quorums for each card (local state — no broadcast).
Decode the plaintext of the card at cardIndex. Call once the card is CardPhase.REVEALED. If my own layer is the one remaining (single-reader quorum), it is stripped locally first; a community card (everyone stripped) decodes directly; a partial multi-member quorum member reads their own reveal track (QuorumTrack) and strips their layer locally. Local — does not broadcast.
Optionally escrow my key to a trusted server for liveness recovery.
Encrypt every card with my key. Seeds the deck from deck on first call (encoding each plaintext once via encodePlaintext). If remote ops have already grown the deck with placeholder cards (the synchronous test harness delivers the first shuffler's ops before the second shuffler calls this), the deck is used as-is so the second shuffler's key composes on top.