12 years of challenge-response: what I learned coding an anti-spam alone
In 2014, my mail had become unlivable. I coded FrozenSpam in a weekend. Twelve years later, it still runs. Here's the real story — the one that doesn't appear in the "about" page.
Published May 23, 2026 by Emmanuel Daunizeau · 12 min read
The trigger
Summer 2014. I ran two IT companies in parallel. All my mail arrived on a Windows Server with SmarterMail. The native anti-spam filter was overwhelmed: I received 200 spams a day, and worse, I was losing client mail in the spam folder.
One Sunday evening, I found an important client brief from Thursday, by chance. I'd lost three days and almost lost the project. It became personal. Monday morning I opened Visual DialogScript (the language I co-created with Julian Moss in 1993, still my favorite tool for Windows hooks) and started coding.
The initial idea
Challenge-response existed since the early 2000s (TMDA, Spamarrest...). At the time, it was judged "heavy" by the community. But I'd read Brad Templeton (his essay on C/R principles) and was convinced that well implemented, it was the only technique with a structurally zero false-positive rate.
"Well implemented" meant:
- One challenge per sender, ONCE in a lifetime
- No challenge on whitelisted domains (newsletters, partners)
- No challenge on `noreply@`, `bounce@`, `mailer-daemon@`
- No challenge on SPF/DMARC failures (backscatter guard)
- No challenge on people I'VE written to — the central idea that existed nowhere else
The first pitfall: backscatter
The first version, released September 2014, sent a challenge to every unknown sender. Mistake. A spammer impersonated accountant@bigcompany.com. My challenge went to the real accountant. Who received my challenges for mails she hadn't sent. Backscatter.
Three weeks after launch, I got an angry mail from a client bank's IT dept: their anti-spam had blacklisted my domain for backscatter. I had to make a humble call, explain, and fix the bug in 48 hours.
The fix: verify SPF + DMARC of the sender's domain BEFORE sending a challenge. If authentication fails, silent drop. No challenge. This became FrozenSpam's "layer 4" (the backscatter guard).
The second pitfall: conversation threads
December 2014. I realize a business partner isn't receiving my replies. Why? Because when I replied to his mail, my SmarterMail server sent via a different flow than what FrozenSpam observed. FrozenSpam didn't know I'd written. So when the partner replied, FrozenSpam sent him a challenge. The partner missed it in his spam folder.
I had to hook the outbound flow of SmarterMail into FrozenSpam, to populate an outbound_log table in real-time. Anyone I've written to in the last 90 days is automatically whitelisted. This is the outbound auto-whitelist, which became FrozenSpam's signature.
Nobody else does it correctly. More articles coming soon in English; full French version: auto-WL sortants.
The third pitfall: bulk release
When a challenge is validated, what happens to the mails that arrived since from the same sender? In the first version, I only released the mail that triggered the challenge. Bad idea: a client wrote 4 mails in a row (urgent project), I replied to the first challenge, and only saw the other 3 thirty days later at the quarantine purge.
The fix: bulk release. On challenge validation, release ALL quarantined mails from the same sender. Obvious in hindsight. Took me two months to figure out.
The 2026 pivot: multi-tenant SaaS
For 11 years (2014-2025), FrozenSpam was my personal tool + that of a few SmarterMail admins I'd given it to. It ran on my own machines, with my Visual DialogScript code, never commercialized.
In 2025, two things changed:
- Spam exploded again (cf this article) — LLMs made spam profitable again.
- I gained enough perspective to think: what I built for myself isn't a private quirk. It solves a real problem for thousands of European SMBs struggling with MailInBlack-with-quote or Gmail Workspace that no longer keeps up.
I therefore ported the engine to Python on Linux, with multi-tenant isolation in PostgreSQL. The historical VDS code on Windows still serves as reference — it still runs on my personal domains.
What I'd do the same
- Code alone, slowly, over 12 years — produces a product that holds.
- Challenge-response combined with SPF/DMARC + outbound auto-WL + DNSBL — not standalone.
- Refuse to analyze mail content. Too many symbolic violations for an anti-spam.
- Keep the power to say no to undesired features. FrozenSpam does ONE thing well.
What I'd do differently
- Start on Linux from 2014 (I lost a lot depending on the Windows Server + SmarterMail closed-source combo).
- Document publicly earlier. The network effect would have been huge.
- Not wait until 2026 to port to SaaS. I let MailInBlack take the entire French market for 10 years while staying in my corner.
FrozenSpam is now publicly available. 12 years of production on my own domains, close to a million spams blocked, zero false positive. Start for €1, refundable within 15 days.