Early version. me3 has not been independently audited yet. Please don’t rely on it where your safety depends on it. Why

me3 — home Open me3

Security model

What me3 is designed to protect, how, and — just as important — what it can’t protect. The full threat model is in the project’s SECURITY.md; this is the readable version.

The honest limits first

  • No independent audit yet. me3’s code has been tested (including known-answer tests against the published test vectors and an independent re-implementation of our key steps), but no professional security reviewer has examined it. Don’t rely on it for high-risk use — journalism under threat, abuse situations, anything where being read could put someone in danger — until that review has happened.
  • Web delivery means trusting our server. Your browser downloads me3’s code from me3.org.uk. If our server were hacked or forced to, it could send changed code that leaks your keys. Installed apps from app stores share this kind of risk with the store, but a website can change between visits. me3 keeps a cached copy on your phone and checks each script against a fixed fingerprint, which helps, but it isn’t a full answer.
  • Metadata is reduced, not hidden. See what the server can see.
  • Your device is the vault. Someone who has your unlocked phone, or malware on it, can read your messages. JavaScript in a browser also can’t promise the constant-time behaviour that native crypto code aims for.

What me3 aims to protect

The cryptography

me3 doesn’t invent cryptography. Every primitive comes from libsodium.js (version 0.8.4, built from libsodium 1.0.22), and the protocols follow Signal’s published specifications:

JobWhat me3 usesSpecification
First contact (key agreement)X3DH with X25519, SHA-256, a signed prekey and a one-time prekey in every inviteX3DH, revision 1 (2016)
Prekey signatureXEdDSA (XEd25519) — signatures with the X25519 identity keyXEdDSA, revision 1 (2016)
Message keysDouble Ratchet (section 3), HKDF-SHA256 root chain, HMAC-SHA256 message chainsDouble Ratchet, revision 4 (2025)
Message encryptionXChaCha20-Poly1305 (an AEAD); key and nonce derived from each one-time message key with HKDFAllowed by Double Ratchet section 3.1
PassphraseArgon2id (3 passes, 64 MiB) unlocks a random key that encrypts everything stored on the devicelibsodium crypto_pwhash
Contact codes, mailbox numbersHMAC-SHA256 of a per-pair secret derived from the X3DH resultme3’s own glue — see below

Where me3 makes its own choices, they’re built only from the pieces above and written down for reviewers: the invite format (the X3DH “prekey bundle” travels in the QR code rather than being fetched from a server), an outer encrypted envelope that hides ratchet headers from the server, the rotating mailbox and code derivations, padding to fixed sizes, and a safety number modelled on Signal’s (but not compatible with it). Signal’s optional header-encryption variant and its newer post-quantum ratchet are not implemented.

What the server can see

Our database doesn’t record IP addresses. Rate limiting uses a keyed hash of the address that is replaced every day and deleted within two hours. The web host’s own access logs are separate — see the privacy notice.

Checking the code

The libsodium.js file we serve is byte-for-byte the official dist/browsers-sumo/sodium.js from release 0.8.4, checked against the npm registry’s signed hash and the GitHub release tree. Its SHA-256 is:

5d6f10ef3e530b45536ee0aa40fa54a46220cc78984df6992a9d48e9a741e8f7

The app page loads every script with a Subresource Integrity hash, so the browser refuses a file that doesn’t match. The tests that check our code against the published test vectors ship with the project.

Found a problem?

Please email hello@me3.org.uk with “security” in the subject. We’ll reply, fix what we can and credit you if you’d like.

Sources

Checked 20 October 2026.