Sync LiveStore via Cloudflare Durable Objects using @livestore/sync-cf.
The @livestore/sync-cf package provides a sync provider backed by Cloudflare Durable Objects. Each store gets its own Durable Object instance, which manages push/pull operations and persists events in DO SQLite (or optionally Cloudflare D1). Three transport modes are available: WebSocket, HTTP, and Durable Object RPC.
Client (LiveStore) │ WebSocket / HTTP push & pull ▼ Cloudflare Worker (routes by storeId) │ ▼ Durable Object (per storeId) │ Read/Write ▼ DO SQLite (default) or D1
Worker — routes sync requests to the correct Durable Object by storeId and handles auth validation.Durable Object — manages sync state, handles push/pull, maintains WebSocket connections.Storage — events are persisted in DO SQLite by default, or in a Cloudflare D1 database.
When a sync backend is reset (for example, by deleting .wrangler/state), clients with cached local data detect the mismatch via a backendId comparison. Configure the behavior with onBackendIdMismatch:
When using D1, the local database is stored at .wrangler/state/d1/miniflare-D1DatabaseObject/<id>.sqlite.
Delete .wrangler/state to reset the local sync backend. If your client has cached data, set onBackendIdMismatch: 'reset' to let it recover automatically.
Events are stored in the Durable Object’s own SQLite database. Table names follow the pattern eventlog_{VERSION}_{storeId}.Pros: easiest to deploy (no D1 provisioning), data co-located with the DO, lowest latency.Cons: not directly inspectable outside the DO; operational tooling must go through the DO.
D1 (optional)
Pass storage: { _tag: 'd1', binding: 'DB' } to makeDurableObject and add a [[d1_databases]] binding to wrangler.toml.Pros: inspectable via D1 tooling; enables cross-store analytics.Cons: extra network hop; requires D1 provisioning.
LiveStore identifies sync requests by search parameters alone — the request path does not matter. Use matchSyncRequest to detect sync traffic anywhere in your worker:
import type { CfTypes } from '@livestore/sync-cf/cf-worker'import { matchSyncRequest } from '@livestore/sync-cf/cf-worker'declare const request: CfTypes.Requestconst searchParams = matchSyncRequest(request)if (searchParams !== undefined) { const { storeId, payload, transport } = searchParams console.log(`Sync request for store ${storeId} via ${transport}`)}
Parameter
Type
Required
Description
storeId
string
Yes
Target LiveStore identifier
transport
'ws' | 'http'
Yes
Transport protocol selector
payload
JSON (URI-encoded)
No
Arbitrary JSON for auth/tenant routing; validated in validatePayload
WebSocket: https://sync.example.com?storeId=abc&transport=ws (must include Upgrade: websocket)