⛔ 2026-08-21 — do NOT raise enforcement yet. Two blockers, one of them undocumented until now.
1. The evidence step cannot be completed. Step 1 below says "read a few days of rua
reports". Searching admin@'s mailbox for the last 30 days returns exactly one aggregate
report — Google, Report-ID: 9866291112064302175, covering a single day (2026-08-16→17). One
day of reports from one receiver is not a basis for enforcing a policy on a domain that sends
payment receipts.
2. #368 is open, and it is a hard dependency this ticket does not name. Replies to
admin@scripthammer.com still leave through a personal Gmail. Gmail's outbound is not in
the root SPF (which lists only Cloudflare, for inbound) and a plain send-as does not DKIM-sign
as d=scripthammer.com. So those replies fail DMARC alignment today — harmlessly, because
p=none. Under p=quarantine the maintainer's own replies start going to spam.
That is the concrete failure a pct=25 rollout is supposed to surface slowly, except here it
is known in advance and free to avoid: close #368 first, then let reports accumulate, then
raise.
Live DNS re-verified 2026-08-21 — all four records present and correct: DMARC p=none
with rua to admin@, root SPF including Cloudflare, resend._domainkey DKIM key present,
MX on route1/2/3.mx.cloudflare.net. Nothing has drifted since 2026-08-19.
What was delivered instead: the assertion this ticket asks for under "Not automatable from
this repo". scripts/ci/check-mail-policy.mjs now checks the LIVE zone against an INTENDED
policy declared in the repo, so raising p= becomes a one-line reviewable change that CI
enforces — rather than an undocumented dashboard edit nothing records. It fails on a missing
DMARC record, a policy that does not match the declared intent, a missing DKIM key, a missing
SPF, and a missing MX. That last one matters for #881: if inbound routing breaks, admin@
stops receiving and the security policy silently starts dropping vulnerability reports again.
_dmarc.scripthammer.com is p=none — monitor-only. Nothing is enforced. Anyone can
send mail claiming to be @scripthammer.com today and receivers will deliver it; DMARC
only reports it afterwards.
That matters more here than for a brochure domain: this one sends payment receipts and
signup confirmations. A spoofed "your payment failed, re-enter your card" from your own
domain passes every visual check a customer makes.
Verified DNS state, 2026-08-19 (read over DoH)
| record |
value |
role |
_dmarc.scripthammer.com TXT |
v=DMARC1; p=none; rua=mailto:admin@scripthammer.com |
no enforcement |
scripthammer.com TXT |
v=spf1 include:_spf.mx.cloudflare.net ~all |
Cloudflare |
scripthammer.com MX |
route1/2/3.mx.cloudflare.net |
inbound Email Routing |
send.scripthammer.com TXT |
v=spf1 include:amazonses.com ~all |
Resend's sending subdomain (SES underneath) |
resend._domainkey TXT |
RSA public key present |
Resend DKIM-signs d=scripthammer.com |
The authentication itself is correct, and that is worth stating because the root SPF
looks wrong at a glance: it lists only Cloudflare, which handles inbound. Resend does not
need to be there — it sends from the send. subdomain with its own SPF and DKIM-signs
with d=scripthammer.com, which aligns with the From:. DMARC passes if either aligns, so
transactional mail authenticates. Do not "fix" the root SPF by adding Resend to it.
What to do
The standard path is none → quarantine → reject, and the daily aggregate reports are
the evidence for each step. Unlike some monitoring, these reports will genuinely tell us
something, because real mail is flowing through Resend right now.
- Read a few days of
rua reports and confirm every legitimate sender aligns — Resend,
and anything else that turns out to be sending (that is what the reports are for).
- Move to
p=quarantine; pct=25, watch, then raise pct.
- Move to
p=reject.
Also worth adding at step 1: ruf= (forensic reports) is absent, and sp= (subdomain
policy) is unset, so send.scripthammer.com inherits whatever the root becomes.
Not automatable from this repo
The DMARC record lives in Cloudflare DNS, not in this tree — the same shape as the cache
rules in #635, where nothing in the repo would notice if the config were lost. If this is
tightened, it is worth a smoke.yml-style assertion that the published policy is still
what we intend, for the same reason check-cache-headers.mjs exists.
Prompted by a routine Google aggregate report landing in admin@ on 2026-08-19 — which is
the system working, not a fault.
Related: #781 (registrar still Squarespace), #368 (admin@ send-as), #635 (Cloudflare config
living outside the repo).
_dmarc.scripthammer.comisp=none— monitor-only. Nothing is enforced. Anyone cansend mail claiming to be
@scripthammer.comtoday and receivers will deliver it; DMARConly reports it afterwards.
That matters more here than for a brochure domain: this one sends payment receipts and
signup confirmations. A spoofed "your payment failed, re-enter your card" from your own
domain passes every visual check a customer makes.
Verified DNS state, 2026-08-19 (read over DoH)
_dmarc.scripthammer.comTXTv=DMARC1; p=none; rua=mailto:admin@scripthammer.comscripthammer.comTXTv=spf1 include:_spf.mx.cloudflare.net ~allscripthammer.comMXroute1/2/3.mx.cloudflare.netsend.scripthammer.comTXTv=spf1 include:amazonses.com ~allresend._domainkeyTXTd=scripthammer.comThe authentication itself is correct, and that is worth stating because the root SPF
looks wrong at a glance: it lists only Cloudflare, which handles inbound. Resend does not
need to be there — it sends from the
send.subdomain with its own SPF and DKIM-signswith
d=scripthammer.com, which aligns with theFrom:. DMARC passes if either aligns, sotransactional mail authenticates. Do not "fix" the root SPF by adding Resend to it.
What to do
The standard path is
none→quarantine→reject, and the daily aggregate reports arethe evidence for each step. Unlike some monitoring, these reports will genuinely tell us
something, because real mail is flowing through Resend right now.
ruareports and confirm every legitimate sender aligns — Resend,and anything else that turns out to be sending (that is what the reports are for).
p=quarantine; pct=25, watch, then raisepct.p=reject.Also worth adding at step 1:
ruf=(forensic reports) is absent, andsp=(subdomainpolicy) is unset, so
send.scripthammer.cominherits whatever the root becomes.Not automatable from this repo
The DMARC record lives in Cloudflare DNS, not in this tree — the same shape as the cache
rules in #635, where nothing in the repo would notice if the config were lost. If this is
tightened, it is worth a
smoke.yml-style assertion that the published policy is stillwhat we intend, for the same reason
check-cache-headers.mjsexists.Prompted by a routine Google aggregate report landing in
admin@on 2026-08-19 — which isthe system working, not a fault.
Related: #781 (registrar still Squarespace), #368 (admin@ send-as), #635 (Cloudflare config
living outside the repo).