Open-Source Wikis

/

tldraw

/

Packages

/

@tldraw/sync-core

tldraw/tldraw

@tldraw/sync-core

Active contributors: David Sheldrick, Mitja Bezenšek, Steve Ruiz, alex

Purpose

@tldraw/sync-core is the protocol-level multiplayer engine. It runs on both the client (paired with @tldraw/sync) and the server (paired with a Cloudflare Durable Object or Node host). It defines the wire format, the room machinery, and the persistence interface. It does not depend on React.

Directory layout

packages/sync-core/
├── package.json
└── src/
    ├── index.ts
    └── lib/
        ├── protocol.ts                          # wire format
        ├── server-types.ts
        ├── TLSyncRoom.ts                        # canonical room state (40 KB / 1.3k lines)
        ├── TLSyncClient.ts                      # client-side room (32 KB / 1k lines)
        ├── TLSocketRoom.ts                      # server-side socket room (33 KB)
        ├── ClientWebSocketAdapter.ts            # client transport (23 KB)
        ├── ServerSocketAdapter.ts               # server transport
        ├── RoomSession.ts                       # one client's session in a room
        ├── DurableObjectSqliteSyncWrapper.ts    # CF DO + sqlite persistence wrapper
        ├── NodeSqliteSyncWrapper.ts             # Node + sqlite wrapper
        ├── NodeSqliteWrapper.ts
        ├── InMemorySyncStorage.ts               # in-memory persistence (tests)
        ├── SQLiteSyncStorage.ts                 # CF DO sqlite storage (20 KB)
        ├── TLSyncStorage.ts                     # storage interface
        ├── TLRemoteSyncError.ts
        ├── MicrotaskNotifier.ts
        ├── chunk.ts
        ├── diff.ts
        ├── recordDiff.ts
        ├── interval.ts
        ├── findMin.ts
        └── computeTombstonePruning.test.ts

Key abstractions

Type File Purpose
TLSyncRoom packages/sync-core/src/lib/TLSyncRoom.ts The server-side authoritative state of a room. Holds the canonical store, applies inbound diffs, broadcasts outbound diffs, prunes tombstones.
TLSyncClient packages/sync-core/src/lib/TLSyncClient.ts The client-side counterpart. Connects to a room over a socket adapter, applies remote diffs to a local store, sends local diffs to the server.
TLSocketRoom packages/sync-core/src/lib/TLSocketRoom.ts Server-side wrapper that owns a TLSyncRoom, manages WebSocket connections, and persists snapshots.
ClientWebSocketAdapter packages/sync-core/src/lib/ClientWebSocketAdapter.ts Client transport with reconnection backoff and message chunking.
ServerSocketAdapter packages/sync-core/src/lib/ServerSocketAdapter.ts Server transport.
RoomSession packages/sync-core/src/lib/RoomSession.ts One connected client's session inside a TLSyncRoom.
TLSyncStorage packages/sync-core/src/lib/TLSyncStorage.ts Abstract persistence interface. Implementations: InMemorySyncStorage, SQLiteSyncStorage.
RoomSnapshot (in TLSocketRoom.ts) Serialized snapshot persisted between sessions.

Protocol

packages/sync-core/src/lib/protocol.ts defines the message types exchanged over the WebSocket:

  • connect / connect_response — handshake with version, last-known clock, and snapshot exchange.
  • push — client → server batch of record diffs and presence updates.
  • data — server → client broadcast of accepted diffs.
  • pong — heartbeat.
  • errorTLRemoteSyncError with a code (incompatibility, schema mismatch, etc.).

Messages are chunked (chunk.ts) when they exceed the WebSocket frame size budget.

How it works

sequenceDiagram
    participant ClientStore as Client store
    participant TLSyncClient
    participant Adapter as Client/Server WS
    participant TLSocketRoom
    participant TLSyncRoom
    participant Storage

    ClientStore->>TLSyncClient: local diff
    TLSyncClient->>Adapter: push
    Adapter->>TLSocketRoom: push
    TLSocketRoom->>TLSyncRoom: applyPush
    TLSyncRoom->>Storage: persist
    TLSyncRoom-->>TLSocketRoom: accepted diff
    TLSocketRoom-->>Adapter: broadcast data
    Adapter-->>TLSyncClient: data
    TLSyncClient->>ClientStore: apply diff

Tombstones (deleted record markers) are kept until a quiescence interval has passed across all sessions; pruning logic lives in computeTombstonePruning.test.ts and the implementation in TLSyncRoom.ts.

Persistence

The default Cloudflare host stores room snapshots in R2. The CF Durable Object also has SQLite via DurableObjectSqliteSyncWrapper. The Node wrapper (NodeSqliteSyncWrapper) backs local development and tests. The InMemorySyncStorage is for unit tests.

Integration points

  • Imports. @tldraw/store, @tldraw/tlschema, @tldraw/utils.
  • Importers. @tldraw/sync (React hooks), apps/dotcom/sync-worker (production server), apps/bemo-worker (demo server), templates/sync-cloudflare and templates/simple-server-example/socketio-server-example.

Entry points for modification

  • Wire-format changes go through protocol.ts — bump the protocol version and add a migration path if necessary.
  • Room-level invariants (e.g., conflict resolution) live in TLSyncRoom.ts.
  • Transport changes live in ClientWebSocketAdapter.ts and ServerSocketAdapter.ts.
  • Persistence changes plug into TLSyncStorage.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

@tldraw/sync-core – tldraw wiki | Factory