Last updated 27 August 2026
Security
This page describes what we actually do, in enough detail that you can check it. It is not a trust badge. Where a control has a limit, the limit is written down here as well.
Keeping tenants apart
One organisation must never be able to see another organisation's data, and that is enforced below the level of any individual screen. Every database query runs through a repository that requires an organisation context and adds the organisation identifier to the query itself. Parent and child records are linked by composite keys that include the organisation, so a record that pointed at another tenant's parent would be rejected by the database, not by a forgotten check in application code.
A request for an identifier that exists but belongs to someone else returns "not found", not "forbidden". The difference matters: a "forbidden" response confirms the record exists. Repeated mismatches from one account are recorded as an abuse signal and raise an alert, because that pattern is enumeration rather than a typo.
Background jobs get no shortcut. A job builds the same organisation context from its own payload and uses the same repositories, so an automated task cannot reach across tenants either. The remaining risk in this area is hand-written SQL that forgets the condition, so raw queries are confined to one directory, each file carries a mandatory isolation test, and changes there need a second reviewer.
Secrets at rest
Some secrets have to be recoverable rather than hashed: the credentials our servers use to talk to your mailboxes, DKIM private keys, and two-factor seeds. Those are encrypted with AES-256-GCM under a data key from a managed key service, stored with a version number so they can be rotated without downtime, and decrypted only in the process that needs them at the moment it needs them.
- A DKIM private key is generated by us, encrypted before it is stored, and is not returned by any API or shown on any screen, to you or to us.
- The browser never receives a mail credential. Webmail talks to our server, and our server talks to the mail servers.
- Our logger has a redaction list. Any field named like a password, secret, token, authorization header, signature or DKIM key is replaced before the line is written.
- Payment keys are server-side only. A build check fails the release if any secret value appears in the JavaScript sent to browsers.
- Backups are encrypted with a separate key, and restoring one requires two people.
Sign-in and sessions
- Passwords are hashed with Argon2id. We check new passwords against known breach lists and reject ones that appear there.
- Sign-in attempts are throttled by IP address and by email address, with a lockout that lengthens after repeated failures.
- The session cookie holds a random opaque token. The database stores only its SHA-256, so a stolen database dump does not yield usable sessions.
- The cookie is Secure, HttpOnly, SameSite=Lax and host-prefixed. Nothing about the session is readable by JavaScript, and no token is ever put in a URL.
- Sessions expire after 8 hours of inactivity and 30 days absolutely, whichever comes first.
- A new session identifier is issued at sign-in and whenever your permissions change. Changing or resetting a password revokes every session on the account.
- Staff impersonation, used only to reproduce a reported fault, carries a reduced permission set, expires after 30 minutes, cannot take a payment or change a password, is fully audited, and notifies the organisation owner.
The audit log
Every action that changes state writes an audit row in the same database transaction as the change itself. An action that succeeded but was not logged is a bug, not an accepted trade-off. Each row records who acted, what they acted on, the outcome, the IP address, the user agent and the request identifier.
Audit rows hold identifiers and counts. They never hold a message subject or body, because an audit trail that quietly accumulates mail content is a second copy of your mail with weaker access rules. Organisation owners and admins can read their own audit log in the admin screens. Rows are kept for 24 months.
Domain authentication: SPF, DKIM and DMARC
These three records are what stop other people sending mail that claims to be from your domain, and what stops your own mail landing in spam. When you add a domain we generate the record set for you and check it continuously, showing you exactly which record is missing rather than a general warning.
- SPF: a TXT record listing our servers as allowed senders for your domain, ending in a hard fail so that everything else is rejected.
- DKIM: we generate an Ed25519 key pair per domain, hold the private half encrypted, sign every outgoing message with it, and give you the public half to publish. Keys can be rotated without interrupting delivery, and we fall back to RSA-2048 for verifiers that do not yet support Ed25519.
- DMARC: a policy record telling receivers what to do with mail that fails the first two, plus an address to send reports to. We start you at p=none so you can read the reports, and we will help you move to quarantine and then reject.
- We also publish MTA-STS and TLS reporting for our mail hosts, so mail to your domain is delivered over TLS or not at all.
- A domain does not go live until MX and the authentication records check out. We would rather delay activation than let your first message fail authentication at a large provider.
Reading mail safely
- Message HTML is sanitised on our servers and rendered in a sandboxed frame with scripts and same-origin access switched off.
- Remote images are blocked until you ask for them, then fetched through our servers, so opening a message does not report back to the sender or hand over your IP address.
- Attachments are always served as downloads with the correct disposition, never rendered in the page.
- Attachment links use a 22-character random identifier, expire, can be limited by download count, can carry a password, and are backed by short-lived signed URLs that are valid for about two minutes and are never logged.
Algorithms in use
| Where | What |
|---|---|
| Passwords | Argon2id, 64 MiB memory, 3 passes, per-user random salt |
| Session tokens | 32 random bytes; only a SHA-256 of the token is stored |
| Stored secrets | AES-256-GCM, envelope encrypted with a managed key, versioned for rotation |
| Attachment links | 16 random bytes as a 22-character identifier |
| DKIM signing | Ed25519, with RSA-2048 for verifiers that need it |
| Webhook verification | HMAC-SHA256 over the raw body, compared in constant time |
| Transport | TLS 1.2 and above, HSTS on every response |
What we do not claim
- Mail is not end-to-end encrypted. It is encrypted in transit and the storage is encrypted at rest, but we hold your mail in a form we can deliver and index. Anyone who claims otherwise while also offering search and webmail is describing something else.
- No member of staff has permission to read mailbox contents and there is no code path that would show it. If that ever changes, it will require your consent, be time limited, and appear in your audit log.
- We have not yet had an independent penetration test of the tenant boundary. It is scheduled before general availability, and we will say here when it has been done.
Reporting a vulnerability
Email [SECURITY EMAIL]. Include what you did, what you expected, what happened, and enough detail for us to reproduce it. We acknowledge within three working days and tell you what we intend to do.
- Test only against your own account or a trial account you created. Do not access, modify or keep anyone else’s data.
- Do not run load tests, denial of service tests, or social engineering against our staff or our customers.
- Give us a reasonable time to fix the issue before you publish. We will credit you when the fix ships if you would like us to.
- We will not take legal action over research done in good faith within these limits.
For how long we keep what we hold, and who processes it for us, see the privacy policy.