Skip to content
AlphaMergeCase study 07 — DurakLive · our own product
Case study 07·Realtime game · live

Durak — six cards each, and somebody has to lose.

The classic game, played against bots, with friends who have your code, or around one screen in the same room. The rules live on the server and only on the server, so a client has nothing to argue with — and a card game on an authoritative server turns out to be a privacy problem before it is a rules problem.

06
Cards a hand
03
He · en · ru
01
Authority
12
Story tables
TV
Couch mode
2

Hand privacy

The hard part

The room's state contains every hand at the table. Synchronise it naively and each player is shipped everyone else's cards — invisible on screen, and perfectly readable in the console.

Hiding a hand in the interface is not hiding it. The fix has to happen before the bytes leave the server, and it cannot be a hand-rolled filter in the broadcast path, because every new field added to the room state is another chance to forget one. So the state schema itself declares what each seat is allowed to see, and the framework filters every patch per client on the way out. A player receives their own cards and the public table; the other hands are not withheld from the UI, they are never sent.

01The server owns the deck, the turn and every legality check
02State patches are filtered per client, per seat
03A hand reaches exactly one socket and no other
04Couch mode holds too — the TV is sent no hand at all
3

The room

Rooms are attached to the same HTTP server as the API rather than run beside it, which keeps one process, one port and one certificate. The cost is a routing subtlety worth writing down: a room socket connects to two path segments at the root — the same shape as a single-page route like /play/:code. A proxy cannot tell them apart by path, so the connection is routed by its upgrade header instead.

One process

Realtime rooms and the REST API share a server, so there is no second deployment to keep in step and no second certificate to renew.

Routed by upgrade

Because a room path and an app route are indistinguishable, routing switches on the handshake rather than the URL. It is documented before it is changed.

Join by code

Play now deals you in against three bots — easy, medium or hard, and one takes over a seat that drops. Otherwise join a friend's code, put the table on a TV, or take a story table, which waits for its one human rather than filling the seat. Guests play without an account; signing in is what starts counting your games.

A dark red screen headed “Somebody has to lose.” A name field, an orange “Play now” button, then “Join with a code” and “Play on a TV” side by side, a collapsed “Table settings” row, and a Google sign-in line. Language switches sit in the top bar.
The lobby — play now, join with a code, or open the table on a TV. Hebrew, English and Russian.
4

The Long Night

Story mode

A random table teaches you nothing in particular. Twelve fixed ones, played in order, can: each seats named regulars at a set deck and variant, opens with a scene and closes with another, and asks for something harder than the usual bar — not to avoid being the fool, but to go out first. A table moves either the opposition or the rules from the one before it, never both, and a test fails if one moves both.

Regulars, not levels

The six now play as differently as they talk. The Colonel will not spend a good trump cheaply; the Undertaker piles on once the deck is gone; the Apprentice throws trumps in and never transfers. A briefing names each one and how they play, in all three languages.

A seat kept for you

A story table has one human seat, no spectators and no takeover bot: leave, and it waits. The seed and every move are saved as you play, so a table interrupted by a deploy is dealt back exactly where it stood, for a week.

Where it stands

Signed in only — progress belongs to an account, so a guest has none to keep. The first three tables are free; the remaining nine wait behind a Campaign Pass that is built but not yet on sale.

5

Build

As deployed
ClientAngular PWA — standalone, strict, zoneless · installable
BoardPixiJS 8 · GSAP, in a chunk loaded only when a table opens
TableClassic or transferable · 36 or 52 cards · two to six seats · six regulars who each play differently as well as talk, quiet for under-16s
RealtimeColyseus 0.17 authoritative rooms with per-client state views
ServerExpress · TypeScript · Mongoose · Google sign-in and guest sessions
DeliveryMulti-stage Docker · Caddy with automatic TLS · Actions to a GCP VM behind a health gate · keyless deploy, nightly off-box backups
StatusLive — every merge to main deploys

Deal yourself in and check the console.

Guests play without an account. The other hands are not in the payload — that is the whole claim, and it is inspectable.