A language-agnostic protocol for building offline-first applications
SyncStore is an open protocol specification for client-first, offline-capable datastores. It provides a standardized way to synchronize collected key-value pairs holding JSON data between clients and servers, with optional graph-like relations between records.
Build applications that work seamlessly without connectivity. Changes sync automatically when back online.
Implement in any language or framework. The protocol speaks JSON, MsgPack, or Protobuf over HTTP.
The inbox/outbox pattern with explicit outcomes (accepted/rejected) gives you full control over conflict resolution.
Collections, indexes, and optional graph relations let you model complex data without sacrificing sync simplicity.
Outbox — Clients queue local changes (set, patch, delete) with a unique reference ID.
Sync —
Changes are pushed to the server via
POST /sync/outbox.
Inbox —
Clients pull confirmed changes via
GET /sync/inbox, learning what was accepted or rejected.
Checkpoint — Periodic snapshots allow clients to bootstrap without replaying full history.
A collection is a named container for key-value pairs. Keys are strings and are automatically sorted in lexicographical order. Each key maps to a JSON value. Collections provide the primary way to organize and query data in SyncStore.
Indexes allow you to filter and re-order a subset of items in a collection using arbitrary string values as ordering keys. An item can belong to multiple indexes, each with its own ordering key. When querying with an index, only items in that index are returned, sorted lexicographically by their ordering key.
Relations create graph-like connections between records across collections using a triple-based model: subject → predicate → object. The subject is the item the relation belongs to, the predicate (type) describes the relationship, and the object references another item in any collection.
| Operation | Description |
|---|---|
| set | Replace the entire value at a key in a collection |
| patch | Apply a JSON Patch (RFC 6902) to an existing value |
| delete | Remove a key from a collection |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/:db/sync/inbox | Pull confirmed changes from server |
| POST | /v1/:db/sync/outbox | Push local changes to server |
|
||
| GET | /v1/:db/sync/checkpoint | Retrieve a snapshot for bootstrapping |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/:db/data | List all collections |
| GET | /v1/:db/data/:collection | List items in a collection (with optional index query) |
| GET | /v1/:db/data/:collection/:key | Read a single item |
| PUT | /v1/:db/data/:collection/:key | Create or replace an item |
|
||
| PATCH | /v1/:db/data/:collection/:key | Apply JSON Patch (RFC 6902) to an item |
|
||
| DELETE | /v1/:db/data/:collection/:key | Remove an item |
Indexes allow you to assign an arbitrary ordering key to items in a collection. When querying with an index, results are sorted lexicographically by this key, enabling filtering and custom ordering. Only items with an index entry are returned.
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/:db/data/:collection/:key/indexes | List all index entries for an item |
| PUT | /v1/:db/data/:collection/:key/indexes/:name | Set an item's entry in an index |
Query with: |
||
| DELETE | /v1/:db/data/:collection/:key/indexes/:name | Remove an item from an index |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/:db/data/:collection/:key/relations | List all relations for an item |
| PUT | /v1/:db/data/:collection/:key/relations/:id | Create or update a relation |
|
||
| DELETE | /v1/:db/data/:collection/:key/relations/:id | Remove a relation |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/:db/batch | Execute multiple data operations atomically |
|
||
SyncStore is transport-format agnostic. Any format that can be encoded to and decoded from JSON without loss is valid. We recommend implementors support JSON and MessagePack at minimum.
| Content-Type | Status | Description |
|---|---|---|
| application/json | Recommended | Standard JSON encoding, universally supported |
| application/msgpack | Recommended | Binary format, more compact and faster to parse |
| application/protobuf+json | Optional | Protocol Buffers with JSON mapping |
Clients specify their preferred format via the
Content-Type and
Accept headers.
These limits are recommendations to keep the protocol lightweight and performant. Implementors may choose to adjust them based on their use case.
| Resource | Limit |
|---|---|
| Item keys | 128 bytes |
| Index names | 128 bytes |
| Index ordering keys | 128 bytes |
| Relation IDs | 128 bytes |
| Item values | 1 MB (uncompressed JSON) |