A local DKIM check beats every mail-a-service validator
The instinct when mail looks wrong is to send a message to a validator and read the page it prints. It is a reasonable first move and a weak last one. A third-party validator tests one message it received: its own path, its own envelope, its own timing. Your signing path — your selector, your submission route, your relay — is what you have to be able to vouch for, and the check that does that runs on your own host.
The path you actually ship
On this fleet, submission is not SMTP from a client to the internet. It is mail.ymrtech.com:587 STARTTLS over the WireGuard mesh, with SASL per mailbox — so the address that sends and the address that authenticates are two different facts, and both matter. Port 25 does not leave the host (587, 2525, 465 and 80 reach from it; 25 does not), so external delivery goes through a smarthost with a sender-scoped relay map rather than a global relayhost.
That matters for verification because the relay is part of the path under test. Whatever you measure has to be measured through the pair you actually ship: this mailbox, through this submission port, out through this relay.
Your own host already stamps the verdict
Third-party validators exist partly because the receiving side records what it checked. Your receiving side can do that too, and hosts/mail/default.nix says how:
1
2
3
4
5
6
# rspamd stamps NOTHING for local/authenticated mail ... a purely internal
# submission carries no Authentication-Results ... This host vouches for
# the submission path instead. The stamp lives in the submission-only
# cleanup service, and reaching that service already requires SASL over the
# wg0-only firewall rule below ... Inbound mail from the internet (25) is
# untouched and still gets rspamd's real verdict.
So there are two verdicts to read, and they are not interchangeable. Inbound mail from outside carries the receiving server’s own Authentication-Results — real verdicts, and note that such a pass rides on relaxed DKIM alignment, since a VERP envelope like bounce.<opaque>@return.<domain> cannot satisfy aspf=s. Mail handed in over submission is vouched for by a submission-only stamp recording a true smtp.mailfrom per identity, derived from the account config rather than hand-listed, so adding an alias cannot silently produce mail that looks unauthenticated.
One caveat, because it wastes sessions: a message delivered to a mailbox on the same server never passes through a receiving MTA’s authentication check. Its headers show DKIM-Signature and Received, and no Authentication-Results. When someone asks you to send them mail “so I can inspect the headers”, say plainly that a local copy carries no verdict and point them at an external mailbox’s “Show original” — while you verify the signature itself, which needs nobody’s cooperation.
Read the result with the selector attached
A bare dkim=pass is a weak observation. Record the signer domain, the selector and the body-hash result together, because they fail independently:
- The signing config pins domain and selector:
rspamadm configdump dkim_signingshows the path and the selector, and the rspamd log printsDKIM_SIGNED{ymrtech.com:s=mail}. - A selector mismatch is what a
dkim=passwithout a selector hides, and it is exactly what a rotation breaks. - A body-hash mismatch with a valid RSA header signature usually means your verifier’s relaxed canonicalization is wrong, not the sender’s. Get the padding check right and the false negative disappears.
- Check SPF against the envelope sender the relay actually used, not the
From:header. Here DMARC passes on SPF alignment alone for mail sent from this host (aspf=s, envelope domainymrtech.com), which means a broken DKIM costs reputation rather than delivery — a good reason never to use “it arrived” as your DKIM test.
Rotation is the outage you are pre-empting
A DKIM selector change is a mail outage waiting to happen unless both the old and the new selector are published before the switch. A local check is the cheapest way to prove both resolve: query the old name and the new one, and confirm the new key is the complete value, not a truncation.
That last failure has a signature. The record is 410 characters with a p= of 392 (an RSA-2048 SPKI), and a record pasted into a DNS user interface is easily cut to the first 255-character chunk — which was the real defect on this domain, not a “stray control byte”, a wrong diagnosis worth remembering. Byte-compare, never eyeball.
What you can check
- The submission shape. The mail host comments in
hosts/mail/default.nix: the submission stamp, the SASL-only firewall rule, and inbound port 25 keeping rspamd’s own verdict. - The signing selector.
rspamadm configdump dkim_signingon the mail host, and theDKIM_SIGNED{...:s=...}symbol in the rspamd log — the two facts a validator’s web page never shows you. - The key bytes. The published TXT against the key file: 410 characters,
p=of 392, compared programmatically rather than by eye.
Then send through the production path, read the verdict your own system stamped, and keep the output — it is the artifact you would have to produce anyway. For how the same discipline applies to a claim you cannot inspect, see the audit page and how the fleet is configured.