Skip to main content
Version: 2.0 PartyKit

Architecture

Repository layout​

/
β”œβ”€β”€ back/ ← PartyKit backend (Durable Object)
β”‚ β”œβ”€β”€ game.ts ← GameSession class (main entry point)
β”‚ β”œβ”€β”€ types.ts ← exported types & interfaces
β”‚ β”œβ”€β”€ constants.ts ← word lists, category prompts
β”‚ β”œβ”€β”€ utils.ts ← pure utility functions (shuffle, pseudonym gen)
β”‚ β”œβ”€β”€ scoring.ts ← pure scoring functions
β”‚ └── timer.ts ← TimerManager class
β”œβ”€β”€ front/ ← Astro + React frontend
β”‚ └── src/
β”‚ β”œβ”€β”€ lib/socket.ts ← PartySocket factory
β”‚ β”œβ”€β”€ types/events.ts ← shared event payload types
β”‚ β”œβ”€β”€ stores/
β”‚ β”‚ β”œβ”€β”€ hostStore.ts
β”‚ β”‚ └── participantStore.ts
β”‚ β”œβ”€β”€ hooks/
β”‚ β”‚ β”œβ”€β”€ useHostSocket.ts
β”‚ β”‚ └── useParticipantSocket.ts
β”‚ └── components/
β”‚ β”œβ”€β”€ host/
β”‚ └── participant/
β”œβ”€β”€ docs-site/ ← Docusaurus docs
β”œβ”€β”€ proxy-worker/
β”‚ └── index.ts ← Cloudflare Worker: routes /docs/* to docs Pages
β”œβ”€β”€ partykit.json ← PartyKit config + KV binding
β”œβ”€β”€ package.json ← root: partykit + partysocket
└── docker-compose.yml ← Mode B: full-stack with PartyKit in Docker

Runtime communication​

Browser (Host)          Browser (Participant)
β”‚ β”‚
β”‚ PartySocket ws β”‚ PartySocket ws
β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PartyKit Durable Object β”‚
β”‚ room = 4-digit session code β”‚
β”‚ β”‚
β”‚ this.room.storage ← game state β”‚
β”‚ this.room.broadcast ← fan-out msgs β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ KV write
β–Ό
Cloudflare KV
SOMMELIER_HOSTS
key: host:{TANNIC-FALCON}

Durable Object lifecycle​

HookCalled whenWhat we do
onStart()DO wakes from evictionRestore SavedState from storage
onConnect(ws)Client opens WebSocketRegister connection (host or participant)
onMessage(ws, msg)Message arrivesParse { type }, dispatch to handler
alarm()Timer alarm firesAuto-reveal current question
onClose(ws)Client disconnectsMark participant offline; detect host drop

Host vs participant socket flow​

Host joins β†’ sends rejoin_host { hostId } on socket open β†’ server validates hostId against stored value β†’ sends host:state_snapshot

Participant joins β†’ sends join_session { code } β†’ server creates participant, issues rejoinToken, broadcasts lobby:updated

Participant rejoins β†’ localStorage { rejoinToken, code } detected on mount β†’ sends rejoin_session { rejoinToken } β†’ server sends participant:state_snapshot

Docker vs PartyKit dev​

Mode A (partykit dev)Mode B (docker)
Backendnpx partykit dev on :1999partykit Docker container on :1999
Frontendnpm run dev on :4321nginx on :4321
Docscd docs-site && npm startDocker container on :3002
Requires DockerβŒβœ…
Hot-reload backendβœ…βŒ