How the Docs Module Implements Real-Time Collaboration with Loro CRDTs and their‑mirror

The Macro Docs product delivers instant, conflict‑free collaboration by storing each document in a Loro CRDT object (via the their‑mirror library) and syncing edits through a WebSocket‑connected sync‑service durable object.

The Macro repository's docs module implements conflict-free replicated data types (CRDTs) to enable multiple users to edit documents simultaneously without locks or merge conflicts. According to the macro‑inc/macro source code, this architecture combines the Loro CRDT engine, a JavaScript bridge called their‑mirror, and a Rust‑powered sync‑service backend running on Cloudflare Workers.

Core Architecture: Loro CRDT + their‑mirror

At the heart of the system is Loro, a high‑performance CRDT library written in Rust with JavaScript bindings. The Macro team built their‑mirror (packages/loro‑mirror) to create an ergonomic JavaScript API over raw Loro operations.

The Mirror class in packages/loro‑mirror/src/core/mirror.ts provides bidirectional synchronization between plain JavaScript state and a Loro<Schema> document:

  • It tracks local mutations and translates them into Loro operations.
  • It applies incoming remote operations to the local document.
  • It exposes a reactive state object that UI components can read directly.
import { Mirror } from '@macro-inc/loro-mirror';

const mirror = new Mirror({
  schema: MarkdownSchema,
  docId: 'doc-123',
  onUpdate: (updates) => ws.sendUpdates(updates),
});

The schema system in packages/loro‑mirror/src/schema/index.ts defines how JavaScript types map to Loro CRDT primitives:

Schema Helper Loro Type Use Case
schema.text() LoroText Rich text content, markdown
schema.list(child) LoroList Ordered collections (mentions, comments)
schema.map(shape) LoroMap Structured objects (metadata, attributes)

Connecting to the Sync‑Service

Real‑time synchronization happens through sync‑service, a Cloudflare Worker that hosts per‑document Durable Objects. Each document ID maps to exactly one durable object "room" that maintains the canonical CRDT state and broadcasts updates to all connected clients.

The client‑side connection is managed by SyncServiceSource in packages/collaboration/src/sync-service/source.ts:

import { createSyncClient } from '@macro-inc/collaboration/sync-service/socket';

const ws = createSyncClient(`wss://sync-service.example.com/document/${docId}`);

ws.onMessage((bebopEncodedUpdates) => {
  mirror.applyUpdates(bebopEncodedUpdates);
});

Key responsibilities of the sync‑service pipeline:

  1. WebSocket lifecycle: Opens a persistent connection to the durable object.
  2. Bebop encoding: All CRDT updates are serialized with Bebop for compact, fast binary transfer.
  3. Broadcast fan‑out: When any peer sends an update, the durable object broadcasts it to all other sockets in the same room.

The Collaboration Loop: How Edits Flow

The complete data flow for a real‑time edit follows this path:


┌─────────┐    local edit     ┌─────────┐    Loro op     ┌─────────────┐
│   UI    │ ─────────────────→│  Mirror │ ──────────────→│  WebSocket  │
└─────────┘                   └─────────┘                └──────┬──────┘
     ↑                                                          │
     └──────────────────────────────────────────────────────────┘
                            remote op (broadcast)

Step‑by‑step operation:

  1. User types in the editor → UI calls mirror.getState().body.insert(index, text).
  2. Mirror creates Loro operation and invokes onUpdate callback.
  3. SyncServiceSource transmits the Bebop‑encoded update to the durable object.
  4. Durable object broadcasts to all connected clients (including the sender, for echo).
  5. Remote clients apply updates via mirror.applyUpdates(), merging silently with any local edits.
  6. UI re‑renders from mirror.getState(), now reflecting the merged state.

Because Loro CRDTs guarantee strong eventual consistency, concurrent edits from multiple users are automatically merged without conflicts—even if users go offline and reconnect later.

Offline Support and Version Vectors

The their‑mirror architecture includes robust offline capabilities:

  • Local persistence: Each client's Mirror can serialize its full document state (including version vector) to IndexedDB or local storage.
  • Incremental sync on reconnect: When a client rejoins, it sends its version vector to the sync‑service durable object.
  • Delta encoding: The server computes and sends only the operations the client missed, minimizing bandwidth.

This design ensures users can continue editing without network access and seamlessly sync when connectivity returns.

Presence and Cursor Tracking

Beyond document content, the sync‑service protocol carries presence metadata for live collaboration indicators. The Mirror can expose peer cursor positions, selection ranges, and user awareness data that the UI renders as colored cursors or "user is editing" badges.

The protocol distinguishes document operations (CRDT updates) from presence messages (ephemeral state), routing each appropriately through the same WebSocket connection.

Summary

  • Loro CRDT (packages/loro‑mirror) provides the underlying merge‑free data structure for document state.
  • their‑mirror's Mirror class (packages/loro‑mirror/src/core/mirror.ts) bridges idiomatic JavaScript APIs with Loro operations.
  • Schema definitions (packages/loro‑mirror/src/schema/index.ts) type‑check document structures at runtime.
  • SyncServiceSource (packages/collaboration/src/sync-service/source.ts) manages WebSocket transport and Bebop encoding.
  • Sync‑service durable objects (services/sync-service) host per‑document rooms and broadcast updates.
  • Offline‑first design with version vectors and delta sync ensures resilience.

Frequently Asked Questions

What is their‑mirror and why does Macro use it?

their‑mirror is Macro's JavaScript abstraction layer over the Loro CRDT engine. It provides a reactive, schema‑validated API that converts between plain JavaScript objects and Loro's internal operation log. Macro uses it to simplify UI integration while retaining Loro's performance and correctness guarantees.

How does the sync‑service handle concurrent edits from dozens of users?

Each document runs in its own Cloudflare Durable Object with single‑threaded execution. All edits are serialized through this central point, then broadcast as CRDT operations that merge deterministically on each client. The Durable Object acts as a coordination hub without being a bottleneck—CRDT math happens on clients, not the server.

Can users edit documents while offline?

Yes—offline editing is a core feature. Clients store their Loro document state locally with a version vector. On reconnection, they exchange version vectors with the sync‑service and receive only the operations they missed, merging them automatically with any edits made offline.

What data format travels over the network?

All updates use Bebop binary encoding. This compact, schema‑driven format minimizes payload size compared to JSON. The SyncServiceSource class handles encoding/decoding transparently so application code works with JavaScript objects.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →