I went through all 119 changed files in PR #159. The short version: canvas sync went from an in-memory relay to a durable, authenticated system. Below is a full breakdown of every subsystem the PR touches, in dependency order, so you can see how the pieces fit together.
First, the starting point. Before this change, sync state lived entirely in server memory. Updates were relayed between connected clients and never written anywhere. One restart or dropped connection and collaborators could silently diverge, because there was no source of truth to replay from. The PR moves persistence into Supabase and gates every write behind auth, which touches the connection lifecycle, the update path, the storage schema, and the client reconnect logic all at once.
The authentication layer comes first because everything else depends on it. canvasAccess.ts introduces a per-request authorization check: every incoming update carries a session token, the server resolves it to a user, and the user's grant for that specific canvas is verified before the update is even parsed. Previously authorization only happened at connect time, which meant a revoked collaborator could keep writing on an open socket.
- ✓Persistence — every update is written to durable storage before it's acknowledged to the client; the acknowledgement now carries the storage sequence number so clients can detect gaps
- ✓Sync — reconnecting clients replay from the latest snapshot plus the update log tail, in a replay-safe order enforced by loroReplay.ts
- ✓Access — canvasAccess.ts validates authorization on every request, not just at connect time, and caches grant lookups per connection with invalidation on revoke
- ✓Schema — new tables for snapshots, update logs, and per-canvas access grants, with retention policies for compacting the log once a snapshot supersedes it
- ✓Broadcast — fan-out to other collaborators happens only after the durable write succeeds, so no client ever sees state the server could lose
On the storage side, the schema migration adds canvas_snapshots, canvas_update_log, and canvas_access_grants. Snapshots are full Loro document exports taken on a size threshold; the update log holds every incremental update since the last snapshot. Reconnect cost is therefore bounded: one snapshot fetch plus a short log tail, regardless of how long the canvas has existed.
The replay ordering in loroReplay.ts is the subtle part. Two clients reconnecting at once must converge on the same state, so updates are applied strictly in storage-sequence order and the server rejects any update whose causal dependencies haven't been persisted yet. There's a test matrix covering concurrent reconnects, mid-replay disconnects, and a snapshot landing during an active replay.
Risk-wise, the two places I'd review carefully are the grant-cache invalidation in canvasAccess.ts (a stale cache readmits a revoked user until the connection cycles) and the snapshot-during-replay path, which is the only spot where two writers touch the same log range.
Everything else — the remaining ~90 files — is mechanical: renamed imports, the client-side reconnect state machine, and test fixtures updated for the new acknowledgement shape. Want me to walk through the replay path in detail?
119 files changed+16,523−3,403