How it works · technical deep-dive
Every inbound message passes through eight independent layers before a verdict is reached. The scoring layer combines a gradient-boosted classifier trained on a 30-year ISP's live honeypot corpus with Claude (Anthropic's large language model) on the borderline cases. Every component is horizontally scaled across redundant nodes.
Architecture
Every layer can reject. Every verdict is logged with a transparent score breakdown and, where Claude was involved, the reasoning it produced.
Pipeline stages
Every numbered stage corresponds to the architecture diagram above.
Before any SMTP command is processed, Postfix's postscreen daemon inspects the connecting IP. It applies pre-greet checks (talk-before-listen detection), pipelining checks, and queries six independent DNSBLs in parallel.
DNSBLs: zen.spamhaus.org ×3 · spam.dnsbl.sorbs.net ×2 · cbl.abuseat.org ×3 · bl.mailspike.net · psbl.surriel.com · threshold 3
DNSWL: list.dnswl.org — trusted senders bypass the postscreen tarpit
SPF is evaluated by policyd-spf via Postfix's check_policy_service. Hard-fail enforces; soft-fail contributes a feature to the ML score. Per-source rate limits cap abusive senders without blocking legitimate spikes.
Rate limits: 30 connections/min · 60 messages/min · 100 recipients/message
reject_unverified_recipient probes your backend mail server at SMTP time to confirm the recipient is real. Results are cached so probes don't repeat for every message. Eliminates backscatter on bad addresses.
Transport map: proxy:pgsql:/etc/postfix/pgsql-transport.cf · backend verified before any content is queued
ClamAV scans every attachment, including archives and Office macros. DKIM signatures verified by opendkim, DMARC alignment enforced by opendmarc. Body URLs cross-referenced against three URIBLs.
URIBLs: multi.uribl.com · multi.surbl.org · dbl.spamhaus.org
The MxGuard milter (scanner.py) parses headers, body, URLs and attachments. It extracts ~120 features across six buckets (envelope, headers, subject/body, URLs, attachments, reputation) and assembles the feature vector for the classifier.
Implementation: Python 3 + python3-pymilter · in-process · loopback socket 127.0.0.1:7358
The LightGBM classifier scores the feature vector in ~3.4 ms. If the score lands in the uncertain band (0.50–0.95), the message body is sent to Claude via the Anthropic API. Claude reads it for intent and impersonation cues, returns a verdict and a written reason, which is logged with the message.
Thresholds: ≥ 0.97 reject · 0.85–0.97 quarantine · 0.50–0.85 tag · < 0.50 pass
openarc seals the message with an ARC signature carrying the original DKIM/SPF/DMARC results forward. Downstream receivers can trust the original authentication even though MxGuard re-routed the message. Critical for forwarders and mailing-list compatibility.
AuthservID: mx1.mxguard.uk · signs all forwarded mail · RFC 8617 compliant
Clean mail is delivered to the customer's real mail server via SMTP. Standard retry behaviour if the backend is briefly unavailable. Local queue isolation per node means a slow backend on one customer doesn't slow other customers' mail.
Training data
Most anti-spam vendors train on stale public corpora supplemented by a single feedback loop. MxGuard's classifier is trained on something rarer: real, fresh, labelled spam captured continuously by Transcom's own global honeypot network — the same data that has fed our blocklist and abuse-reporting operations since the mid-1990s.
SigmaNetworks / CentralMail operates relay honeypots in Brazil, Canada, India, Japan, Sydney, and the UK. Each node accepts mail it knows is spam (no real recipients) and ships every received message back to the central corpus.
Transcom has operated as a UK ISP since 1993. We have decades of labelled legitimate mail patterns — transactional, mailing list, business correspondence, newsletters — that anchor the “clean” side of the training set against false positives.
Layered on top: Enron, SpamAssassin, TREC, and similar academic datasets used industry-wide. They give the model a stable, reproducible baseline; our honeypot data gives it the edge on what's actually being sent this week.
Spam evolves weekly. New domains, new lure templates, new infrastructure, new social-engineering angles. A model trained on data from 2022 misses what was sent yesterday. MxGuard's training corpus is updated continuously from live honeypot capture — the same model retrain pipeline that feeds our DNSBL contributions and AbuseIPDB reports also feeds the classifier.
The result: the classifier sees fresh attack patterns within hours of them appearing in the wild, not months later when a quarterly model refresh ships.
The AI brain
Layer 1 — an in-process gradient-boosted classifier — decides the easy 95% in milliseconds. Layer 2 hands the genuinely uncertain messages to Claude, Anthropic’s large language model, which reads them the way a person would on a second look.
A gradient-boosted decision-tree ensemble trained on hundreds of thousands of labelled messages from our honeypot corpus and the public datasets above. Runs in-process inside the milter; no external API call. Continuously retrained as new honeypot data arrives.
| Algorithm | LightGBM v1 |
| AUC | 0.988 |
| Features | ~120 |
| Median latency | 3.4 ms |
| Output | score ∈ [0, 1] |
| Decides | ~95% of mail |
When the LightGBM score lands in the uncertain band, the message body and headers are sent to Claude via the Anthropic API. Claude evaluates intent, urgency cues, impersonation patterns, lookalike-domain references and call-to-action structure — producing a verdict and a written reason that is logged with the message.
| Triggered when | 0.50 ≤ score < 0.95 |
| Reads | full body + headers |
| Outputs | verdict + reason |
| Optimised for | BEC, impersonation |
| Handles | ~5% of mail |
| Training | none on customer mail |
What the gradient-boosted model is actually looking at.
Source IP reputation, ASN, rDNS sanity, HELO/EHLO conformance, MAIL FROM domain age, SPF result.
From/Reply-To divergence, display-name impersonation, Message-ID structure, Received chain length, DKIM result, DMARC alignment.
Subject decoded (RFC 2047), urgency markers, gibberish local-part references, money/wire/invoice language, length and structure ratios.
Count, domain age, TLD distribution, URIBL hits, shorteners, IP-literal URLs, mixed-script hosts.
MIME mismatch, archive depth, macro presence, suspicious extensions, ClamAV verdict, encrypted-payload flags.
7-day rolling ham/spam ratio per sender domain (Redis-cached), recipient’s prior interaction with this sender, ham-discount status.
Cluster architecture
MxGuard runs as a horizontally-scaled cluster of identical MX nodes. Every component — SMTP, ML scoring, database, cache — is redundant. There is no single point of failure that can stop mail flowing.
Multiple active MX hosts; DNS MX priority spreads load. Any single node can be drained for maintenance with zero downtime.
PostgreSQL 16 with streaming replication. Automatic failover; reads can be served from the replica.
Reputation, verify, and rate-limit data live in a 3-node Redis cluster fronted by Sentinel. No single Redis node failure can stall scoring.
Each node maintains its own outbound queue. A slow backend on one customer cannot block another customer's mail.
Technical specification
Every component, every standard, every number.
| Transport | |
|---|---|
| SMTP server | Postfix 3.x |
| TLS | TLS 1.2 / 1.3, opportunistic; LE certs auto-renewed |
| Listening | 25 (mta), 465 (submissions), 587 (submission) |
| Rate limits | 30 conn/min, 60 msg/min, 100 rcpt/msg per source |
| Pre-content layer | |
| Postscreen DNSBLs | zen.spamhaus.org ×3, spam.dnsbl.sorbs.net ×2, cbl.abuseat.org ×3, bl.mailspike.net, psbl.surriel.com; threshold 3 |
| DNSWL | list.dnswl.org — trusted-sender bypass on hi/med scores |
| SPF | policyd-spf (check_policy_service) |
| Recipient verification | reject_unverified_recipient via proxy:pgsql transport map |
| Content layer | |
| Antivirus | ClamAV (clamav-milter); signature feed clamav-freshclam |
| URIBLs | multi.uribl.com, multi.surbl.org, dbl.spamhaus.org |
| DKIM | opendkim — verify inbound, sign outbound |
| DMARC | opendmarc — alignment + policy enforcement |
| ARC | openarc — AuthservID=mx1.mxguard.uk; seals forwarded mail |
| AI scoring | |
| Milter | scanner.py on 127.0.0.1:7358 (python3-pymilter) |
| ML model | LightGBM v1 gradient-boosted trees, ~120 features, AUC 0.988 |
| ML latency | ~3.4 ms median, < 5 ms p95 |
| LLM evaluator | Claude (Anthropic) via API — triggered on 0.50 ≤ score < 0.95; reads body + headers; outputs verdict + written reason |
| Verdict thresholds | ≥ 0.97 reject · 0.85–0.97 quarantine · 0.50–0.85 tag · < 0.50 pass |
| Cluster & storage | |
| MX nodes | N+1 active SMTP hosts behind DNS MX priority; horizontal scale-out |
| Database | PostgreSQL 16, primary + hot-standby streaming replica, TZ Europe/London |
| Cache | Redis 5+ with Sentinel quorum; reputation, verify, rate-limit data |
| Quarantine retention | 30 days default (0–365 configurable), purged daily 04:00 |
| Audit log | Every release / delete / rule edit / label change; searchable, exportable |
| Region | UK only — processing and storage |
| Console & API | |
| Web console | FastAPI + HTMX, served via nginx at app.mxguard.uk |
| Live feed | Real-time verdict stream; releases, allow/block from row |
| Daily digest | 600px mobile-first email, one-click release/delete/allow |
| REST API | Domain mgmt, rules, verdicts, quarantine actions; key-auth |
| Metrics | Prometheus /metrics endpoint, per-domain counters |
Standards
Setup
From sign-up to live mail filtering: typically 10 minutes plus DNS propagation.
From your dashboard, enter the domain and the hostname of your real mail server (where clean mail should land). Done in 30 seconds.
Add a single TXT record to your DNS. Click verify. We confirm via DNS lookup and flip the domain to active.
Add two MX records:
mx1 is primary; mx2 is automatic failover. Mail begins flowing through MxGuard within minutes.