Audit SPF, DKIM and DMARC for a list of domains. Catches SPF 10-lookup permerrors, subdomain policy gaps and Gmail/Outlook bulk-sender failures. RFC 9989 tree walk.
Three verdicts change in this build. Each one changes because the previous
answer was wrong, and each is listed here with the domains it affects.
MX no longer implies that a domain sends mail
What changed: the bulk-sender verdict used to treat any domain publishing MX
records as a sender. MX means a domain receives mail. It says nothing about
sending. Every receive-only domain — a forwarding address, a contact mailbox, a
domain that takes replies and originates nothing — was graded as a bulk sender
and failed for lacking SPF and DKIM it had no reason to publish.
What a domain is graded on now is positive evidence of sending: a published
DKIM key (it signs what it sends), or an SPF record authorising at least one host
to send as it. v=spf1 -all authorises nobody and is the opposite of a sending
claim. An own DMARC record is not evidence either — publishing a defensive
p=reject on a domain that sends nothing is exactly what a careful operator does.
Verdict change: a domain with MX but no DKIM key and no SPF naming a sender
now returns not_applicable instead of fail. The summary names which of the
three non-sending cases it is, because they are not the same thing to act on:
receives-but-does-not-send, an explicit null MX, or nothing in DNS at all.
Not affected: real senders. A domain with a DKIM key or an SPF record naming
senders is graded exactly as before.
Wildcard _domainkey no longer produces phantom DKIM keys
What changed: a domain can publish a wildcard TXT record under _domainkey
that answers every selector. Probing the 12 common selectors then "found" 12
keys the domain never published, and the Actor reported DKIM as present.
The Actor now asks for one selector that cannot exist before probing any real
one. If DNS answers that, the domain has a wildcard, no key is reported, and the
result says the presence of DKIM cannot be determined here — which is neither
"absent" nor "present". It also costs 12 fewer DNS queries.
Verdict change:example.com was reported as a passing bulk sender. It
publishes a wildcard v=DKIM1; p= and a null MX — a domain that accepts no mail
at all. It now returns not_applicable.
Revoked DKIM keys are read as revocations
What changed: RFC 6376 §3.6.1 — p= with an empty value means the key has
been revoked. The Actor counted the record's existence as DKIM presence, so a
domain that had deliberately retired a key was reported as still signing with it.
Revoked selectors are now reported separately in dkimRevokedSelectors and a
warning names them. They do not count toward DKIM presence.
Null MX is read as the declaration it is
What changed: RFC 7505 — a single MX with preference 0 and a root exchange is
a domain stating outright that it accepts no mail. The Actor previously read it
as an ordinary MX record, i.e. as a mail-receiving domain.
It is now reported in the nullMx field, noted as correct configuration rather
than a fault, and excluded from receivesMail.
New output fields
sendsMail, receivesMail, nullMx, dkimWildcard, dkimRevokedSelectors.
sendsMail is the field the bulk-sender verdict actually turns on, so a
not_applicable row can be checked rather than taken on trust.
0.2 — 2026-08-23
Pay-per-event pricing at $0.004 per domain audited. Every run writes a
BILLING_RECEIPT; charging stops at the buyer's maximum cost per run and never
past it. package-lock.json shipped so the SDK version is pinned across builds.
0.1 — 2026-08-22
Initial release. SPF, DKIM and DMARC audit with the RFC 9989 DNS tree walk,
true SPF lookup counting across the full include graph, and Gmail/Outlook
bulk-sender verdicts.