Documentation

Architecture & mail flow

What runs where, and every hop a message takes in each direction.

A single node. Postfix owns the SMTP conversation, a content filter makes every policy decision, and three web applications sit behind nginx. Nothing in the mail path leaves the host.

Processes and ports

ComponentListens onRole
Postfix smtpd:25 (public)Accepts inbound mail and relays outbound.
Content filterPostfix pipe, max 10 concurrentOne process per message. Every filtering decision happens here.
Postfix reinjection127.0.0.1:10026Filtered mail re-enters here with the filter disabled — the loop breaker.
OpenDKIM127.0.0.1:8891Signs on the reinjection hop, after filtering.
nginx:80, :443Terminates TLS; routes /, /admin and /api.
Admin console127.0.0.1:8001Configuration, quarantine, reporting, diagnostics.
Recipient portal127.0.0.1:8002Where external recipients read secure messages.
Send API127.0.0.1:8003Authenticated outbound submission for applications.
PostgreSQL127.0.0.1:5432Configuration, metadata, audit and mail log.
ClamAVUnix socketAnti-malware scanning, when enabled.

Only 25, 80 and 443 are exposed. Everything else binds to loopback, so the web applications are reachable only through nginx and the database is not reachable off-host at all.

Throughput. The practical limit is the filter’s concurrency cap of 10 processes, since each message forks a Python interpreter. That is the knob to look at first under load, ahead of CPU or RAM.

Inbound: internet to your mail server

  1. Postfix accepts on :25. Connection-level rejects happen here — optional HELO and unknown-sender-domain checks, any Postfix-level blocklists, and always an anti-open-relay check so the gateway will only accept mail for domains you configured.
  2. Handed to the content filter as raw RFC822 with the envelope details.
  3. Direction is decided by recipient domain: any recipient in your configured domains makes the message inbound.
  4. Per-domain policy overlay. Domains can override most filtering settings independently.
  5. Anti-malware. ClamAV plus the attachment policy, which inspects one level into archives. If the scanner is unavailable the message is deferred, never delivered unscanned — Postfix retries it later.
  6. Greylisting, if enabled: first sighting of a sender triplet is temporarily rejected.
  7. Archive copy written encrypted, if eDiscovery journaling is on.
  8. Spam scoring. A weighted score across every enabled check — blocklists, SPF, DKIM alignment, content heuristics, DNSBL, phishing signals, the AI classifier, Bayesian filtering and DMARC. Over the threshold, the configured action applies: quarantine, tag the subject, or deliver anyway.
  9. Recipient verification, if enabled: confirm the address exists before accepting it.
  10. DLP runs whenever any recipient is external, in either direction.
  11. URL rewriting and the inbound footer applied, if enabled.
  12. Reinjected on 10026 and relayed to your mail server via a bracketed transport, so no MX lookup is done for your own domains.

Outbound: your mail server to the internet

The same path, with one difference at the policy step: the gateway evaluates your encryption rules, and on a match the message is not delivered as email at all.

  1. Your mail server relays to the gateway as a smart host.
  2. Filter runs; DLP applies to any external recipient.
  3. Encryption rules evaluated. Rules match on a named header or a subject regex — so a client add-in stamping a header, or a user typing a tag in the subject, both work.
  4. On a match, the message is protected — encrypted to the local store and replaced with a notification (see below). Evaluated per recipient.
  5. Otherwise reinjected, DKIM-signed, and delivered via MX or your smarthost.

No rules ship by default. The gateway does not guess what should be encrypted. Until you create at least one rule under Protection rules, outbound mail flows normally.

How a secure message actually works

There is no S/MIME, no PGP and no per-message password to communicate. When a rule matches:

  1. The entire raw message — headers, bodies and every attachment — is encrypted as a single blob and written to the local store with mode 0600.
  2. The recipient receives a plain notification email containing a link to your portal. No content, no attachment.
  3. Opening the link shows their masked address and offers to send a code. A 7-digit one-time code is emailed to the address stored on the message, never to one supplied by the visitor — so forwarding the link does not transfer access.
  4. Only the code’s hash is stored. Five wrong attempts burn it; each failure also counts toward an IP block. Resends are rate-limited.
  5. On success the message is decrypted for viewing, sanitised, and rendered in a sandboxed frame. Attachments decrypt individually on demand.
  6. Replies come back through the portal. A reply to an internal address is injected as normal mail; a reply to another external address becomes a new secure message. Reply attachments are re-scanned, because the injection path bypasses the filter.
DefaultValue
One-time code7 digits, 15 minutes, 5 attempts, 60-second resend cooldown
Message readable for14 days from delivery
Unlocked session30 minutes, per message
Reply attachmentsUp to 10 files

After 14 days the link returns an expired response. Rows and ciphertext are deleted later by the retention job — 30 days by default — so a message becomes unreadable before it is erased.

Scheduled work

Systemd timers, all adjustable:

JobSchedulePurpose
System samplerEvery 60sCPU, memory, disk and network series for the dashboard.
Health checkEvery 5 minQueue depth, disk, certificate expiry, an active pipeline probe; alerts on change.
Update checkHourly, jitteredSignature-verified version check. Auto-apply off by default.
AI advisorHourly, jitteredHonours its own configured period.
Backup02:30Database, encrypted store, and secrets when a passphrase is set.
DMARC reports03:00Sends your aggregate reports.
Maintenance03:30Applies retention and prunes metrics.
Quarantine digest07:00Per-user held-mail summary with self-service release.
Admin report07:30Scheduled summary to your operations mailbox.

Failure behaviour

Worth knowing precisely, because it is where mail security products usually disappoint:

  • Any unhandled filter error defers the message. Postfix keeps it queued and retries. Mail is not lost to a filter bug.
  • Anti-malware fails closed. ClamAV unreachable means deferred, never delivered unscanned.
  • Reputation and AI checks fail open. SPF, DKIM, DNSBL, Safe Browsing and the AI classifier all yield “no opinion” on timeout, so a third-party outage cannot stop your mail.
  • DMARC enforcement quarantines, never drops. A false positive is always recoverable.