self-hosted · open source · Rust + Python
A drop-in anti-spam gateway for your mail server.
Postarmor sits in front of Postfix, Exchange, or any SMTP backend. It scans with ClamAV, runs a cascaded FastText → transformer → LLM classifier, learns from operator feedback, and quarantines threats — all without touching your existing mail server configuration.
$ sudo ./scripts/prepare_debian.sh
$ make setup && make up && make migrate
$ open http://localhost
What it does
Inbound mail hits a small Rust SMTP binary (~5 MB RSS) that runs cheap rejection gates first — RBL, rDNS, SPF, header and attachment policy, SURBL/URIBL URL reputation, VIP impersonation detection, a content-hash blocklist, and a fuzzy SimHash blocklist — before calling a FastText classifier sidecar on the hot path. Messages that survive the gates get DKIM / DMARC verification, then hand off to an async worker for ClamAV scanning and LLM analysis.
The LLM tier runs against a local Ollama model by default, or the Anthropic Claude API when you want stronger reasoning without giving up self-hosting. Results are cached by content hash; operator labels retrain the FastText model in-place with no container restart.
Everything runs in Docker Compose: SMTP · API · worker · classifier · ClamAV · Valkey · PostgreSQL · nginx + React dashboard. Point your MX records at one machine and you have a full filtering tier in under ten minutes.
Features
Rust SMTP frontend
tokio-based server on ports 25 / 587, ~5 MB idle RSS, full ESMTP state machine, STARTTLS, and a DATA-phase pipeline ordered cheapest check first so expensive crypto runs last.
DKIM / DMARC / ARC
RFC 6376 DKIM verify, RFC 7489 DMARC evaluate (off · tag · reject), RFC 8617 ARC sealing for downstream forwarders. All wrapped in a 5 s timeout so slow DNS never stalls the hot path.
Cascade classifier
FastText (<5 ms) then a CPU transformer if FastText is unsure. Confident verdicts skip the LLM entirely. Borderline messages escalate to Ollama or the Anthropic Claude API.
Content-hash & SimHash blocklists
Operator spam labels populate Valkey keyspaces that the SMTP server reads during DATA. Exact-hash blocks byte-identical resends; fuzzy SimHash catches near-duplicate mutations with Hamming-distance match.
ClamAV antivirus
INSTREAM TCP scan of HTML bodies and attachments. Virus detection quarantines the message and records the signature name in the dashboard.
LLM analysis with context
SPF, DKIM, and DMARC verdicts are injected into the LLM prompt via
trusted Authentication-Results and
X-Gateway-* headers. Results are cached by content hash
so spam blasts skip the LLM.
Operator feedback loop
Release-from-quarantine and mark-as-spam write labeled training rows. When the LLM confidently disagrees with the fast stages, the worker auto-labels the row. Admins retrain FastText on demand from the dashboard — model swaps atomically, no restart.
Per-IP rate limiting
Four independent throttles per source IP: concurrent session cap, new-connection rate, accepted-message rate, adaptive auto-blocker driven by a sliding-window rejection counter. All state in Valkey, fail-open on errors.
Delivery retry (RFC 5321)
Backend 4xx or connection failure queues the message with exponential backoff (5 min → 30 min → 2 h → 6 h → 24 h → 48 h → 96 h). Only hard 5xx bypasses the retry queue.
React dashboard
Live metrics, email log with per-stage verdict chip, quarantine with bulk release, feedback & retrain controls, rate-limit editor, SMTP debug trace viewer. TanStack Query keeps everything near-real-time.
SMTP debug trace
Runtime-toggleable per-session protocol trace captured into
smtp_debug_logs. Every accepted, rejected, or aborted
connection recorded in full when enabled — zero overhead when
off.
Self-hosted & auditable
Runs entirely on your hardware. No SaaS dependency, no third-party analytics, no phone-home. Full source on GitHub — Rust for the SMTP / worker binaries, Python for the FastAPI control plane and classifier, React for the dashboard.
How it works
One sending MTA talks to one Rust binary. Everything else hangs off Valkey and PostgreSQL.
Sending MTA
|
v
+------------------------------------------------------------+
| smtp_rust DATA-phase pipeline (cheapest first) |
| |
| rate-limit -> RBL/SPF -> header/attachment checks -> |
| content-hash -> SimHash -> FastText inline -> |
| DKIM / DMARC verify -> stamp A-R -> insert |
+------------------------------------------------------------+
| |
v v
PostgreSQL <----- worker_rust --------> Valkey
|
+------------+-----------+-----------+
v v v v
ClamAV classifier Ollama Claude
antivirus FastText + (local) (API)
transformer
Inbound mail gets rejected at the earliest stage that can decide — a spam-blast hit on the content-hash or SimHash blocklist never pays the DKIM DNS lookup cost. Messages that survive the DATA pipeline land in PostgreSQL, get enqueued in Valkey, and are picked up by the worker binary for antivirus scanning and cascade classification. Confident FastText or transformer verdicts write a final decision directly; borderline cases escalate to the LLM tier.
Quick start
Postarmor runs on Linux, macOS, or anything that runs Docker. On a fresh Debian 12 / 13 host the bootstrap script installs every dependency in one shot — Docker Engine, Node.js 22, Rust stable, Python 3, and the base C build toolchain.
1. Bootstrap the host (Debian 12 / 13 only)
sudo ./scripts/prepare_debian.sh # install everything
sudo ./scripts/prepare_debian.sh --check # verify-only
newgrp docker # activate group membership
2. Configure
git clone https://github.com/ahmetbuba/postarmor.git
cd postarmor
make setup # copies .env.example -> .env
$EDITOR .env # set DB password, backend SMTP host, admin password
3. Start the stack
make up # builds all images and starts services (~5 min first run)
make migrate # creates the database schema
make pull-model # downloads llama3.2:3b (~2 GB, one-time)
4. Open the dashboard
open http://localhost
# login with ADMIN_USERNAME / ADMIN_INITIAL_PASSWORD from .env
Point your MX records (or inbound SMTP relay) at this server on port
25. Set BACKEND_SMTP_HOST or configure domain routes in
the dashboard to send cleaned mail onward to your real backend.
Documentation & links
- README — full feature list, configuration reference, runtime settings
- Requirements — host specs and supported platforms
- Roadmap — tracked implementation backlog
- Issue tracker — bugs, feature requests, questions