OpenE2EE

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.

Encrypting content is a narrow guarantee, and the narrowness is the point. This page states the boundary precisely. A privacy promise outside this boundary creates liability and misleads users.

E2EE is not anonymity. The relay never needs message plaintext or device private keys. It does need to route, and routing is information.

The opacity ledger

Opacity ledger

ArtifactStays on deviceSent to relayIn object storeVisible as metadata
identityKeyPair private halfyesnonono
identityKeyPair public halfyesyesnoyes
Message plaintextyesnonono
Message ciphertextyesyesnoenvelope size
Session record (version: 4)yesnonono
Message keys (deleted after use)yesnonono
One-time prekeys, public halvesyesyesnoconsumption rate
recipientUserId passed to send()yesyesnoyes
deviceId and registration IDyesyesnoyes, device count
encryptedDeviceNameyesyesnociphertext length
Sender certificate from fetchSenderCertificate()yesyes, issued by itnoissuance time
Attachment plaintextyesnonono
Encrypted attachment bytes via client.media.upload()stagednoyesobject size, upload time
Profile name via encryptProfileNameyesyes, as ciphertextnociphertext length
GroupsV2 encrypted group stateyes, decryptedyes, encryptednoversion sequence, change rate
Sender-key distribution to a group memberyesyes, encryptednomembership size, timing
Read receipt from sendReadReceipt(...)yesyesnotiming
Typing indicator from sendTypingIndicator(...)yesyesnotiming, frequency
Safety-number decision via confirmSafetyNumber(...)yesnonono
IP address and TLS fingerprintn/ayesyesyes

Read the right-hand column as an attacker would. Connection records reveal the social graph, relationship frequency, and approximate sleep schedules. They also reveal group size, membership-change timing, approximate message size, and sending device. For many threat models, this metadata is more valuable than the content.

Sealed sender, stated with its documented limits

Sealed sender removes the sender identifier from the envelope. The relay then handles a message that it cannot attribute to a sender. client.isSealedSenderEnabled reports whether it is active. This improves privacy but does not provide sender anonymity.

Published research is direct about why:

this one-sided anonymity is broken when two parties send multiple messages back and forth; that is, the promise of sealed sender does not compose over a conversation of messages

The mechanism is delivery receipts. Receipts are typically on by default, and each one is an automatic reply that correlates a sealed envelope with an identified fetch. The same work reported that Signal Messenger could link sealed sender users in as few as 5 messages. Follow-up research extended the technique from pairs to deanonymizing entire group conversations.

The property that matters most for your threat model: this attack operates at the application layer. It correlates message and receipt patterns, not network addresses. It therefore works against users behind Tor or a VPN because network anonymity protects a different layer.

Use sealed sender. Do not describe it to users as anonymity.

What defeats the guarantee entirely

A compromised endpoint. End-to-end encryption protects the middle. It does not protect an endpoint. Malware, a hostile OS, a jailbroken device, or an attacker with an unlocked phone can read device plaintext. A person who can see the screen can also read it. No SDK changes this property.

Backups. A backup is a copy, and its encryption defines its protection. A device backup can copy decrypted message rows to a cloud service. If the provider can access the key, the content returns to the custody model in E2EE is not TLS. Your app owns decrypted message rows and local files after the client decrypts or stages them. Your app must exclude or protect them in each platform backup path.

Browser code delivery. In a web application, the server in your threat model ships the encryption code. One malicious or compromised deployment can capture keys before ciphertext exists. There is no general solution today. One practitioner summary states that "these mechanisms for trusting web apps are still pretty early and there's no blessed path."

A non-extractable IndexedDB key is a signing and decryption oracle, not a safe. Cross-site scripting can read the handle and call decrypt() with attacker-selected input. In this attack, "the attacker never touches the raw key bytes" because the handle remains usable. This behavior makes the protection weaker than it appears. The browser store (/local/store/web) documents this boundary directly: encryption at rest is not an XSS defense, and a deployment requires the origin-security review its adapter guide describes.

Deletion. Cryptographic confidentiality creates no remote deletion guarantee. After another participant's device decrypts a message, that participant controls the plaintext. Delete-for-everyone is a cooperative client request, not an enforcement mechanism. It cannot remove a screenshot, export, or modified-client copy. State this limit in your product copy. See Deletion and revocation.

What we will not claim

Not FIPS 140-validated. This is pure-JavaScript cryptography. It is not a FIPS 140 validated cryptographic module and it does not run inside one. The SDK's security documentation uses the phrase "PQXDH uses standardized FIPS 203 ML-KEM-1024 behavior" to describe algorithm behavior, not validation. If procurement requires a validated module, this SDK does not satisfy the requirement.

Many US federal and federal-adjacent programs have this requirement. Configuration cannot change this status. Check the requirement before you build.

Reviewed continuously by adversarial AI agents; not audited by any independent firm. Treat every security property here as documented, tested, and machine-reviewed. Do not treat it as external evidence from a firm. See Security review for what we run today.

Not yet 1.0. The SDK is 0.1.x. Public APIs and persisted formats may change before 1.0.

Next

On this page