Skip to main content
Byzantine Fault Tolerant (BFT) op signing lets clients cryptographically prove that every CRDT op they send actually originated from them. The server verifies each op against the client’s Ed25519 public key embedded in the token — a compromised cluster node cannot forge or replay ops without breaking the signature.

Threat model

BFT signing protects against compromised infrastructure: An authenticated client can still send any op permitted by its token. BFT signing provides origin integrity, not access control — use permissions for that.

How it works

  1. On MeridianClient.create() with signing: true, the SDK loads (or generates) a per-client Ed25519 keypair from localStorage
  2. Every outgoing op is signed over its raw msgpack bytes (op_bytes) before sending
  3. The server verifies the signature against client_pubkey embedded in the token
  4. Ops with an invalid or missing signature are rejected with HTTP 401

Quick start

1. Enable signing on the client

2. Register the public key when issuing a token

The token must embed the client’s public key before signing is enforced. This requires a two-step flow: Step A — get the client’s public key (before or after create, but before issuing the token):
Step B — pass it to your app server’s token issuance endpoint:
Step C — your server forwards client_pubkey to Meridian:
Once client_pubkey is in the token, the server will require a valid signature on every op from that client.

3. Use the client normally

Signing is fully transparent — gcounter.increment(), lwwregister.set(), etc. work identically. Every op is signed automatically before sending. Ops queued while offline are signed on reconnect.

Keypair persistence

Keypairs are persisted in localStorage under two keys: On next page load, loadOrGenerateKeypair() reloads the same keypair — the client_pubkey embedded in the token remains valid across sessions. If localStorage is unavailable (SSR, Node.js), a fresh keypair is generated per session and client.publicKeyBytes will differ each time. In that case, issue a new token with the updated client_pubkey on each session start.

Combining with E2E encryption

BFT signing and E2E encryption are independent features and compose freely:
Signing protects op origin. Encryption protects op content. Together they prevent both forged ops and plaintext leakage through a compromised cluster node.

API reference

client.publicKeyBytes exposes the raw public key bytes after create() resolves. This is equivalent to keypair.publicKeyBytes and is null when signing is not enabled.