A focused security-operations aid for reviewing a domain's public SPF, DMARC, and selector-based DKIM records during phishing or business email compromise (BEC) triage.
The tool turns DNS TXT records into readable findings, preserves the raw values for analyst notes, and calls out where a result is incomplete. It supports an investigation; it does not decide whether an email is malicious.
An analyst investigating a reported message may need to understand whether its claimed sender domain publishes basic anti-spoofing controls. This project makes that public DNS review quicker and easier to document:
- SPF on the base domain
- DMARC on
_dmarc.<domain> - DKIM on
<selector>._domainkey.<domain>when a selector is known - Raw records, parsed fields, a plain-language posture label, and follow-up recommendations
The result is one evidence source alongside the original message, full headers, mail-gateway verdicts, user context, URL and attachment analysis, and the organisation's response procedures.
- Preserve the reported message and its full headers according to local policy.
- Record the visible
Fromdomain, envelope/Return-Pathdomain,Reply-Todomain, and the DKIMd=domain ands=selector when present. - Check the relevant domain here and supply the observed DKIM selector. Do not guess that DKIM is absent merely because one selector returns no record.
- Compare the published DNS posture with the message's
Authentication-Resultsheader and the mail gateway's SPF, DKIM, and DMARC verdicts. - Add the raw record evidence and limitations to the ticket, then contain or escalate based on the full set of indicators and organisational policy.
INC-2026-0147 — suspected supplier impersonation (training scenario). The visible From domain was
accounts-payable.example; the Reply-To used a different domain. The preserved message reported SPF fail, no aligned DKIM result, and DMARC fail. A public DNS review found no SPF or DMARC record and no TXT record for the observed DKIM selectormail2026. The lookup cannot prove malicious intent or rule out other DKIM selectors. Combined with the domain mismatch and urgent bank-detail change, treat as suspected BEC: preserve the original message, escalate to the incident queue, and verify the request with the supplier through a known contact channel before taking action.
All domains, people, and identifiers in this example are synthetic.
- Next.js App Router, TypeScript, and CSS Modules
- Local/server deployment: Node runtime route at
POST /api/analyze-domainwith DNS TXT lookups throughnode:dns/promises - GitHub Pages deployment: static export with browser-side DNS-over-HTTPS
- Input normalization and validation before lookup
- Selector-specific DKIM checks without claiming automatic selector discovery
- Focused Vitest coverage for normalization, parsing, and posture scoring
- GitHub Actions checks lint and tests, builds
out/, and deploys the live demo
The workflow diagram above shows the input, lookup, parsing, and review path.
POST /api/analyze-domain
Request body:
{
"domain": "example.com",
"dkimSelector": "google"
}domainis required.dkimSelectoris optional.- A pasted URL is normalized to its hostname when possible.
- The interface is designed around bare domains.
The normal Next.js build uses the local API route. GitHub Pages cannot run that route, so its static build selects browser-side DNS-over-HTTPS:
GITHUB_PAGES=true NEXT_PUBLIC_DNS_LOOKUP_MODE=doh npm run buildOn Windows PowerShell, use:
$env:GITHUB_PAGES="true"
$env:NEXT_PUBLIC_DNS_LOOKUP_MODE="doh"
npm run build- Public DNS TXT records are the only data source examined by this MVP.
- SPF and DMARC use predictable DNS names; DKIM requires a known selector.
- The analyst obtains message headers and gateway verdicts elsewhere.
- A posture label is triage guidance, not proof of authenticity, compromise, deliverability, or malicious intent.
- DNS changes over time, so a result describes the resolver response at the time of the check.
- The tool does not ingest an email, parse
Authentication-Results, inspect links or attachments, or verify mailbox-level delivery. - A published SPF record does not prove that every legitimate sender is covered or that a specific message passed SPF with identifier alignment.
- A public DKIM key does not prove that a specific message has a valid signature. No result for one selector does not prove that DKIM is absent.
- A correct-looking DNS posture does not rule out display-name spoofing, lookalike domains, compromised mailboxes, forwarding effects, or operational misconfiguration.
- DNS-over-HTTPS requests from the public demo are sent to its configured resolver. Do not submit a domain if that disclosure is unacceptable.
- Resolver timeouts and errors remain inconclusive findings rather than failures of the domain's security controls.
See SECURITY.md for the project's disclosure scope and safe-use expectations.
- SPF: existence, raw record, includes,
ip4,ip6, and finalall - DMARC: existence, raw record,
p,rua,ruf,pct,adkim, andaspf - DKIM: selector-based lookup only; no generic selector discovery
- Recommendations: next steps for missing, weak, ambiguous, or inconclusive records
Reviewer-friendly sample inputs are in
docs/sample-domains.json. Live DNS answers may
change after these examples are published.
npm install
npm run devOpen http://localhost:3000.
npm run lint
npm run test
npm run build
npm auditManual smoke test:
- Start the app with
npm run dev. - Open
http://localhost:3000. - Analyze
example.comwith DKIM selectorgoogle. - Confirm raw results, posture notes, and recommendations render without an application error. DNS answers may change, so do not hard-code a security conclusion from this live domain.
- Submit an invalid value such as
not a domain!and confirm validation blocks the lookup.
- API route tests with mocked DNS responses
- Optional import of sanitised message-header fields for side-by-side comparison
- A short screen recording of the analyst workflow



