LiveStore event synchronization WebSocket API for cross-device data sync.
The LiveStore sync WebSocket enables real-time event synchronization between a user's devices using an event-sourced architecture. This channel operates as a peer-to-peer relay where the server routes messages between devices without persisting events.
Device A ──┐ ┌── Device B
│ ┌──────────┐ │
├──>│ Server │<────┤
│ │ (Relay) │ │
│ └──────────┘ │
Device C ──┘ └── Device D
/api/v1/livestore/syncauth_token cookie or ?token= query parameterSame-origin (cookie-based):
const ws = new WebSocket('wss://api.example.com/api/v1/livestore/sync');
Cross-origin (token parameter):
const ws = new WebSocket(`wss://api.example.com/api/v1/livestore/sync?token=${jwtToken}`);
Device A Server Device B
│ │ │
│──── push ─────────>│ │
│ │──── push ─────────>│
│<─── ack ───────────│ │
│ │ │
New Device Server Existing Device
│ │ │
│── peer_pull ──────>│ │
│ │── peer_pull ──────>│
│ │<─ peer_pull_resp ──│
│<─ peer_pull_resp ──│ │
│ │ │
This implementation follows the @livestore/adapter-web protocol specification with custom extensions for peer-to-peer history sync.
Each client must identify itself on first message:
clientId: Unique device identifier (persisted per device)sessionId: Current session identifier (changes on reconnect)Production LiveStore sync server
JWT token stored in HTTP-only cookie named auth_token.
For cross-origin WebSocket connections, the token can also be
passed as a ?token= query parameter.
Development LiveStore sync server
JWT token stored in HTTP-only cookie named auth_token.
For cross-origin WebSocket connections, the token can also be
passed as a ?token= query parameter.
Bidirectional WebSocket channel for LiveStore event synchronization.
This channel supports both sending and receiving messages for full peer-to-peer event synchronization.
Send sync messages to the server for relay
Send events or sync requests to the server.
| Type | Direction | Description |
|---|---|---|
push |
Client -> Server -> Peers | Push new events to other devices |
pull |
Client -> Server | Request events from server (returns empty + peer list) |
peer_pull |
Client -> Server -> Peers | Request history from online peers |
peer_pull_response |
Client -> Server -> Requester | Respond to peer pull with history |
ws.send(JSON.stringify({
type: 'push',
clientId: 'device-abc-123',
sessionId: 'session-xyz',
timestamp: Date.now(),
payload: {
events: [{
eventName: 'message.created',
eventNumber: 42,
clientId: 'device-abc-123',
sessionId: 'session-xyz',
data: { conversationId: '...', text: 'Hello!' },
timestamp: Date.now()
}]
}
}));
Available only on servers:
Accepts one of the following messages:
Push new events to other devices via server relay
Send new events from this device to be relayed to all other online devices of the same user. The server does not persist events.
Push a new message event
{
"type": "push",
"clientId": "device-abc-123",
"sessionId": "session-xyz-456",
"timestamp": 1733644800000,
"payload": {
"events": [
{
"eventName": "message.created",
"eventNumber": 42,
"clientId": "device-abc-123",
"sessionId": "session-xyz-456",
"data": {
"conversationId": "conv-123",
"text": "Hello world!"
},
"timestamp": 1733644800000
}
]
}
}
Request events from server
Request events since a timestamp. Since the server doesn't persist events, this returns an empty list along with a list of online peers that can provide history.
Request events since a timestamp
{
"type": "pull",
"clientId": "device-abc-123",
"sessionId": "session-xyz-456",
"timestamp": 1733644800000,
"payload": {
"sinceTimestamp": 1733640000000
}
}
Request history from online peer devices
Request event history directly from other online devices of the same user. The server routes this request to all online peers.
This enables new devices to sync historical data from existing devices without requiring server-side event storage.
Request history from peers
{
"type": "peer_pull",
"clientId": "device-new-123",
"sessionId": "session-xyz-456",
"timestamp": 1733644800000,
"payload": {
"requestId": "req-abc-123",
"sinceTimestamp": 1733640000000,
"retentionDays": 30
}
}
History response from a peer device
Response to a peer pull request containing historical events from another device. The server routes this back to the requesting device.
History response with events
{
"type": "peer_pull_response",
"clientId": "device-def-789",
"sessionId": "session-abc-123",
"timestamp": 1733644800000,
"payload": {
"requestId": "req-abc-123",
"events": [
{
"eventName": "message.created",
"eventNumber": 40,
"clientId": "device-def-789",
"sessionId": "session-old-456",
"data": {
"conversationId": "conv-123",
"text": "Earlier message"
},
"timestamp": 1733641000000
}
],
"hasMore": false
}
}
Bidirectional WebSocket channel for LiveStore event synchronization.
This channel supports both sending and receiving messages for full peer-to-peer event synchronization.
Receive sync messages from server
Receive events and responses relayed from other devices.
| Type | Source | Description |
|---|---|---|
push |
Peer device | Events pushed by another device |
pull_response |
Server | Response to pull request (includes online peer list) |
ack |
Server | Acknowledgment of received push |
peer_pull |
Peer device | History request from another device |
peer_pull_response |
Peer device | History response from peer |
error |
Server | Error response |
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'push':
// Apply events from peer device
applyEvents(msg.payload.events);
break;
case 'ack':
// Push was acknowledged
console.log(`${msg.payload.receivedCount} events received`);
break;
case 'pull_response':
// Server has no events but provides peer list
if (msg.onlinePeers?.length > 0) {
requestHistoryFromPeer(msg.onlinePeers[0]);
}
break;
case 'peer_pull':
// Another device wants history
respondWithHistory(msg.payload.requestId, msg.payload.sinceTimestamp);
break;
case 'peer_pull_response':
// Received history from peer
applyEvents(msg.payload.events);
break;
case 'error':
console.error(`Sync error: ${msg.code} - ${msg.message}`);
break;
}
};
Available only on servers:
Accepts one of the following messages:
Push new events to other devices via server relay
Send new events from this device to be relayed to all other online devices of the same user. The server does not persist events.
Push a new message event
{
"type": "push",
"clientId": "device-abc-123",
"sessionId": "session-xyz-456",
"timestamp": 1733644800000,
"payload": {
"events": [
{
"eventName": "message.created",
"eventNumber": 42,
"clientId": "device-abc-123",
"sessionId": "session-xyz-456",
"data": {
"conversationId": "conv-123",
"text": "Hello world!"
},
"timestamp": 1733644800000
}
]
}
}
Response to pull request with events and peer list
Server response to a pull request. Since the server doesn't persist
events, this typically contains an empty event list but includes
a list of online peers that can provide history via peer_pull.
Pull response with online peers
{
"type": "pull_response",
"clientId": "server",
"sessionId": "",
"timestamp": 1733644800000,
"payload": {
"events": [],
"fromClientId": "server",
"hasMore": false
},
"onlinePeers": [
"device-def-789",
"device-ghi-012"
]
}
Server acknowledgment of received push
Sent by the server to acknowledge receipt of a push message. Confirms how many events were received and relayed.
Acknowledgment of 3 events
{
"type": "ack",
"clientId": "server",
"sessionId": "",
"timestamp": 0,
"payload": {
"receivedCount": 3,
"lastTimestamp": 0
}
}
Request history from online peer devices
Request event history directly from other online devices of the same user. The server routes this request to all online peers.
This enables new devices to sync historical data from existing devices without requiring server-side event storage.
Request history from peers
{
"type": "peer_pull",
"clientId": "device-new-123",
"sessionId": "session-xyz-456",
"timestamp": 1733644800000,
"payload": {
"requestId": "req-abc-123",
"sinceTimestamp": 1733640000000,
"retentionDays": 30
}
}
History response from a peer device
Response to a peer pull request containing historical events from another device. The server routes this back to the requesting device.
History response with events
{
"type": "peer_pull_response",
"clientId": "device-def-789",
"sessionId": "session-abc-123",
"timestamp": 1733644800000,
"payload": {
"requestId": "req-abc-123",
"events": [
{
"eventName": "message.created",
"eventNumber": 40,
"clientId": "device-def-789",
"sessionId": "session-old-456",
"data": {
"conversationId": "conv-123",
"text": "Earlier message"
},
"timestamp": 1733641000000
}
],
"hasMore": false
}
}
Error response from server
Sent when the server encounters an error processing a message.
Error message from server
Invalid message error
{
"type": "error",
"code": "invalid_message",
"message": "Failed to parse message"
}
Push new events to other devices via server relay
Send new events from this device to be relayed to all other online devices of the same user. The server does not persist events.
Request events from server
Request events since a timestamp. Since the server doesn't persist events, this returns an empty list along with a list of online peers that can provide history.
Response to pull request with events and peer list
Server response to a pull request. Since the server doesn't persist
events, this typically contains an empty event list but includes
a list of online peers that can provide history via peer_pull.
Server acknowledgment of received push
Sent by the server to acknowledge receipt of a push message. Confirms how many events were received and relayed.
Request history from online peer devices
Request event history directly from other online devices of the same user. The server routes this request to all online peers.
This enables new devices to sync historical data from existing devices without requiring server-side event storage.
History response from a peer device
Response to a peer pull request containing historical events from another device. The server routes this back to the requesting device.
Error response from server
Sent when the server encounters an error processing a message.
Error message from server
The type of LiveStore sync message
Base structure for all LiveStore messages
A single event in the LiveStore eventlog
Payload for push messages
Payload for pull requests
Payload for pull response messages
Payload for acknowledgment messages
Error encountered during sync
Payload for peer-to-peer pull requests
Payload for peer-to-peer pull response
Error message from server