DNS Doctor
MethodologyHow the verdicts are computed

How DNS Doctor decides each verdict

Every verdict is deterministic and reproducible. This page is the citable source for how the numbers are counted and where DNS Doctor deliberately refuses to guess.

SPF

How DNS Doctor counts SPF lookups

SPF allows at most 10 DNS lookups when a record is evaluated (RFC 7208 §4.6.4). DNS Doctor counts them in code, following includes recursively, so the count is authoritative rather than a guess — a record that resolves to more than 10 lookups returns a permanent error at receivers and stops authenticating your mail, even though the record still looks valid.

Each of these mechanisms costs one lookup: include, a, mx, ptr, exists, and the redirect= modifier. The all, ip4, and ip6 mechanisms cost zero — they resolve no DNS. A nested include adds its own lookups on top, resolved recursively against live DNS, which is why a record with only a handful of visible terms can still blow the limit through a provider's deep include chain.

The counter is the same deterministic engine the free SPF lookup counter and the scanner use, so the number you see is the number a receiver computes. SPF is diagnose-only: DNS Doctor never auto-generates a shorter SPF record, because mechanically trimming a policy can silently de-authorize a real sender. It names the offending mechanism and leaves the fix to a human who knows which senders are legitimate.

DMARC

Why enforcement is capped by evidence

A scan can justify p=quarantine at most, and only when it observes alignment-capable configuration — a valid SPF record and a published DKIM selector; a scan cannot prove real mail passes. Without that signal DNS Doctor recommends no enforcement record at all: it tells you to publish a rua= reporting address first. p=reject is never recommended from a scan; it is unlocked only by aggregate-report evidence. Moving to enforcement without that evidence does not stop spoofing; it silently blocks your own mail.

A single scan can see whether the published SPF record is valid and capable of producing an aligned pass, and whether a DKIM selector is published — configuration health, not proof that real mail authenticates; only report evidence shows that. That signal decides whether the scanner suggests quarantine or withholds a record and asks for reporting first. Reaching full enforcement, though, is not a point-in-time question — it needs roughly 30 days of aggregate (RUA) report evidence, which is why no scan path reaches reject. RFC 9989 removed the middle ground a partial pct= rollout used to offer: a conformant receiver ignores pct and applies the whole policy, so gentleness now comes from evidence rather than from partial application.

The monitoring readiness engine unlocks the next policy step only when every known sending source is at least 99% aligned over the trailing 30-day window, the unaligned share of unknown mail stays under 0.5% of volume, and the window actually contains mail. Aligned mail already passes at reject by definition, so a fully-aligned forwarder never blocks the upgrade — only the unaligned unknown share does. The record itself is generated and validated by the deterministic engine; the AI layer never writes it.

SPF

How the SPF supply-chain audit decides a domain is at risk

An SPF record delegates authority: every include and redirect hands the right to send as your domain to someone else's record, recursively. The supply-chain audit resolves that whole tree and reports who can transitively send as you — including the case where a domain you delegate to no longer exists, so a stranger could register it and become an authorized sender.

The audit walks each include and redirect against live DNS and records, per node, which mechanisms it contributes and what they cost against the 10-lookup limit. It reports four typed findings: a broken include (a target that publishes no SPF, which is a permanent error at receivers today), a +all nested anywhere in the chain (an authorization of the whole internet, wherever in the tree it hides), a delegated-to registration lapsing within 30 days, and a delegated-to domain that no longer exists in DNS. That last one is graded rather than asserted: the registry is asked as well, and only a registry that confirms the name is gone earns the words confirmed unregistered — a name whose nameservers were merely pulled answers DNS identically while still being owned, so where the registry cannot answer the finding says so instead of claiming it. It also reports one verdict-free number — the count of IPv4 addresses the tree authorizes — which is arithmetic, not a judgement. Only space a record positively authorizes is counted, so a mechanism published to exclude an address does not inflate it, and if any address lookup failed the number is flagged as a floor rather than presented as a total.

The takeover finding is raised on confirmed absence only, and confirmation has to come from a source that answers the question directly: either the name's own apex returns NXDOMAIN for both SOA and NS, meaning there is no delegation at all, or the registry's own RDAP record says it has no record of the domain. Every other outcome — a timeout, a registry that publishes no RDAP, a redacted record, an audit that ran out of registration lookups, an ambiguous answer — is reported as "could not verify registration", never as "available". Claiming a name is free when it is not is the one error that turns analysis into bad advice, so the uncertain case never becomes a finding.

The walk is bounded by a node budget and a single deadline, and a node left unresolved when the budget runs out is marked not-evaluated rather than dropped — a tree we only partly resolved says so, instead of reading as a clean one. A record large enough that even the not-evaluated markers would run past the budget has the remainder reported as a count, so nothing disappears in silence there either. As everywhere else, this surface emits no SPF fix record: it names what is delegated and to whom, and the decision about which sender to drop stays with the human who knows which ones are real.

Parked domains

Why the parked-domain pack is the one place an SPF record is emitted

