CodeSync: Collaborative Editor
January 12, 2026
Real-time collaborative code editor (Google Docs, but for code). Multiple users with live cursor tracking. Rust handles document merging (CRDTs), Node.js manages WebSocket connections.
What is it?
I wanted to build a collaborative code editor that felt like Google Docs for code. Multiple users can join the same session, type at the same time, see each other’s cursors, and converge on the same document without manual conflict resolution.
That goal pushed me into CRDTs, WebSocket gateway design, session management, and the split between low-latency collaboration and durable document state. It is one of those projects that looks simple from the outside but gets complicated fast as soon as two people type at once.
Architecture
I split the system into three pieces. The frontend uses Monaco Editor. A Node.js gateway handles WebSocket connections and session-level coordination. A Rust service handles the heavier CRDT persistence logic. When one client makes an edit, the gateway broadcasts it immediately to the other clients and also forwards it to the Rust engine.
I liked this split because it kept the fast network fan-out in JavaScript, where it was easy to iterate, while moving the state-heavy CRDT work into Rust. I also added a fallback path where the gateway can temporarily use an in-process JavaScript CRDT if the Rust service is unavailable. That way the whole editor does not fall apart just because one internal service is cold or restarting.
The CRDT: Loro
The key idea behind a CRDT is that multiple users can make changes independently and still end up with the same final document once all operations are delivered. I used Loro, which assigns identity to edits in a way that lets concurrent operations merge deterministically.
At a high level, each inserted character carries enough identity information that two people inserting text at the same location can still be resolved in a stable order. Deletes do not immediately erase history either. They act more like tombstones, which helps the merge logic stay consistent across peers. Once I understood that model, CRDTs stopped feeling magical and started feeling like very careful bookkeeping.
Security layer
With a WebSocket app, I did not want random origins and random traffic hammering the gateway. So I added origin checks before upgrade, internal authentication between the Node gateway and the Rust engine, request rate limiting, and caps on document size.
Those limits matter because collaboration systems are easy to abuse. A giant document or a flood of events can hurt performance quickly. By bounding request rate and document size, I keep the service much more predictable and reduce the ways a single bad client can make the session worse for everyone else.
Deployment on Cloud Run
The Rust engine is built with a multi-stage Docker image so I get a proper compile environment during build and a much smaller runtime image in production. The gateway runs separately and keeps long-lived WebSocket connections open.
One deployment detail that mattered a lot was timeout configuration. Cloud Run’s defaults are not friendly to long-lived collaboration sessions, so I had to raise the timeout to keep sessions alive. Otherwise the infrastructure would have quietly killed exactly the kind of persistent connection the product depends on.
Key takeaways
- Loro CRDT: peer+counter character IDs, tombstone deletions, deterministic convergence
- Gateway fallback pattern: JS CRDT as in-process backup when Rust engine is cold
- Cloud Run WebSocket timeout: 3600s needed to keep long-lived connections alive
- Multi-stage Rust Docker build: bookworm builder to bookworm-slim runtime for minimal image
- LRU session cache in Rust: lru crate, bounding memory with max 10K sessions