[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › local‑first/Protocol

Evolu Protocol

Evolu Protocol is a local-first, end-to-end encrypted binary synchronization
protocol optimized for minimal size and maximum speed. It enables data sync
between a client and a relay. In the future, direct peer-to-peer (P2P) sync
between clients will be possible without a relay.

Relays don't need to sync with each other—clients using those relays will
sync them eventually. If a relay is offline (e.g., for maintenance), it will
sync automatically later via client sync logic. For relay backup using
SQLite, see https://sqlite.org/rsync.html (uses a similar algorithm to Evolu
RBSR).

Evolu Protocol is designed for SQLite but can be extended to any database. It
implements [Range-Based Set
Reconciliation](https://arxiv.org/abs/2212.13567). To learn how RBSR works,
check [Negentropy](https://logperiodic.com/rbsr.html). Evolu Protocol is
similar to Negentropy but uses different encoding and also provides data
transfer, ownership, real-time broadcasting, request-response semantics, and
error handling.

## Message structure

| Field                                                                                                        | Notes                                                                                                |
| :----------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------- |
| **Header**                                                                                                   |                                                                                                      |
| - [protocolVersion](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/protocolVersion)           |                                                                                                      |
| - [OwnerId](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerId)                              | [Owner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/Owner)                           |
| - messageType                                                                                                | [MessageType](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/MessageType)             |
| **Request (messageType=0)**                                                                                  |                                                                                                      |
| - hasWriteKey                                                                                                | 0 = no, 1 = yes                                                                                      |
| - [OwnerWriteKey](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerWriteKey)                  | If hasWriteKey = 1                                                                                   |
| - subscriptionFlag                                                                                           | [SubscriptionFlags](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/SubscriptionFlags) |
| **Response (messageType=1)**                                                                                 |                                                                                                      |
| - [ProtocolErrorCode](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolErrorCode)       |                                                                                                      |
| **Broadcast (messageType=2)**                                                                                |                                                                                                      |
| - (no additional fields)                                                                                     |                                                                                                      |
| **Messages**                                                                                                 |                                                                                                      |
| - [NonNegativeInt](https://evolu.dev/docs/api-reference/common/Type/variables/NonNegativeInt)                             | A number of messages.                                                                                |
| - [EncryptedCrdtMessage](https://evolu.dev/docs/api-reference/common/local-first/Storage/interfaces/EncryptedCrdtMessage) |                                                                                                      |
| **Ranges**                                                                                                   |                                                                                                      |
| - [NonNegativeInt](https://evolu.dev/docs/api-reference/common/Type/variables/NonNegativeInt)                             | Number of ranges.                                                                                    |
| - [Range](https://evolu.dev/docs/api-reference/common/local-first/Storage/type-aliases/Range)                             |                                                                                                      |

## WriteKey validation

The initiator sends a hasWriteKey flag and optionally a WriteKey. The
WriteKey is required when sending messages as a secure token proving the
initiator can write changes. It's ok to not send a WriteKey if the initiator
is only syncing (read-only) and not sending messages. The non-initiator
validates the WriteKey immediately after parsing the initiator header, before
processing any messages or ranges.

## Synchronization

- **Messages**: Sends [EncryptedCrdtMessage](https://evolu.dev/docs/api-reference/common/local-first/Storage/interfaces/EncryptedCrdtMessage)s in either direction.
- **Ranges**: Determines messages to sync. Usage varies by transport—e.g., sent
  only on WebSocket connection open or with every fetch request.

Synchronization involves an initiator and a non-initiator. The **initiator**
is typically a client, and the **non-initiator** is typically a relay. Each
side processes the received message and responds with a new `ProtocolMessage`
if further sync is needed or possible, continuing until both sides are
synchronized.

The **non-initiator always responds** to provide sync completion feedback,
even with empty messages containing only the header and no error. This allows
the initiator to detect when synchronization is complete.

Both **Messages** and **Ranges** are optional, allowing each side to send,
sync, or only subscribe data as needed.

When the initiator sends data, the [OwnerWriteKey](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerWriteKey) is required as a
secure token proving the initiator can write changes. The non-initiator
responds without a [OwnerWriteKey](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerWriteKey), since the initiator’s request
already signals it wants data. If the non-initiator detects an issue, it
sends an error code via the `Error` field in the header back to the
initiator. In relay-to-relay or P2P sync, both sides may require the
[OwnerWriteKey](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerWriteKey) depending on who is the initiator.

## Protocol errors

The protocol uses error codes in the header to signal issues:

- [ProtocolWriteKeyError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteKeyError): The provided WriteKey is invalid or missing.
- [ProtocolWriteError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteError): A serious relay-side write failure occurred.
- [ProtocolQuotaError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolQuotaError): Storage or billing quota exceeded.
- [ProtocolSyncError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolSyncError): A serious relay-side synchronization failure
  occurred.
- [ProtocolVersionError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolVersionError): Protocol version mismatch.
- [ProtocolInvalidDataError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolInvalidDataError): The message is malformed or corrupted.

All protocol errors except `ProtocolInvalidDataError` include the `OwnerId`
to allow clients to associate errors with the correct owner.

## Message size limit

The protocol enforces a strict maximum size for all messages, defined by
[ProtocolMessageMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolMessageMaxSize). This ensures every [ProtocolMessage](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessage) is
less than or equal to this limit, enabling stateless transports, simplified
relay implementation, and predictable memory usage. When all messages don't
fit within the limit, the protocol automatically continues synchronization in
subsequent rounds using range-based reconciliation.

Database mutations are limited to 640KB, which is smaller than the protocol
message limit to ensure efficient sync with
[defaultProtocolMessageRangesMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/defaultProtocolMessageRangesMaxSize).

## Why Binary?

The protocol avoids JSON because:

- Encrypted data doesn’t compress well, unlike plain JSON.
- Message size must be controlled during creation.
- Sequential byte reading is faster than parsing and avoids conversions.

It uses structure-aware encoding, significantly outperforming generic binary
serialization formats with the following optimizations:

- **NonNegativeInt:** Up to 33% smaller than MessagePack.
- **DateIso:** Up to 75% smaller.
- **Timestamp Encoding:** Delta encoding for milliseconds and run-length
  encoding (RLE) for counters and NodeIds.
- **Small Integers (0 to 19):** Reduces size by 1 byte per integer.

To avoid reinventing serialization where it’s unnecessary—like for JSON and
certain numbers—the Evolu Protocol relies on MessagePack.

## Versioning

Evolu Protocol uses explicit versioning to ensure compatibility between
clients and relays (or peers). Each protocol message begins with a version
number and an `ownerId` in its header.

**How version negotiation works:**

- The initiator (usually a client) sends a `ProtocolMessage` that includes its
  protocol version and the `ownerId`.
- The non-initiator (usually a relay or peer) checks the version.
  - If the versions match, synchronization proceeds as normal.
  - If the versions do not match, the non-initiator responds with a message
    containing **its own protocol version and the same `ownerId`**.

- The initiator can then detect the version mismatch for that specific owner
  and handle it appropriately (e.g., prompt for an update or halt sync).

Version negotiation is per-owner, allowing Evolu Protocol to evolve safely
over time and provide clear feedback about version mismatches.

## Credible exit

The protocol specification is intentionally non-configurable to ensure
universal compatibility. This design allows applications (users) to switch
between any compliant relay without negotiation or compatibility checks
beyond version matching. Relays are generic infrastructure that any
application can use interchangeably making exit from any single provider
technically feasible and economically viable.

## Functions

| Function                                                                                                                                     | Description                                                                                                                                                                                                                                                                                               |
| -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [applyProtocolMessageAsClient](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/applyProtocolMessageAsClient)                   | -                                                                                                                                                                                                                                                                                                         |
| [applyProtocolMessageAsRelay](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/applyProtocolMessageAsRelay)                     | -                                                                                                                                                                                                                                                                                                         |
| [createProtocolMessageBuffer](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/createProtocolMessageBuffer)                     | -                                                                                                                                                                                                                                                                                                         |
| [createProtocolMessageForSync](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/createProtocolMessageForSync)                   | Creates a [ProtocolMessage](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessage) for sync.                                                                                                                                                                                   |
| [createProtocolMessageForUnsubscribe](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/createProtocolMessageForUnsubscribe)     | -                                                                                                                                                                                                                                                                                                         |
| [createProtocolMessageFromCrdtMessages](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/createProtocolMessageFromCrdtMessages) | Creates a [ProtocolMessage](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessage) from CRDT messages.                                                                                                                                                                         |
| [createTimestampsBuffer](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/createTimestampsBuffer)                               | -                                                                                                                                                                                                                                                                                                         |
| [decodeFlags](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeFlags)                                                     | Decodes a byte into an array of boolean flags.                                                                                                                                                                                                                                                            |
| [decodeNodeId](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeNodeId)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [decodeNonNegativeInt](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeNonNegativeInt)                                   | Decodes a non-negative integer from a variable-length integer format.                                                                                                                                                                                                                                     |
| [decodeNumber](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeNumber)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [decodeProtocolMessageToJson](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeProtocolMessageToJson)                     | Decodes a ProtocolMessage into a readable JSON object for debugging.                                                                                                                                                                                                                                      |
| [decodeRle](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeRle)                                                         | -                                                                                                                                                                                                                                                                                                         |
| [decodeSqliteValue](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeSqliteValue)                                         | -                                                                                                                                                                                                                                                                                                         |
| [decodeString](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decodeString)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [decryptAndDecodeDbChange](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/decryptAndDecodeDbChange)                           | Decrypts and decodes an [EncryptedCrdtMessage](https://evolu.dev/docs/api-reference/common/local-first/Storage/interfaces/EncryptedCrdtMessage) using the provided owner's encryption key. Verifies that the embedded timestamp matches the expected timestamp to ensure message integrity.                            |
| [encodeAndEncryptDbChange](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeAndEncryptDbChange)                           | Encodes and encrypts a [DbChange](https://evolu.dev/docs/api-reference/common/local-first/Storage/variables/DbChange) using the provided owner's encryption key. Returns an encrypted binary representation as [EncryptedDbChange](https://evolu.dev/docs/api-reference/common/local-first/Storage/type-aliases/EncryptedDbChange). |
| [encodeFlags](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeFlags)                                                     | Encodes an array of boolean flags into a single byte.                                                                                                                                                                                                                                                     |
| [encodeLength](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeLength)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [encodeNodeId](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeNodeId)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [encodeNonNegativeInt](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeNonNegativeInt)                                   | Encodes a non-negative integer into a variable-length integer format. It's more efficient than encoding via [encodeNumber](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeNumber).                                                                                                   |
| [encodeNumber](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeNumber)                                                   | Evolu uses MessagePack to handle all number variants except for NonNegativeInt. For NonNegativeInt, Evolu provides more efficient encoding.                                                                                                                                                               |
| [encodeSqliteValue](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeSqliteValue)                                         | -                                                                                                                                                                                                                                                                                                         |
| [encodeString](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/encodeString)                                                   | -                                                                                                                                                                                                                                                                                                         |
| [parseProtocolHeader](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/parseProtocolHeader)                                     | Parses the protocol header needed for routing.                                                                                                                                                                                                                                                            |

## Interfaces

| Interface                                                                                                                                       | Description                                                                                                                                                                                                                |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [ApplyProtocolMessageAsClientBroadcast](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientBroadcast)   | Base interface for objects with a discriminant `type` property.                                                                                                                                                            |
| [ApplyProtocolMessageAsClientNoResponse](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientNoResponse) | Base interface for objects with a discriminant `type` property.                                                                                                                                                            |
| [ApplyProtocolMessageAsClientOptions](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientOptions)       | -                                                                                                                                                                                                                          |
| [ApplyProtocolMessageAsClientResponse](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsClientResponse)     | Result type for [applyProtocolMessageAsClient](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/applyProtocolMessageAsClient) that distinguishes between responses to client requests and broadcast messages. |
| [ApplyProtocolMessageAsRelayOptions](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsRelayOptions)         | -                                                                                                                                                                                                                          |
| [ApplyProtocolMessageAsRelayResult](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ApplyProtocolMessageAsRelayResult)           | Result type for [applyProtocolMessageAsRelay](https://evolu.dev/docs/api-reference/common/local-first/Protocol/functions/applyProtocolMessageAsRelay).                                                                                  |
| [ProtocolHeader](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolHeader)                                                 | Parsed protocol header for supported protocol messages.                                                                                                                                                                    |
| [ProtocolInvalidDataError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolInvalidDataError)                             | Error for invalid or corrupted protocol message data.                                                                                                                                                                      |
| [ProtocolMessageBuffer](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolMessageBuffer)                                   | Mutable builder for constructing [ProtocolMessage](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessage) respecting size limits.                                                               |
| [ProtocolQuotaError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolQuotaError)                                         | Error when storage or billing quota is exceeded.                                                                                                                                                                           |
| [ProtocolSyncError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolSyncError)                                           | Error indicating a serious relay-side synchronization failure. Clients should log this error and show a generic sync error to the user.                                                                                    |
| [ProtocolTimestampMismatchError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolTimestampMismatchError)                 | Error when embedded timestamp doesn't match expected timestamp in EncryptedDbChange. Indicates potential tampering or corruption of CRDT messages.                                                                         |
| [ProtocolVersionError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolVersionError)                                     | Represents a version mismatch in the Evolu Protocol. Occurs when the initiator and non-initiator are using incompatible protocol versions.                                                                                 |
| [ProtocolWriteError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteError)                                         | Error indicating a serious relay-side write failure. Clients should log this error and show a generic sync error to the user.                                                                                              |
| [ProtocolWriteKeyError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolWriteKeyError)                                   | Error when a [OwnerWriteKey](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerWriteKey) is invalid, missing, or fails validation.                                                                           |
| [TimestampsBuffer](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/TimestampsBuffer)                                             | -                                                                                                                                                                                                                          |
| [TimestampsRangeWithTimestampsBuffer](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/TimestampsRangeWithTimestampsBuffer)       | -                                                                                                                                                                                                                          |

## Type Aliases

| Type Alias                                                                                                                                | Description                           |
| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| [ApplyProtocolMessageAsClientResult](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ApplyProtocolMessageAsClientResult) | -                                     |
| [MessageType](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/MessageType)                                               | -                                     |
| [ProtocolError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolError)                                           | -                                     |
| [ProtocolMessage](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessage)                                       | Evolu Protocol Message.               |
| [ProtocolMessageMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessageMaxSize)                         | Protocol message maximum size.        |
| [ProtocolMessageRangesMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/ProtocolMessageRangesMaxSize)             | Protocol message ranges maximum size. |
| [SubscriptionFlag](https://evolu.dev/docs/api-reference/common/local-first/Protocol/type-aliases/SubscriptionFlag)                                     | -                                     |

## Variables

| Variable                                                                                                                                 | Description                                                                                                                                |
| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| [decodeLength](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/decodeLength)                                               | -                                                                                                                                          |
| [defaultProtocolMessageMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/defaultProtocolMessageMaxSize)             | Default [ProtocolMessageMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolMessageMaxSize) (1MB).              |
| [defaultProtocolMessageRangesMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/defaultProtocolMessageRangesMaxSize) | Default [ProtocolMessageRangesMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolMessageRangesMaxSize) (30KB). |
| [MessageType](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/MessageType)                                                 | -                                                                                                                                          |
| [ProtocolErrorCode](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolErrorCode)                                     | -                                                                                                                                          |
| [ProtocolMessageMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolMessageMaxSize)                           | Protocol message maximum size.                                                                                                             |
| [ProtocolMessageRangesMaxSize](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolMessageRangesMaxSize)               | Protocol message ranges maximum size.                                                                                                      |
| [ProtocolValueType](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/ProtocolValueType)                                     | -                                                                                                                                          |
| [protocolVersion](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/protocolVersion)                                         | Evolu Protocol version.                                                                                                                    |
| [SubscriptionFlags](https://evolu.dev/docs/api-reference/common/local-first/Protocol/variables/SubscriptionFlags)                                     | -                                                                                                                                          |