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.tsKey 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.error—TLRemoteSyncErrorwith 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 diffTombstones (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-cloudflareandtemplates/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.tsandServerSocketAdapter.ts. - Persistence changes plug into
TLSyncStorage.
Related
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.