Summary
- A BIMI certificate renewal turned into a same-day DNS audit - on 2026-07-02 we swept SPF, DKIM and DMARC across all 11 Cloudflare zones we own, including typo-squat domains registered defensively, and fixed everything we found the same day
- tallyfy.net published two DMARC records at once - which RFC 7489 treats as no DMARC at all, since policy discovery terminates. Our strictest-looking domain had no working policy, plus a duplicate SPF record that guaranteed a permerror under RFC 7208
- tallyfy.ai was an active Cloudflare zone with zero DNS records - anyone could send mail claiming to be us. Two records fixed it: SPF
v=spf1 -alland DMARCp=reject - Four DMARC report vendors had accreted across our zones - Cloudflare, DMARC Digests, Red Sift OnDMARC and Postmark. Forensic reports still streamed to a service cancelled on 2026-06-15. DNS rots the same way unread audit trails do
On 2026-07-02 we did something we should have done years ago: we read every DNS zone we own, record by record.
The trigger was our BIMI certificate. The live one expires on 2026-07-28, the replacement was mid-validation at DigiCert, and switching BIMI vendors had already made June a DNS-heavy month for us. Since we were staring at zone files anyway, we widened the sweep to every zone in the Cloudflare account. Eleven of them: tallyfy.com and tallyfy.net, product domains like tallyfy.ai and tallyfy.email, a billing domain, plus typo-squats like tallify.com and tallifi.com that we registered so nobody else could.
Four of the 11 needed fixes, and not cosmetic ones. Our strictest-looking domain was publishing two DMARC records at once, which receivers respond to by ignoring both. A live, delegated zone had zero DNS records and was spoofable by anyone with an SMTP client. And forensic reports about our mail were still streaming to a vendor we cancelled on 2026-06-15.
Everything got found and fixed inside one day, which tells you how cheap these fixes are once someone actually looks.
One thing that surprised us: the problems clustered on the domains we never think about. Nobody rereads their own DNS. We certainly hadn’t.
Two of everything on tallyfy.net
tallyfy.net routes our API traffic, so it gets treated as serious infrastructure. Its email records looked the part too: reject policies with strict alignment flags. Then we listed the TXT records at _dmarc.tallyfy.net and got two of them:
"v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s;"
"v=DMARC1; p=reject; pct=100; rua=mailto:re+rrqkq1shnu5@dmarc.postmarkapp.com,mailto:re+e6bad03e23e5@inbound.dmarcdigests.com; sp=reject; aspf=r;"
Both say reject. Neither counted. RFC 7489 section 6.6.3 is blunt about duplicates: “If the remaining set contains multiple records or no records, policy discovery terminates and DMARC processing is not applied to this message.” Two DMARC records don’t average out to your strictest policy. They cancel to no policy at all. Receivers had been treating our hardest-locked zone as a domain with no DMARC whatsoever.
How long had that been true? We can’t say exactly, and that’s sort of the point: no resolver complains about a duplicate TXT record, and none of our dashboards did either. We deleted the first record and kept the second, and the choice had nothing to do with strictness. The deleted record carried stricter alignment (adkim=s; aspf=s) but no rua tag, so it produced zero reports and zero visibility. The kept record ships aggregate reports to Postmark and DMARC Digests. When two candidates both say p=reject, keep the one that tells you what receivers are seeing. Funnily enough, the stricter-looking record was the one that had to go.
Then we scrolled to the apex and found the same disease in SPF form:
"v=spf1 -all"
"v=spf1 include:_spf.google.com ip4:54.240.112.240 -all"
The first record claims this domain sends no mail, ever. The second authorizes Google Workspace plus one Amazon SES address. RFC 7208 section 4.5 settles the contradiction without mercy: “If the resultant record set includes more than one record, check_host() produces the ‘permerror’ result.” Permanent error. Any receiver doing strict SPF evaluation on tallyfy.net mail was being told our SPF is broken, full stop.
The keep decision took about thirty seconds of evidence. tallyfy.net has live Google Workspace MX records (aspmx.l.google.com plus four alternates), so mail really does arrive at @tallyfy.net addresses and plausibly leaves from them, and 54.240.112.240 belongs to Amazon SES. Keeping the permissive record can’t break legitimate mail. Keeping v=spf1 -all could. We kept the permissive one and deleted the lie.
At some point someone decided tallyfy.net sends no mail, someone else decided it does, and DNS stored both opinions side by side, for who knows how long.
There’s no compiler for a zone file.
An empty zone and a zombie record
tallyfy.ai should have been the boring one. We registered it, delegated it to Cloudflare, and forgot it existed. On 2026-07-02 we pulled its record set and the API returned an empty list. Zero records. No SPF and no DMARC, not even an MX.
A live, delegated, empty zone is worse than it sounds. Anyone could send mail claiming to be billing@tallyfy.ai, and receiving servers would find no policy to check it against. The mail wouldn’t arrive pre-trusted, but nothing we published could mark it as forged either. Domains you own defensively are exactly the ones attackers like, because the owner never sends from them and rarely looks at them.
The fix took two TXT records, the standard parked-domain treatment:
tallyfy.ai TXT "v=spf1 -all"
_dmarc.tallyfy.ai TXT "v=DMARC1; p=reject; sp=reject;"
The SPF record declares that no host anywhere is authorized to send for this domain. The DMARC record tells receivers what to do with mail claiming otherwise: reject it, and reject it for every subdomain too, which is what sp=reject covers.
Five minutes of work, and tallyfy.ai went from fully spoofable to as locked as a non-sending domain gets.
tallyfy.email had the opposite problem: a leftover record that said too much. Sitting in its zone was this:
default._bimi.tallyfy.email TXT "v=BIMI1; l=https://tallyfy.com/wp-content/uploads/tallyfy_inc_icon.svg;a=;"
Count the failures packed into one 74-byte value. The l= tag points at a WordPress upload URL that died when we moved tallyfy.com off WordPress. The a= tag, which should point at a verified mark certificate, is empty. And the domain’s DMARC sits at p=none, which Google’s BIMI requirements as of July 2026 rule out plainly: “The policy option (p) must be set to quarantine or reject. BIMI doesn’t support DMARC policies that have the p option set to none.” A logo record pointing at a dead file with no certificate, on a domain where the logo could never display anyway, so we deleted it. The resolution chain that record was failing to satisfy is covered in how BIMI resolves, and whether the checkmark pays for itself is its own discussion.
What we deliberately didn’t change on tallyfy.email matters just as much. Its DMARC stays at p=none for now because the domain family actively sends: the apex SPF includes mailgun.org, and the crm., outbound-email. and newsletter. subdomains carry live Mailgun, Amazon SES and Google DKIM keys. Jumping an active sender straight to p=reject is how you torch your own mail. The plan we wrote down instead: watch the Postmark aggregate reports until every legitimate source shows aligned, move to p=quarantine, watch again, then go to p=reject. The ratchet takes months. That’s the point of it.
The decision tree we now apply to every domain we own is small enough to draw:
What else did we find on tallyfy.com itself?
The apex gets the most eyeballs, so we expected a clean bill there, and mostly we got one. DMARC sits at p=reject with pct=100 and SPF ends in -all. The BIMI record resolves and serves. The problems were smaller and quieter, and they say more about how DNS rots than any single broken record does. Our DMARC record still carried a ruf tag pointing at inbox.ondmarc.com, which belongs to Red Sift OnDMARC, a service we cancelled on 2026-06-15. Seventeen days after cancellation, per-message failure reports about our mail were still flowing to a vendor we’d stopped paying. Here’s the record before and after the 2026-07-02 edit:
BEFORE: "v=DMARC1; p=reject; sp=reject; rua=mailto:cf58ddc3b39843d4bc555830361dd0ca@dmarc-reports.cloudflare.net,mailto:re+9d471d5a524d@inbound.dmarcdigests.com; ruf=mailto:ef373732@inbox.ondmarc.com; aspf=r; pct=100"
AFTER: "v=DMARC1; p=reject; sp=reject; rua=mailto:cf58ddc3b39843d4bc555830361dd0ca@dmarc-reports.cloudflare.net,mailto:re+9d471d5a524d@inbound.dmarcdigests.com; aspf=r; pct=100"
While tallying report destinations we noticed the bigger pattern: four different DMARC monitoring vendors across our zones. Cloudflare DMARC Management and DMARC Digests take aggregate reports for tallyfy.com. Red Sift OnDMARC was taking the forensic ones. Postmark takes reports for tallyfy.net and tallyfy.email. Who picked that four-vendor stack? Nobody picked it. It accreted, one well-intentioned setup at a time, and now each vendor sees a slice of our mail flow while no single dashboard sees all of it.
The DKIM selector list was the most educational part of the whole audit. Every _domainkey entry on tallyfy.com is a fossil of some service that sent mail for us at some point: google for Google Workspace; six Mailgun selectors (mx, mx.app, mx.staging, mailo.mail.app, smtp.announcements, k1.outreach); mailjet for Mailjet; three CNAMEs into dkim.amazonses.com for Amazon SES; cf2024-1 from Cloudflare in 2024; and fpn for FirstPromoter. Thirteen selector entries across four active sender platforms, all riding one apex SPF record:
"v=spf1 mx include:_spf.google.com include:mailgun.org include:spf.mailjet.com ip4:54.240.112.240 -all"
That record works today. It also lives close to a hard ceiling. RFC 7208 section 4.6.4 caps SPF evaluation at 10 DNS lookups: “SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation, to avoid unreasonable load on the DNS. If this limit is exceeded, the implementation MUST return ‘permerror’.” The mx term costs a lookup, every include costs a lookup, and includes expand recursively into more of them. Four sender platforms on one apex spends most of that budget. Whoever signs up sender platform number five, years from now, might push the whole record into permerror without touching anything that looks risky.
Two smaller notes from the same zone. We have TLS failure reporting wired up through Mailhardener (_smtp._tls.tallyfy.com is a CNAME into their endpoint) but no MTA-STS policy published, so we built the reporting half of a pair and skipped the policy half. And our sending subdomains, app, staging, announcements and outreach among them, sign with their own Mailgun DKIM keys but publish no _dmarc or default._bimi of their own, so they inherit the org-level sp=reject and their BIMI display rides the org-domain fallback record. Both of those are defensible defaults. Neither was ever an actual decision until we wrote them down this week.
What caught us off guard was our own API token. The scoped Cloudflare token we use for reads could enumerate every zone and every record, but each write attempt came back with Authentication error (10000). Annoying, mid-cleanup. All the fixes went out through the legacy Global API Key instead. A scoped token that can see everything and change nothing is exactly what you want. Least-privilege protects you right up until you’re the one doing the cleanup.
Run this audit on your own domains
None of this needed tooling. The audit was dig plus the Cloudflare API, and the whole read took a day. These are the checks we ran per domain, copy-paste ready:
# DMARC: expect EXACTLY one record
dig +short TXT _dmarc.example.com
# SPF: expect exactly one string starting v=spf1
dig +short TXT example.com | grep spf1
# Does mail flow here at all? MX answers the park-or-keep question
dig +short MX example.com
# BIMI: if a record exists, fetch the l= and a= URLs and expect HTTP 200
dig +short TXT default._bimi.example.com
# TLS-RPT and MTA-STS: should exist as a pair or not at all
dig +short TXT _smtp._tls.example.com
dig +short TXT _mta-sts.example.com
# DKIM: spot-check the selectors your senders use
dig +short TXT google._domainkey.example.com
The single test that catches the tallyfy.net class of bug: dig +short TXT _dmarc.example.com returning more than one line starting v=DMARC1 means your DMARC is void.
And the checklist we intend to rerun every July:
- Inventory every domain you own, across every registrar and DNS account, including typo-squats and products that never shipped (ours surfaced 11 zones).
- Split them into senders and non-senders: MX records and DKIM selectors don’t lie; memory does.
- Harden non-senders straightaway with the two parked-domain records,
v=spf1 -allandv=DMARC1; p=reject; sp=reject;. - For senders, enumerate before you enforce: list every DKIM selector, read a month of aggregate reports, then ratchet toward reject one step at a time. The reports are your source list; guessing is not.
- Count records, because more than one
_dmarcTXT record, or more than onev=spf1string, is silently fatal. - Delete anything that references something dead, like a cancelled vendor or a URL that now 404s.
- Write down who receives your
ruaandrufreports, and ask whether you’d notice if they stopped arriving. Ours had been going to a cancelled account for 17 days.
The deeper lesson is a bit uncomfortable for a company that sells process software. DNS gets written once, at setup, by whoever did the setup, and then it rots in the dark. A zone file survives job changes and platform migrations without ever being reread. The domains you bought defensively are the purest case: they exist so attackers can’t have them, and then nobody guards them. We document the security posture all of this feeds into on our compliance page, and the fix for the rot is the same discipline we sell as process audit software: checks that recur on a schedule instead of living in someone’s memory. A yearly sweep might have caught the tallyfy.net duplicates before we did, and we plan to repeat the 2026-07-02 sweep every July. You don’t need a product for this one, just dig, a day, and a reminder that fires in a year.
Recurring beats heroic.