OpenE2EE

How E2EE changes your architecture

End-to-end encryption converts a server-authoritative CRUD application into a device-authoritative one, and the real cost is the inventory of logic that has to move.

The cryptography is already written. Adopting end-to-end encryption changes where authority lives. Each feature that assumed server authority creates migration work.

A conventional application is server-authoritative. The server holds the canonical rows, answers queries, enforces invariants, runs the background jobs, and hands clients a view. Clients are thin.

End-to-end encryption inverts that. Devices hold the keys, so devices hold the meaning. The server holds opaque envelopes and a sequence number. Your relay becomes an authenticated delivery and prekey service, not a source of truth.

This change requires two migrations. Data moves from queryable rows to ciphertext and device-local state. Logic moves from one controlled service to many devices.

Evidence from the largest team to complete this work

Meta rebuilt Messenger for default end-to-end encryption. Timothy Buck, who worked on it, described the scope plainly:

People have come to expect a large number of features in Messenger, across messaging and calling, well over 100. And each of those had to be redesigned and rebuilt... A lot of the logic was on the server and that doesn't work anymore.

Use the timeline to calibrate your plan.

Meta announced the intent in 2019. Default end-to-end encryption shipped in December 2023. Meta was still revising the backup protocol in May 2026.

The initial project took approximately four and a half years. Meta had extensive cryptography expertise and controlled both clients.

Zoom publishes a list of features that stop when users enable end-to-end encrypted meetings. The list includes cloud recording and server-side transcription. Publish a similar feature-parity list for your product.

Week one

The things that break first are the ones you never thought of as features.

WHERE content LIKE '%invoice%' stops existing. So does every ORDER BY over message text, every full-text index, every reporting query that groups by keyword.

Link previews have no server-side input. The server cannot see the URL, so a device must create the preview. The device then makes the outbound request. This request exposes metadata to the link target that your server previously absorbed.

Thumbnails have no input. Image resizing, EXIF stripping, and transcoding all assumed a server-side pipeline over readable bytes. That moves client-side, or the ciphertext carries a thumbnail the sending device produced.

Profanity filters, spam classifiers, and content-based rate limits have no input. What remains is metadata-shaped: rate, size, recipient count, recency. Some abuse signals survive that translation. Many do not.

Notifications lose their body. Your push service cannot render "Alice: are we still on for 3?" because it does not have it. The device decrypts on wake and rewrites the notification locally.

Month six

The second wave is organisational rather than technical.

Admin dashboards show row counts and nothing else. Support tooling that let an agent open a conversation to reproduce a bug cannot open anything. "Log in as this user" is not a permission you can grant. It is a capability that no longer exists.

Bots and integrations either become cryptographic group members with their own device identity and keys, or they disappear. A webhook that receives plaintext is a hole straight through the property you spent four quarters building.

Analytics moves to what devices choose to report. Retention, engagement, and funnel analysis become opt-in client telemetry with all the sampling bias that implies.

Compliance archiving and legal hold become member-side problems. If a regulated customer requires retention, add a designated archival identity to the conversation. Participants must be able to see this identity. This requirement needs a product decision.

Composition boundaries

The SDK separates portable protocol behaviour from application infrastructure. Each boundary has a defined content and a defined prohibition.

BoundaryContainsMust not contain
Local storeIdentity private keys, sessions, sender keysRemote service credentials
Delivery adapterPublic prekeys and encrypted envelopesDevice private keys or plaintext
Object adapterOpaque encrypted attachment bytesAttachment plaintext or message keys
ApplicationAccount policy and user experienceUnvalidated protocol-state mutation

Adapters are security boundaries, not convenience callbacks. Validate data returned by an adapter before committing it to live protocol state.

This is why storage is a required adapter on createSignalProtocolClient and relay is optional. A client with no relay is still a functioning cryptographic endpoint. A client with no local store is not a client at all. See Local-first and offline-first for what follows from that ordering.

The split the SDK documents is worth quoting rather than paraphrasing:

The client owns protocol coordination; the host application owns persistence, authentication, authorization, and product policy.

Your app owns decrypted message rows and local files after the Signal Protocol client decrypts or stages them.

Everything past that second line, search indexes, caches, exports, backups, screenshots, is yours to design and yours to get wrong.

The SDK is 0.1.x. Public APIs and persisted formats may change before 1.0. Plan the architecture work as the long pole, not the integration.

Next

On this page