The transport: LiveKit for media, SignalR for state
Most collab tools punt on this and use a single transport. We chose two — LiveKit for video/audio/screen, SignalR for everything else — and we don't regret it.
The split lets us optimise each pipe for what it actually carries. LiveKit handles the multi-megabit/second media path with SFU forwarding. SignalR carries small JSON deltas — typically under 200 bytes — and can survive a 5-second reconnect without breaking the room.
Why we picked CRDTs over OT
Operational Transform is the obvious choice if you're building shared editors. It's what Google Docs uses. It's what our first prototype used. And it broke us in week two.
The problem is that OT requires a central server to serialise operations. Our entire premise was peer-driven sync — clients sending deltas at each other through SignalR with the server as a thin relay. CRDTs are commutative by construction: any two clients applying the same set of edits in any order land at the same state. No central authority required.
The first OT bug we hit was a classic: two cursors edit the same line, both inserts, server reorders them, and one client ends up with “Hello world” while the other has “Hellrold w” — divergent, wrong, and no way to recover without a hard reset.
The failover that saved us at 11pm
In December our self-hosted LiveKit cluster failed during the EU evening peak. Forty-six rooms went silent. We had no fallback.
The fix: a health-monitor background service that probes the primary every 15 seconds and routes new connections to LiveKit Cloud after three consecutive failures. Existing rooms drain naturally; new rooms come up on the fallback in under a second.
Wrapping up
Three years in, the LiveKit + SignalR + CRDT stack is still the right call. The 95th percentile end-to-end edit latency is 78 ms; the 99th is 142 ms. Most of that is the speed of light.