← back to /blog← powrót do /blog
mailops: SPF, DKIM, DMARC and the rest of mail, from one terminal
Email breaks quietly. SPF still resolves, but you crossed the ten-lookup limit last
week. DKIM is fine on the old selector and missing on the new one. DMARC says
p=reject, except half your senders were never aligned. MTA-STS is published — but the
policy doesn't actually cover the MX host you migrated to. None of it throws an error.
Mail just starts landing in spam, and you find out from a customer.
So I built mailops — a small Go CLI that checks the whole mail posture of a domain in one pass, from the terminal or CI.
What it does
One subcommand, mailops check example.com, and it looks at:
- SPF — presence, multiple records, the
allpolicy, and an estimated DNS-lookup count afterinclude/redirectexpansion, so you see the ten-lookup limit coming. - DKIM — selector discovery across common and provider selectors, plus public-key parsing and key size.
- DMARC — policy,
pct, report destinations, and external-report authorization hints. - MTA-STS & TLS-RPT — the TXT record, the HTTPS policy file, and whether your real
MX hosts are actually covered by the policy
mx:patterns. - BIMI — record syntax, the SVG logo and VMC authority-evidence URIs, and DMARC-enforcement compatibility.
- Reputation — DNSBL checks for MX IPs out of the box, plus optional domain DBL / SURBL / URIBL zones.
With --smtp it goes active: connect to the MX or submission hosts, read the banner,
EHLO, STARTTLS on 25/587, implicit TLS on 465, check the negotiated TLS version and
certificate expiry, list the advertised AUTH mechanisms, and run an unauthenticated
open-relay RCPT probe that never sends DATA. --mda does the same posture check for
IMAP/POP on 143/993/110/995 — availability and TLS only, no login, no mailbox reads.
Provider profiles
Most domains sit behind a known platform, so --profile google-workspace (or
microsoft-365, amazonses, sendgrid, mailgun, postmark, zoho, …) pre-fills the
right DKIM selectors and endpoints. Anything you pass explicitly wins over the profile.
In CI
Output is human-readable text by default, or --json / --yaml / --prom, and
--otel-endpoint POSTs OTLP/HTTP metrics straight to a collector. Exit codes mean what
they say — --fail-on warn and --strict decide exactly which findings break the build.
Drop it in a scheduled job and you catch the broken SPF, the expiring MX cert, or a
fresh DNSBL listing before your customers do.
Where it lives
- Landing: systeam.pl/mailops
- Source: github.com/systeampl/mailops — Apache 2.0
- Same family as dnsops and certops
- Issues and pull requests welcome
— Paweł