Local-first and offline-first
Why the local store is a required adapter and the relay is optional, what changes when the device is the authoritative copy, and the parts of local-first that are still genuinely unsolved.
createSignalProtocolClient requires adapters.storage and makes adapters.relay optional. The type signature states the architecture directly.
A client with no relay is still a complete cryptographic endpoint: it has an identity, holds sessions, encrypts and decrypts, and owns its state. A client with no local store has nothing: no identity to be, no ratchet to advance, no way to persist the state that decryption mutates. The store is the client. Delivery is a service the client optionally uses.
The device is the source of truth
Ink & Switch's local-first work states the distinction better than a paraphrase would:
The key difference between traditional systems and local-first systems is not an absence of servers, but a change in their responsibilities.
And, on what the old arrangement assumed:
In cloud apps, the data on the server is treated as the primary, authoritative copy of the data; if a client has a copy of the data, it is merely a cache.
End-to-end encryption forces the inversion whether or not you planned a local-first design. Once the server cannot read the content, it cannot be the authoritative copy. It holds bytes it cannot validate, index, merge, or repair. The device that can decrypt is the only place where the data is data. Everything else is transport and durability.
Three consequences follow, each a product decision rather than an implementation detail.
Support impersonation becomes impossible by construction. "Log in as this user to reproduce the bug" is not a permission you can grant. There is no server-side view to impersonate into. Support needs a different tool: user-initiated device-side diagnostic export, or structured error reporting that carries identifiers and error codes without content. Design it before launch.
State converges rather than commits. There is no single write path where the server adjudicates conflicts, because the server cannot see what is in conflict. Two devices that were both offline both hold valid state. Reconciliation happens on devices, after delivery, over data only devices can read. Offline and reconciliation covers the mechanics.
Durability is your problem. A cloud app's user who drops their phone in a river loses nothing. A local-first user loses their keys, and their history with them, unless you built a recovery path: itself a cryptographic design with real tradeoffs. See Recovery, backup, and migration.
Access control has to travel with the data
Server-authoritative systems put authorization at a network boundary: a request arrives, a guard checks it, the guard decides. Local-first systems do not have that boundary. Ink & Switch's Keyhive work names the problem exactly:
The local-first setting does not have the luxury of a network boundary: access control must travel with the data itself and work without a central guard.
In this SDK, group sender keys and encrypted GroupsV2 state provide this control. Cryptographic state represents membership. A device either holds the sender key or it does not. The backend stores opaque encrypted group state and sequences changes.
It enforces authenticated access and version order without receiving plaintext group attributes or group master keys. Removing a member therefore requires key rotation with rotateGroupSenderKey(groupId), not row deletion. See Groups.
The genuinely unsolved parts
This is where local-first documentation usually goes quiet. It should not.
CRDTs and group-key protocols pull in opposite directions. CRDT operations apply in any order and still converge. Group key agreement generally requires an agreed order. The local-first community states one MLS problem directly: "There is one major difficulty with MLS: it expects a total order for commits." A total-order key schedule and order-independent data operations use opposite assumptions. Reconciling them is active research, not configuration.
Encryption makes op-log growth worse. A plaintext sync server can compact history: read a hundred operations, determine they collapse into one, store the result. A relay cannot compact what it cannot read. Key-holding devices must compact and redistribute the result. This process adds distributed garbage collection to key management.
Practitioners report this problem. One developer who ships local-first software described the experience:
Synchronization has been my nightmare for the past couple of months. I have rewritten it a couple of times and still not fully satisfied.
Treat synchronization as a subsystem with its own test plan and on-call surface. Do not treat it as glue between the store and relay.
A commercially significant signal, reported honestly
Jazz is a local-first platform built on cryptographic access control. Its documentation appears to describe a move toward a trusted server that applies richer policies during synchronization. The documentation is under reorganization, so this is a report, not a settled fact. We have not independently confirmed the current design or reasoning.
If this reading is correct, it shows implementation difficulty, not a correct general design. The team reconsidered its boundary after implementing expressive, evolving access policies. Plan your permission model early. Retrofitting one makes this type of system expensive.
The SDK is 0.1.x. Public APIs and persisted formats may change before 1.0, and local persistence formats are the ones most likely to move.
Next
- Keys, identity, and sessions: the state in the local store.
- Local storage: adapters, atomicity, and platform constraints.
- Offline and reconciliation: convergence after a disconnection.
- Limits and metadata: what the relay still learns while all this happens.
Keys, identity, and sessions
The composite identity tuple, the four lifetimes of key material, what a session actually is, and why trust-on-first-use is a decision your users re-run on every phone upgrade.
Limits and metadata
End-to-end encryption is not anonymity. This is the exhaustive, cited inventory of what remains visible, what defeats the guarantee entirely, and what we will not claim.