# How GeoLibre Real-Time Collaboration Synchronization Works: WebSocket Architecture Explained

> Discover how GeoLibre uses WebSocket architecture with Cloudflare Durable Objects for real-time collaboration synchronization of edits, cursors, and chat across users. Learn more about opengeos/GeoLibre.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: internals
- Published: 2026-08-01

---

**GeoLibre uses a lightweight WebSocket relay built on Cloudflare Durable Objects to synchronize live edits, presence cursors, and chat across multiple users in real time.**

Real-time collaboration in GeoLibre enables multiple users to edit geographic projects simultaneously without conflicts. The system, as implemented in [opengeos/GeoLibre](https://github.com/opengeos/GeoLibre), separates concerns into three distinct layers: client-side transport, server-side relay, and durable persistence. This article breaks down exactly how synchronization works across these layers, from session creation to live cursor updates.

## The Three-Layer Architecture

GeoLibre's real-time collaboration synchronization relies on a clean separation between **client**, **relay**, and **storage**. Each layer has specific responsibilities and error-handling strategies.

| Layer | Key Responsibility | Primary File |
|-------|------------------|--------------|
| **Client** | WebSocket connection, local state observation, UI integration | [`apps/geolibre-desktop/src/lib/collab-client.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/collab-client.ts) |
| **Relay** | Connection management, permission enforcement, broadcast coordination | [`workers/collab/src/session.ts`](https://github.com/opengeos/GeoLibre/blob/main/workers/collab/src/session.ts) |
| **Storage** | Durable snapshot persistence, revision tracking, bounded chat history | [`workers/collab/src/session.ts`](https://github.com/opengeos/GeoLibre/blob/main/workers/collab/src/session.ts) (Durable Object storage) |

The client layer watches the Zustand store for local edits and publishes **snapshot**, **presence**, **chat**, and **mode** frames. The relay layer enforces **monotonic revision numbers**, **byte-size limits** (~1 MiB for snapshots), and **rate-limits** for chat messages. The storage layer maintains the latest project snapshot and revision in Cloudflare's Durable Object storage, ensuring durability across server restarts.

## Session Initialization and WebSocket Handshake

Collaboration begins with session creation and the join handshake. Understanding this flow explains how GeoLibre establishes trust and state synchronization.

### Creating a Session

A host initiates collaboration via `POST /sessions`, which triggers the Durable Object's `/init` endpoint in [`workers/collab/src/session.ts`](https://github.com/opengeos/GeoLibre/blob/main/workers/collab/src/session.ts). This stores:

- The session **mode** (`co-edit` or `view-only`)
- A cryptographically random **host token** for subsequent authority claims

### The Join Handshake

When a client connects to `/ws`, the Durable Object accepts the WebSocket upgrade via `this.ctx.acceptWebSocket`. The client then sends a `join` frame:

```typescript
// Client sends
{
  "type": "join",
  "displayName": "Alice",
  "color": "#4a90e2",
  "hostToken": "optional-token-if-host"
}

```

The server responds with a `welcome` frame containing:

- A server-generated `clientId` (unique to this connection)
- Current **session mode**
- The latest **project snapshot** (parsed safely, never executed)
- Live **presence map** of all participants
- Recent **chat history** (trimmed to limits)
- Current **revision number**

This handshake ensures late joiners immediately receive the newest state. The `handleJoin` function in [`session.ts`](https://github.com/opengeos/GeoLibre/blob/main/session.ts) coordinates this exchange.

## Snapshot Synchronization: The Core Edit Flow

Project edits follow a strict validation and broadcast pattern. This is where GeoLibre's real-time collaboration synchronization prevents conflicts and data corruption.

When a participant makes a local edit, the client:

1. Serializes the new project to JSON
2. Increments its local revision tracker
3. Sends a `snapshot` frame

The server validates before accepting:

```typescript
// Simplified validation in session.ts
if (!canEdit(clientId)) throw 'Not authorized';
if (byteSize > MAX_SNAPSHOT_BYTES) throw 'Snapshot too large';
if (rev <= storedRevision) throw 'Stale revision';

```

On validation success, the server:

- Persists the new snapshot: `this.ctx.storage.put('snapshot', project)`
- Updates stored revision: `this.ctx.storage.put('revision', rev)`
- Broadcasts to all other sockets: `this.broadcast({ type: 'snapshot', project, rev })`

The **monotonic revision requirement** ensures that out-of-order messages or stale edits are rejected, preventing conflicting state merges.

## Presence Updates: Cursor and Viewport Tracking

Presence data—cursors, viewports, and participant activity—uses a separate, lighter-weight path. Unlike snapshots, presence lives **only in memory** for fast updates.

When a mouse moves or the map pans, the client sends:

```typescript
collab.sendPresence({
  cursor: { lng: -122.4194, lat: 37.7749 },
  view: { bounds: [[sw], [ne]], zoom: 12 }
});

```

The server sanitizes via `sanitizeCursor` and `sanitizeView`, updates the in-memory `presence` Map, and forwards to peers. No disk write occurs, keeping latency under 50ms for cursor movements.

## Chat System: Rate-Limited and Bounded

Chat messages persist to durable storage but with strict resource protection:

- **Rate-limiting**: Per-socket tokens prevent spam
- **Size validation**: `isValidChatMessage` checks structure and length
- **Dual trimming**: `CHAT_HISTORY_LIMIT` (count) and `MAX_CHAT_STORAGE_BYTES` (size)

The server appends valid messages, trims the log, persists with `this.ctx.storage.put('chat', trimmedLog)`, then broadcasts.

## Permission Model: Host Controls and Participant Overrides

GeoLibre's collaboration synchronization includes fine-grained permission controls. The host—the session creator—maintains special authority through the stored host token.

Host capabilities include:

- **`set-mode`**: Change global session mode between `co-edit` and `view-only`
- **`set-participant-mode`**: Toggle individual guest edit permissions

When permissions change, the server:

1. Updates durable storage: `this.ctx.storage.put('mode', newMode)`
2. Clears stale overrides to ensure immediate effect
3. Broadcasts the change to all connected clients

Guests without the host token can only view and chat, not modify session configuration.

## Client Integration: React Hooks and Zustand Store

The desktop application integrates collaboration through [`apps/geolibre-desktop/src/hooks/useCollaboration.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/useCollaboration.ts). This React hook:

- Instantiates the `CollabClient` from [`collab-client.ts`](https://github.com/opengeos/GeoLibre/blob/main/collab-client.ts)
- Subscribes to store changes and dispatches snapshots
- Exposes UI state: participant list, chat messages, remote cursors, session mode

The Zustand store slice in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) holds runtime collaboration state separate from persistent project data, enabling clean separation between local editing and remote synchronization.

## Security and Defensive Design

The relay's agnostic design—never parsing project contents beyond JSON validation—provides defense in depth:

- **Type strictness**: Protocol definitions in [`workers/collab/src/protocol.ts`](https://github.com/opengeos/GeoLibre/blob/main/workers/collab/src/protocol.ts) enforce frame structure
- **Sanitization**: All incoming data passes through validation functions (colors must match regex, coordinates must be finite numbers)
- **Size enforcement**: Hard caps prevent storage and memory exhaustion
- **Revision monotonicity**: Prevents rollback attacks or stale overwrites

A compromised client cannot corrupt session state beyond what permissions allow.

## Summary

- GeoLibre's **real-time collaboration synchronization** uses Cloudflare Durable Objects as a WebSocket relay, not a game server—clients maintain authoritative state
- **Snapshots** carry full project state with monotonic revisions; **presence** is ephemeral memory-only
- The **join handshake** ensures late joiners receive current state immediately
- **Host tokens** enable centralized permission control without complex ACL systems
- **Rate limits and size bounds** protect against resource exhaustion
- The codebase separates protocol ([`protocol.ts`](https://github.com/opengeos/GeoLibre/blob/main/protocol.ts)), transport ([`collab-client.ts`](https://github.com/opengeos/GeoLibre/blob/main/collab-client.ts)), and UI ([`useCollaboration.ts`](https://github.com/opengeos/GeoLibre/blob/main/useCollaboration.ts)) concerns cleanly

## Frequently Asked Questions

### What happens when two users edit simultaneously in GeoLibre?

The server enforces **monotonic revision numbers**. The second snapshot to arrive with a stale revision is rejected, and the client receives the newer snapshot from the server. The client must then merge or rebase its local changes. This is a last-write-wins strategy with explicit conflict detection, not operational transforms.

### Can the collaboration server scale horizontally?

No—by design. Each **CollabSession** Durable Object is single-tenant and single-threaded, ensuring consistent state without distributed consensus. Cloudflare's infrastructure migrates Durable Objects to handle load, but a single session runs on one instance. This trades horizontal scaling per-session for simplicity and correctness.

### Does GeoLibre support peer-to-peer collaboration without Cloudflare?

The current implementation requires Cloudflare Durable Objects for the relay and persistent storage. The protocol is generic enough that a self-hosted WebSocket server could implement the same interface, but no such adapter exists in the repository as of the analyzed version.

### How large can collaborative projects be?

Snapshots are limited to approximately **1 MiB** uncompressed. This accommodates complex GeoJSON projects with thousands of features while protecting storage costs and transmission latency. The server rejects oversized snapshots with an explicit error.