In short
- Each message is sealed on the sender’s phone with two algorithms at once: X25519 and the post-quantum ML-KEM-1024 (Kyber). Reading it means breaking both.
- The server receives a mailbox address and ciphertext. There is no text, sender or recipient in it.
- Audio and video are encrypted with DTLS-SRTP between the two phones. The relay forwards packets it cannot open.
- All of this can be checked: the code is on GitHub, 7 tests run with one command, and a call can be inspected in Wireshark.
Messages: how each one is sealed
The text is first encrypted by a Double Ratchet session — each conversation has its own keys, and they change with every message. It is then packed together with the sender’s details into an inner packet, so the sender ends up inside the ciphertext.
That packet is sealed to the recipient: every message gets a fresh one-time X25519 key and an ML-KEM-1024 encapsulation. Both secrets are combined into one key with HKDF-SHA256, and the packet is closed with AES-256-GCM. The result is padded to a fixed size.
The sender’s identity travels inside the ciphertext. The relay sees neither sender nor recipient.
- 1. Ratchetencrypt the message
- 2. Inner packagesender + ratchet message
- 3. SealX25519 + ML-KEM
- 4. Padsize buckets
- 5. Deliverrendezvous ID
ss₁ = X25519(eph_priv, recipient_x25519)ss₂ = ML-KEM-1024.Encaps(recipient_kyber)k = HKDF-SHA256(ss₁ ‖ ss₂, salt = HYBRID_LABEL, info = "key-derivation", 64)[0..32]box = AES-256-GCM(k, iv₁₂, inner) → { ephemeralPublicKey, kyberCiphertext, iv, encryptedInner, authTag }What the server sees
We deliberately show what the server does see. A proof that hides the inconvenient parts is just advertising.
7 tests anyone can run
The repository contains tests/proof.rs. It reproduces the exact seal that closes every message and checks each claim. For example:
The attacker has the recipient’s X25519 key but not the ML-KEM key. The message does not open.
The test models a future quantum computer that has broken X25519. Without the ML-KEM-1024 key, the message still does not open.
Check it yourself — three commands
All you need is Rust. Builds and tests run on Linux, macOS and Windows. rustup.rs
The result on our machine. Yours should match.
Calls: only two phones hold the keys
Audio and video are encrypted with DTLS-SRTP, the encryption built into WebRTC. The DTLS handshake runs directly between the two phones. If a direct connection isn’t possible, packets go through a relay (TURN): it takes no part in the handshake and has no keys. Group calls connect participants directly.
Call keys are created on the two phones. The relay forwards encrypted packets it cannot open.
- Audio and video are encrypted with DTLS-SRTP directly between the two phones.
- The keys never leave the phones. The relay and the network see only encrypted packets.
- Call setup (SDP and key fingerprints) is end-to-end encrypted and signed by the caller, then delivered through the same anonymous channel as messages.
- The server cannot read or change call setup, so it cannot slip itself in as a middleman.
- The call description (SDP with DTLS fingerprints) is encrypted like a regular message.
- A foreign or altered signature means the call isn’t established. The server can’t sit in the middle.
Checking a call in Wireshark
- Connect the phone to a Wi-Fi network you control and record its traffic with Wireshark — for example, on a laptop sharing its connection.
- Make a call in Umbera.
- Apply the filter below.
- You’ll see the DTLS handshake, followed only by encrypted SRTP packets.
Honest assumptions
A proof is only as honest as its conditions.
The participants’ phones aren’t infected
Malware on the device sees the screen — no messenger can protect against that.
The recipient doesn’t forward the conversation
Encryption can’t stop a person from sharing what they received.
Standard algorithms are sound
ML-KEM-1024, X25519, AES-256-GCM, HKDF-SHA256 and DTLS-SRTP are NIST and IETF standards. We don’t invent our own ciphers.
Keys of important contacts are verified
Key transparency and key pinning warn you if a contact’s keys change.
What’s published
The cryptographic core in Rust — the same RustCrypto, dalek and OpenMLS libraries used in the apps — and the protocol layer in Kotlin. The server and the app interface are not in the repository.
Licensed under Apache-2.0: anyone can read, build, run the tests, verify and reuse it.
Found a vulnerability?
If you can disprove even one claim, we want to be the first to know. We reply to every report. How to report is described in SECURITY.md
Privacy you can verify
No phone number or email. Post-quantum encryption for every message.