# How the Macro Sync-Service Uses Cloudflare Workers for Real-Time Synchronization

> Discover how the Macro sync-service leverages Cloudflare Workers for real-time synchronization. Explore its low-latency collaboration with WebSockets and CRDTs.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: how-to-guide
- Published: 2026-08-16

---

**The Macro sync-service runs as a Cloudflare Workers Durable Object that provides low-latency, real-time collaboration by combining WebSockets, CRDT-based document state, and edge-persistent storage.**

The [Macro](https://github.com/macro-inc/macro) open-source project implements its sync-service on Cloudflare's edge runtime to deliver scalable document collaboration. By leveraging **Durable Objects**, **KV storage**, **D1 databases**, and **WebSockets**, the service maintains mutable state in single-threaded isolates while persisting data across the global Cloudflare network.

## Cloudflare Workers Durable Object Architecture

The sync-service's foundation is the `DocumentSyncSession` struct, declared with the `#[durable_object]` attribute in [`durable_object.rs`](https://github.com/macro-inc/macro/blob/main/durable_object.rs). This annotation tells the Workers runtime to instantiate each document in its own isolated Durable Object.

```rust
#[durable_object]                     // ← Durable Object declaration
pub struct DocumentSyncSession { … } // services/sync-service/src/durable_object.rs

```

Each Durable Object provides:

- **Single-threaded execution** – Safe mutable state without locks
- **Persistent identity** – Survives across requests and restarts
- **Location affinity** – Runs at the edge closest to the first request

The module imports core Workers SDK types to interact with the runtime:

```rust
use worker::{
    Cors, Date, DurableObject, Env, Error, Method, Request, Response,
    WebSocket, WebSocketIncomingMessage, WebSocketPair, durable_object,
};

```

## WebSocket-Based Real-Time Channel

When a client connects, the service upgrades HTTP requests to WebSocket connections using `WebSocketPair::new()`. This handshake occurs in [`websocket.rs`](https://github.com/macro-inc/macro/blob/main/websocket.rs), which handles the full lifecycle of real-time communication.

```rust
// sync-service/src/websocket.rs (excerpt)
pub async fn handle_connect(req: Request, env: Env, state: State) -> Result<Response> {
    let pair = WebSocketPair::new()?;
    let client_ws = pair[0].clone();
    let server_ws = pair[1].clone();

    let dss = state.get::<DocumentSyncSession>()?;
    dss.register_ws(server_ws).await?;

    Response::from_websocket(client_ws)
}

```

The `Wsm` wrapper manages socket metadata through `maybe_update_ws_meta_map`, tracking all connections in `ws_meta_map`. Incoming messages are parsed, applied to the CRDT document, and broadcast to every connected peer.

### Client Connection Example

```ts
// TypeScript client (apps/web)
const docId = "12345";
const ws = new WebSocket(`wss://sync-service.${CLOUDFLARE_WORKER_SUBDOMAIN}/connect/${docId}`);

ws.onopen = () => console.log("Connected to sync service");
ws.onmessage = (e) => {
  const update = JSON.parse(e.data);
  // Apply update to local Loro CRDT instance
};

```

## CRDT Document State with Loro

The service uses **Loro**, a Rust CRDT library, to represent document state through the `DocumentState` struct in [`state.rs`](https://github.com/macro-inc/macro/blob/main/state.rs). This approach eliminates merge conflicts by design.

```rust
// sync-service/src/state.rs (excerpt)
pub async fn save_snapshot(&self, snapshot: &[u8]) -> Result<()> {
    let storage = self.session_storage.lock("save_snapshot")?;
    storage.put(&self.document_id, snapshot).await?;
    Ok(())
}

```

Each change is applied locally and immediately broadcast. New peers retrieve the latest snapshot via the dedicated `snapshot` endpoint, enabling instant catch-up without replaying full history.

## Edge Persistence: KV and D1 Storage

The sync-service uses two Cloudflare storage systems with different consistency guarantees:

### Durable KV for Document Snapshots

Writes to `session_storage` persist document state to **Cloudflare KV**, ensuring durability across Durable Object restarts. This is the hot path for real-time synchronization.

### D1 SQLite for Metadata

[`d1.rs`](https://github.com/macro-inc/macro/blob/main/d1.rs) manages relational data including:
- Peer-to-user mappings
- Blame events for attribution

```rust
// sync-service/src/d1.rs handles batched writes to D1

```

This metadata is flushed periodically rather than on every operation, keeping the real-time path unblocked.

## Keep-Alive and Scheduled Alarms

Cloudflare Workers support **scheduled alarms** that the sync-service uses to extend object lifetime. The [`keepalive.rs`](https://github.com/macro-inc/macro/blob/main/keepalive.rs) module implements this pattern:

- Prevents idle Durable Objects from garbage collection
- Flushes pending D1 writes on each alarm tick
- Maintains low latency for active sessions

```rust
// sync-service/src/keepalive.rs
// Alarm handler extends TTL and batches metadata persistence

```

This design ensures eventual consistency for relational data without sacrificing real-time performance.

## Authentication and Access Control

Before any WebSocket upgrade or state modification, [`auth.rs`](https://github.com/macro-inc/macro/blob/main/auth.rs) validates JWT tokens and enforces access levels:

| Level | Permissions |
|-------|-------------|
| read | Receive updates, cannot modify |
| write | Full collaborative access |

Unauthorized requests receive `HTTP 401` via `response(status_codes::UNAUTH)`.

## Worker Entry Point and Request Routing

The [`cf_worker.rs`](https://github.com/macro-inc/macro/blob/main/cf_worker.rs) file defines the top-level `fetch` handler that routes incoming HTTP requests to the appropriate Durable Object instance:

```rust
// sync-service/src/cf_worker.rs
// Worker entry point forwards routes to DocumentSyncSession

```

This separation allows the sync-service to scale horizontally—each document lives in its own Durable Object while sharing the same code and infrastructure.

## Summary

- **Durable Objects** provide isolated, stateful contexts for each document in [`durable_object.rs`](https://github.com/macro-inc/macro/blob/main/durable_object.rs)
- **WebSockets** enable real-time bidirectional communication through [`websocket.rs`](https://github.com/macro-inc/macro/blob/main/websocket.rs)
- **Loro CRDT** guarantees conflict-free concurrent editing in [`state.rs`](https://github.com/macro-inc/macro/blob/main/state.rs)
- **Cloudflare KV** persists snapshots durably across restarts
- **D1 database** stores metadata with batched writes via [`d1.rs`](https://github.com/macro-inc/macro/blob/main/d1.rs)
- **Scheduled alarms** in [`keepalive.rs`](https://github.com/macro-inc/macro/blob/main/keepalive.rs) extend object lifetime and flush pending data
- **JWT validation** in [`auth.rs`](https://github.com/macro-inc/macro/blob/main/auth.rs) secures all endpoints before access

## Frequently Asked Questions

### What makes Durable Objects suitable for real-time collaboration?

Durable Objects provide single-threaded execution with persistent identity, eliminating the need for distributed locking. Each document gets its own isolate that can hold mutable state and coordinate peers without cross-instance communication overhead.

### How does the sync-service handle concurrent edits from multiple users?

The service uses Loro, a CRDT (Conflict-free Replicated Data Type) library. CRDTs guarantee that all peers converge to the same state regardless of edit ordering, eliminating the need for operational transformation or central locking.

### What happens if a Durable Object restarts?

Document snapshots are persisted to Cloudflare KV via `save_snapshot()` in [`state.rs`](https://github.com/macro-inc/macro/blob/main/state.rs). When the Durable Object restarts, it retrieves the latest snapshot to restore state. Metadata in D1 is eventually consistent through the alarm-based flush mechanism.

### Can the sync-service scale to millions of documents?

Yes—each document runs in its own Durable Object, so the architecture scales horizontally. Cloudflare automatically creates new Durable Object instances on demand and migrates them to optimal edge locations based on traffic patterns.