نموذج التهديد
The security model
One sentence carries the whole design: an attacker who takes the entire server — database, secrets, source, live memory — cannot read the mail stored there. Everything below is that sentence, examined.
What is sealed
Bodies, senders, recipients, subjects, previews, attachment bytes and their filenames, drafts, your contacts, the passwords you keep beside your Names with the files kept with them and their earlier versions kept for undo, and the list of senders you block. All of it is sealed with Mashrabiya — a hybrid of a post-quantum and a classical scheme — to a vault identity whose secret half exists only inside your unlocked browser tab. The ingest worker that receives your mail holds the public half: it can seal messages to you and can never open one.
Who we defend against
The operator — us
Defended, and the centre of the design. The historical server mail key was destroyed after every stored message was re-sealed; old database snapshots are unreadable because the key that opened them no longer exists anywhere.
A database thief
Defended for content. A stolen backup or subpoenaed snapshot yields ciphertext, the wrapped vault keys (useless without your passphrase or recovery phrase), and the routing metadata listed below — shapes, not correspondence.
A network attacker
Defended. TLS with HSTS preload, strict CSP with per-request nonces — and sealed payloads are ciphertext before they touch the wire, so TLS is depth, not the only wall.
Password thieves
Defended. Your account password opens the shell: folder counts, the ability to send. Mail needs the vault passphrase, a separate secret stretched with Argon2id on your device and never sent to us.
Brute force
Defended. Limits per address and per account on every credential endpoint, backed by the database so a fresh server instance cannot be farmed for attempts. TOTP two-factor with single-use recovery codes.
Malicious senders
Defended against the usual vectors: remote content blocked until you ask (tracking pixels neutered), HTML sanitised twice, attachments never executed, SPF/DKIM/DMARC verdicts shown.
What we do not defend against
Honesty about limits is the difference between a threat model and an advertisement:
- Your unlocked device. An open vault is an open mailbox — that is what "unlocked" means. Idle lock and aggressive key-wiping narrow the window; they cannot close it.
- Script injection while unlocked. A successful XSS attack reads what you can read. Nonce-based CSP, sandboxed rendering of sender HTML and double sanitisation make it hard; nothing makes it impossible.
- Traffic analysis. The server sees that mail arrived for you, when, and roughly how big. Sealing hides content, not existence. We add no cover traffic and say so.
- Search, a device at a time. Search runs on each device, over an index it builds by opening your mail: we are never told what you search for or what it finds. What we do see is ordinary: each device downloading every letter once to read it, and the move to Spam when a device applies a Block. A device that has not read a letter yet cannot find it, and a locked vault searches nothing.
- A malicious client update. The code your browser runs comes from us. A hostile build could betray the vault on unlock. The source is inspectable and the limit is shared by every browser-delivered E2E product — we state it rather than gloss it.
- A changed ingest. Mail arrives over SMTP as plaintext, as all mail does, and is sealed the moment it arrives. An operator who changed the ingest code could read new mail as it comes in — never what is already stored. The published source and the build manifest on the transparency page are how that is checked.
- Mail to outside addresses. Ordinary email must reach Gmail or Outlook in a form their servers can read, so this server reads a letter to an outside address while it transmits it; the copy in your Sent folder is sealed. Between RocMail members, your browser seals the letter before it leaves, and the server passes on only ciphertext — sealed to a key your browser asked the server for, and only once that key checks out against the instance’s key log, which your own app watches for any key that is not yours. The log has no outside witnesses yet: a server that showed different people different logs is caught only when they compare its head on the transparency page. And mail to a member without a vault is sealed by the server as it arrives; the desktop bridge seals as your browser does, against the same log.
- Your Names, side by side. So that members can seal letters to each other, the directory gives a signed-in member the key a Name seals to. Each of your Names has a key of its own, made in your browser from your mailbox key, so two of your Names answer with two unrelated keys and a member who knows both cannot tell from them that they are one person’s. Two limits remain: answers recorded before 25 October 2026, when every Name of an account shared one key, still link those Names; and a Name made outside the app answers with your account’s key until you next unlock. The server itself always knows which Names are yours.
- A plain export of your passwords. Your passwords can leave in two forms. The encrypted backup is sealed on your device under a passphrase you choose for that file (Argon2id, XChaCha20-Poly1305) and outlives a vault reset; forget that passphrase and nothing opens the file, for us or for you. The plain export, made for moving to another password manager, is not encrypted at all: anyone who gets the file — from a synced folder, a backup service, a borrowed computer — reads every password, one-time-code secret and note in it. The app warns twice before it writes one; after that, guarding the file and deleting it is up to you.
- A password shared by link. A share link carries a sealed copy of the parts of an entry you pick, and the key to it after the `#`, which browsers never send to a server: we keep a ciphertext we cannot open, the views left and the expiry, and delete it at its last view or its expiry. **Anyone who has the link can open it** while it lasts — whoever it is forwarded to, whoever reads the chat it was pasted into, a synced browser history. One view, the default, closes it the moment your reader opens it; opening the page spends nothing, so a chat app’s link preview cannot use it up — only “Show” does. If a one-view link says it is used up when your reader first opens it, someone else opened it first: treat what it held as exposed. Sharing a one-time-code secret hands over the second factor too.
- A seized machine. Whoever takes the server takes what it holds in the clear: the routing metadata below, the security logs within their retention, and the host’s short-lived delivery log. Content stays sealed; the shape of your correspondence does not.
The metadata the server keeps
Each row exists because the server must ACT on it while your vault is closed, and none of it is content. Forwarding addresses, 2FA secrets, DKIM signing keys and the scheduled-send queue are additionally encrypted under a server settings key; the rest are plain database columns, stored unencrypted on this server’s disk.
| Data | Why the server needs it |
|---|---|
| Message-ID | refusing duplicate deliveries needs a comparable value |
| Folder, read, starred flags | server-side triage and unread counts |
| Arrival timestamps and message sizes | ordering, pagination and the storage quota |
| Which name received each message | filing it in the right mailbox at arrival |
| SPF, DKIM and DMARC verdicts | shown beside each message, explained in a sentence |
| Attachment count and size | list rendering without opening bodies |
| Forwarding addresses of your names | forwarding happens at arrival, vault closed |
| Sender domains you mark as spam or not spam | the next letter from them is filed the same way, at arrival |
| Rule conditions | rules run at arrival |
| Scheduled sends | the cron must read what it transmits — rows blank minutes after |
| 2FA secrets | TOTP verifies at login, before any vault exists |
| Custom domains and their DKIM keys | routing needs the name; signing outgoing mail needs the key |
المشربية
Mashrabiya — the seal, by name
Mashrabiya is the name of the construction that seals every message here. A mashrabiya is the carved lattice screen of Arab houses: light passes through it, sight does not — the same lattice the app draws while a message is being unsealed. The server stands on the far side of that screen. It keeps your mail and can never look in.
Every sealed item says which version sealed it — a sealed row is tagged msh2:, a sealed file begins with MSH and a version byte — and a reader that meets a version it does not know refuses it rather than guessing. Version 2, since October 2026, binds each sealed piece to where it belongs: a message to its row, a file to its message, a contact or draft to its own id, all to your key. Whoever holds the database can no longer swap a message’s parts, move one message onto another, or change the authentication badge or the files beside an authentic letter without your device refusing them. Mail sealed with version 1 stays readable.
| Message key | X-Wing hybrid KEM (draft-connolly-cfrg-xwing-kem): ML-KEM-768 + X25519, combined with SHA3-256 |
| Message and attachment content | AES-256-GCM, a fresh key per message and per attachment |
| Passphrase stretching | Argon2id (libsodium crypto_pwhash) — ops 3, memory 64 MiB, derived on your device |
| Key wraps | XChaCha20-Poly1305 |
| Search index | on each of your devices only: XChaCha20-Poly1305 under a key derived from your vault, never sent to the server |
| Recovery phrase | BIP-39, 24 words (256 bits) |
| Security keys | WebAuthn PRF — a hardware key or passkey can open the vault |
| Format | v2 since October 2026: each sealed piece bound to its place; v1 still opens |
The full specification is MASHRABIYA.md, in the source.
Outside the seal, sessions and CSRF use random tokens, constant-time comparison and a double-submit cookie.
What can go permanently wrong
Lose your vault passphrase and your 24-word recovery phrase, and your mail is gone — no reset, no escrow, no override. This is stated at signup, at vault creation, and here, because it is the same fact as our headline claim viewed from the other side. A recovery desk is a key someone else holds.
Questions the page doesn’t answer? The engineering threat model ships with the source — the entire service is public. Or start from the ethics page — the same design, argued from principles instead of primitives.