Cryptography matrix at a glance
| Surface | Algorithm | Who can read the plaintext |
|---|---|---|
| Account password | Argon2id (64 MB, 3 iter) | Nobody — only a verifier is stored |
| Stored external IMAP/SMTP credentials | AES-256-GCM at rest | Server process at the moment of login |
| Mail transport (in) | STARTTLS 1.2/1.3 on :25 | Server — stored in Postgres |
| Mail transport (out) | STARTTLS 1.2/1.3 + DKIM RSA-2048 | Server & recipient |
| 1:1 chat messages | Olm double-ratchet (X25519 + AES-256 + HMAC-SHA256) | You and the recipient — server sees ciphertext only |
| Audio & video calls | WebRTC peer-to-peer + DTLS-SRTP | You and the other caller — media never hits the server |
| TURN relay (NAT-fallback calls) | Coturn with HMAC-SHA1 credentials (1h TTL) | Relayed ciphertext only — media stays DTLS-SRTP encrypted |
| Web session token | HS256 JWT, HTTP-only Secure cookie | Server verification only |
| WebSocket signalling ticket | Short-lived HS256 JWT (5 min) | Server verification only |
Everything marked Server is server-side mail under IMAP semantics — the operator has access so sync, search, and indexing work across devices. If that's not an acceptable model for you, you should encrypt the body yourself with an OpenPGP client before sending through any mail provider, Tacitus included.
Threat model
We optimise for these three adversaries, in order:
- The passive network observer. An ISP, a state-level passive collector, a coffee shop wifi sniffer. STARTTLS on every hop, TLS 1.2/1.3 only, HSTS on the web client, and DKIM/SPF/DMARC on outbound mail.
- The bulk-data adversary. Someone who gets a Postgres dump via a stolen backup or a breach. Account passwords are Argon2id and useless. Stored external-account credentials are AES-256-GCM encrypted at the application layer before they hit the database. Chat ciphertext and WebRTC call data are unreadable by design.
- The compromised single server. If an attacker gets root on the mail host, they can read mail bodies sitting in Postgres and they can MITM new outbound credentials. They cannot retroactively decrypt past chat messages (per-message ratchet keys) or tap old call recordings (we don't keep any — DTLS-SRTP flows directly between participants).
Anti-spam and anti-abuse
Every inbound message hits the following stack, in order:
- Postscreen. Before a real
smtpdworker is forked, the connecting IP is weighted against Spamhaus ZEN, SpamCop, Barracuda and SORBS. Botnets drop at the TCP handshake. - Rspamd milter. The full message is scored against Spamhaus
DBL, SURBL, URIBL, fuzzy hashes from
fuzzy.rspamd.com, SPF / DKIM / DMARC alignment, a Bayesian classifier with per-user autolearning, and a neural network model. Scores ≥ 15 hard-reject at SMTP; ≥ 10 rewrite the Subject; ≥ 6 addX-Spamheaders so downstream clients can filter. - ClamAV. Every outbound attachment is scanned before it leaves the compose endpoint. Infected uploads are rejected with a visible error.
- OpenDKIM. Outbound
@tacitusmail.commail is signed with an RSA-2048 selector; receiving MTAs can verify authenticity via DNS.
All DNSBL lookups for Rspamd route through a local Unbound recursive resolver, so Spamhaus sees our single IP and gives us the full per-resolver free quota instead of competing against millions of shared public resolver queries.
Operational security
Deployment
The service runs in blue/green Docker Compose on a single EU host. Deploys swap the inactive colour behind an internal nginx router and reload without dropping a single WebSocket. We do not maintain staging replicas with production data — real mail never leaves the primary cluster.
Backups
Nightly encrypted Postgres snapshots are stored on a separate EU volume. We do not push backups to any US-based cloud. Retention is 30 days.
Access
SSH to the host is key-based only, with the operator SSH keys pinned in the host config. There is no password auth, no sudo without TTY, and no third-party remote-access agent installed.
Dependencies
Every component is open source, versions pinned at build time. The full list is on the about page.
Responsible disclosure
Found a vulnerability? Please email [email protected] with a proof-of-concept. We commit to:
- Acknowledge your report within 24 hours.
- Triage and confirm or deny the finding within 72 hours.
- Fix critical issues within 7 days, publish the fix as soon as reasonable, and credit you by name (or handle) in the changelog unless you prefer otherwise.
- Never sue or threaten a researcher who follows this process.
We don't run a bug-bounty programme yet, so reports are currently rewarded with our honest gratitude, a hand-written thank you, and — for high-impact findings — a free Tacitus Plus subscription for a year.
Honest limits
Tacitus Mail is not a full end-to-end-encrypted email provider in the OpenPGP sense. Mail bodies are stored server-side so standard IMAP semantics work: sync, search, sorting, sent-copy replication, multi-device access. If your threat model requires that the server operator cannot physically read the plaintext of a stored message, you should encrypt it yourself with an OpenPGP client before sending. Tacitus will deliver PGP-armoured bodies unchanged.
Our 1:1 chat and our WebRTC calls are end-to-end encrypted by construction — the server either sees ciphertext (chat) or never touches the media at all (calls). That is where the strongest cryptographic guarantees live.