Game config
| Field | Default | |
|---|---|---|
key | — | A URL path segment |
players | — | How many form a match |
skillPool | — | A rating pool to bucket on. Omit for region only |
snapshot | — | The multiplayer session a formed match is minted into |
roomCodes.ttlSeconds | 900 | |
roomCodes.maxUses | 9 | |
queue.initialBand | Skill width at enqueue | |
queue.widenPerSecond | Growth per second waited | |
queue.maxWaitSeconds | After which the band is unbounded | |
queue.sweepSeconds | 5 | The cost dial |
Top level: friends (default on), basePath (default /matchmaking). The queue block is the waiting queue’s dial set; the rest configures the other three ways in.
Routes
Shaped like every route in the kit, and refusing with an ordinary error code.
Every one is user — bearer or session.
| Route | |
|---|---|
POST /matchmaking/games/:game/rooms | Open a room |
POST /matchmaking/rooms/:code/join | |
POST /matchmaking/games/:game/invites | |
GET /matchmaking/invites | |
POST /matchmaking/invites/:id/accept | |
POST /matchmaking/invites/:id/decline | |
GET /matchmaking/friends | |
POST /matchmaking/friends/:userId/request | |
POST /matchmaking/friends/:userId/accept | |
POST /matchmaking/friends/:userId/decline | |
DELETE /matchmaking/friends/:userId | |
POST /matchmaking/games/:game/queue | |
GET /matchmaking/games/:game/queue | |
DELETE /matchmaking/games/:game/queue | |
GET /matchmaking/presence | WebSocket |
There is no public surface. Every route binds to the authenticated user id, never to a client-supplied one. The friend routes mount only when friends is on.
Room codes
WXYZ-1234 — four letters, four digits.
Alphabets exclude I, O, 0 and 1. Join tolerates lowercase, whitespace and a missing dash.
Presence events
{ type: "match_found"; sessionId: string; gameKey: string }
{ type: "invite"; inviteId: string; gameKey: string; from: string }
{ type: "friend_request"; from: string }On connect: pending invites, and which friends are online.
Identity is a server-set header, stashed with serializeAttachment so it survives eviction.
Soft ceiling around 1,000 requests per second — one shared object. A throughput ceiling, not a bill.
Bindings
| Binding | Kind | |
|---|---|---|
DB | D1 | |
MATCHMAKING | KV | You paste the namespace id |
QUEUE | Durable Object | MatchmakingQueue |
PRESENCE | Durable Object | MatchmakingPresence |
SESSIONS | Durable Object | Multiplayer’s, not declared here |
Both classes are exported from their own modules, never the package entry point.
Storage
pithy_matchmaking_invites | Indexed by invitee, carrying the session id once accepted |
pithy_matchmaking_friends | One row per pair, indexed both ways |
KV matchmaking:<code> | Room codes |
Queue state is the queue object’s own storage. Presence is the live socket set. Neither is a row you keep.
Error codes
| Code | |
|---|---|
matchmaking/room_not_found | |
matchmaking/room_full | |
matchmaking/invalid_code | |
matchmaking/invite_not_found | |
matchmaking/invite_forbidden | |
matchmaking/already_friends | |
matchmaking/friend_request_not_found | |
matchmaking/user_not_found | A screen name matching zero users, or several |
matchmaking/not_queued |
Seams
| Absent | Result |
|---|---|
| Auth | Every route denies |
| Rating | Region-only bucketing |
| Multiplayer | Pairing runs; no session can be minted |