Skip to content

SyncInbox Durable Object

src/durable-objects/sync-inbox.ts defines SyncInbox, the project’s first and only Durable Object (ADR-011). Each authenticated user connects to one inbox keyed by their verified userId; the inbox fans a tiny SyncSignal (“surface X changed”) to that user’s open sockets.

  • fetch(request) accepts the WebSocket upgrade and calls this.ctx.acceptWebSocket(server)hibernatable WebSockets (not server.accept()), so idle inboxes pause duration billing (constitution §8.4). Idle cost ≈ 0 is the economic point of the design.
  • Connection cap — at most MAX_CONNS open sockets per user; on a new connection beyond the cap the oldest sockets are evicted with CLOSE_CODES.CONNECTION_CAP (multi-tab soft cap).
  • notify(signal) — the RPC the Worker calls to broadcast a SyncSignal to all open sockets.
  • webSocketClose / webSocketError — lifecycle hooks that clean up connection state.
  • The DO holds ephemeral connection state only — no business data; D1 remains the single source of truth (constitution §7.10).
  • It is unreachable except through the authenticated /api/v1/sync/stream upgrade route, which runs Clerk auth and an Origin/CSWSH check before forwarding to the user’s stub (constitution §7.12).
  • The SyncSignal shape and MAX_CONNS/CLOSE_CODES constants are shared via src/lib/sync/signal.ts.

Related: Worker API & Clerk Auth (the upgrade route + auth), LiveConnection WS Client (the browser end), DB Schema & Notify-Affected (who gets notified).