A domain that sends no mail — parked, redirecting, or registered defensively — can be made unusable for spoofing with three records: a Null MX, an SPF record that authorizes nobody, and a DMARC policy that rejects. This is the single exception to SPF being diagnose-only, and it holds only because DNS Doctor verifies for itself that the domain sends nothing before emitting anything.

The SPF record here is a fixed constant — a hard-fail record that authorizes no sender at all — rather than a record composed from anything observed. There is nothing to compose, because the whole point is that no sender is authorized. That is why the carve-out is safe where general SPF generation is not: elsewhere a mechanically written SPF record can silently de-authorize a real sender, and here the premise is that there are none.

Asserting the premise is not enough. A caller must confirm the domain sends no mail, but that flag only unlocks the question — the server then re-checks DNS itself and refuses if it finds a real (non-Null) MX, an SPF record with any mechanism capable of passing, a DKIM key at any of the common selectors, or a domain that does not resolve at all. A refusal returns no records and a plain reason. This mirrors the DMARC alignment gate: the evidence is derived server-side, never accepted from the caller.

The gate is deliberately fail-closed, which is the opposite of how the scanner treats an unresolvable domain elsewhere. A lookup that temporarily fails is reported as a failure and never as a pack, because a wrong answer here stops a domain's mail. The evidence is also strictly DNS-observable: aggregate-report traffic is never consulted, even for a domain being monitored, so a refusal can never disclose whether a domain is a customer.

DMARC

Why every generated DMARC record carries np=reject

A subdomain that does not exist in DNS cannot have published SPF or DKIM records, so it can have no aligned legitimate mail — which makes np=reject (RFC 9989, DMARCbis) safe at any org-domain policy. Without it, mail claiming to come from a subdomain nobody ever created falls back to sp or p, which is often nothing at all.

Non-existent subdomains are a standard spoofing surface precisely because they are unattended: there is no zone, no records, and no owner watching. np= closes that surface separately from the policy governing real subdomains, so a domain can protect names that do not exist while still moving carefully on the ones that do.

DNS Doctor therefore emits np=reject in every record its DMARC builders produce — both a record generated from scratch and every rung of the stepped enforcement ladder, including the first, most cautious one. An np tag already present in a record is preserved exactly as published and never overwritten; the tag is only ever added where there was none. Receivers that predate RFC 9989 ignore the tag and fall back to sp/p, so adding it cannot make a record behave worse than it does today.

The parser reports the gap too: a record with no np tag whose effective subdomain policy is below reject gets an informational finding saying so. As with every other record on this site, the tag is written by the deterministic engine and the generated record is re-validated before it is returned.

Verdicts

A temporary failure is never a failure

When a DNS lookup fails transiently, DNS Doctor reports temperror — a distinct, honest state — and never a failing verdict. Telling you your domain is broken because a resolver timed out would be worse than saying nothing. The same discipline applies everywhere the truth is "we could not check" rather than "this is wrong".

temperror is excluded from the gradeable set, because a timeout is missing evidence, not a finding — it is never counted as a failure. It is not simply ignored, though: if SPF or DMARC is the check that could not be resolved, the grade is re-set to B and marked incomplete, so a missing signal never reads as a clean bill of health. DKIM is reported as "checked common selectors" — a selector we did not find is reported as not-found for the selectors we tried, never as a definitive "you have no DKIM", because DKIM selectors are chosen by the sending platform and there is no way to enumerate them all.

Blacklist checks are equally careful about what they can and cannot assert. Spamhaus is deliberately not queried through public resolvers — it returns a special code to high-volume public queries that is easy to misread as a listing — so the report carries an honest "not checked" line rather than a fabricated "blacklisted". Every check returns one of pass, warn, fail, info, or temperror, and info covers the honest "not found / unavailable" cases.

Grade

How the A–F grade is computed

The letter grade is a pure function of the check verdicts, applied by a fixed, versioned rubric (v1). SPF and DMARC carry the most weight because they decide whether spoofed mail is delivered; temporary failures are never counted as findings, and if SPF or DMARC could not be resolved the grade is set to B and marked incomplete rather than reported as if we had the evidence.

Both SPF and DMARC failing grades an F. Either one failing grades a D. Any other check failing (DKIM, MX, DNS, expiry, blacklist) grades a C. Any warning grades a B. All gradeable checks passing grades an A. temperror and info checks are excluded from the gradeable set; if SPF or DMARC ends up excluded that way, the grade becomes B and is flagged incomplete — deliberately in both directions, so a missing signal never reads as a clean bill of health and a resolver timeout never reads as a failing one.

If no check at all resolves to a gradeable verdict, there is no letter grade rather than a fabricated one — the B cap is a penalty applied to a grade, and with nothing graded there is nothing to cap. That is the state a domain with no DNS records lands in: the report says the domain does not resolve, and the grade slot is empty.

The rubric is a stable public contract — it feeds report titles and preview images — so any change to it is versioned and recorded, never a silent re-weighting.

For agents & scripts

The same deterministic engine is available programmatically: a public REST API v1 and an MCP server at /mcp. The full machine-readable contract lives at /llms-full.txt. An agent gets a validated record, never a generated guess.