Mohamed ARKID logo

Command Palette

Search for a command to run...

Command Palette

Search for a command to run...

Case study

MailProof: getting moarkid.com to send authenticated email

A domain that could receive email but not send it reliably, a tool built to find out why, and the before and after on my own domain.

Client
My own domain, moarkid.com (not a paid engagement)
Stack
Python, FastAPI, Docker Compose, Caddy, GitHub Actions
Dates
Before 2026-10-03, after 2026-10-04
  • 10/10

    mail-tester score

  • Pass

    SPF, DKIM and DMARC in Gmail

  • Inbox

    Gmail delivery, not spam

  • 0

    blocklists listing the sending IP

Every result on this page was recorded in the MailProof repository (docs/case-study-moarkid.md) on the dates shown. This was my own domain, not a client's.

The problem

On 2026-10-03, moarkid.com could receive mail through Cloudflare Email Routing, but nothing was set up to send mail as the domain. There was no DMARC record and no DKIM key, so a receiving server had no way to confirm that a message from hello@moarkid.com was genuine. Lead emails from this site go out through that address, so that had to change.

The first MailProof report on the domain said exactly that:

[HIGH] No DMARC record
    No record at _dmarc.moarkid.com.
[WARN] No DKIM key at any of 21 common selectors
[INFO] SPF record found
    1 of 10 DNS lookups used. Authorized through: Cloudflare Email Routing.

Constraints

  • Read only what DNS says. A check has to report facts read from DNS, never generated text, so a finding can be checked by anyone with dig.
  • DNS cannot list DKIM selectors. The tool can only try common ones (21 of them), so a miss is not proof that DKIM is absent, and the report has to say so.
  • SPF stops at 10 DNS lookups. Receivers fail a record past that limit, so the tool has to follow every include and count.
  • Some networks block DNS over TCP. Large TXT answers do not fit in UDP, and the standard retry over TCP port 53 fails on those networks.
  • One small server. The public checker runs on a single host with Docker Compose and Caddy, so it needs rate limiting and must survive a reboot without manual steps.

Approach

  1. Built the checker first. A command line tool and an HTTP API that read SPF, DMARC, MX and DKIM for a domain and return a list of findings, each with a severity and the fix. Bounce messages from Microsoft and Gmail are decoded into a cause and a fix, with the source of each rule recorded.
  2. Ran it on moarkid.com and fixed what it reported, one finding at a time:
    • published DMARC at _dmarc.moarkid.com with p=none and reports to dmarc@moarkid.com, which cleared the HIGH finding;
    • chose Resend as the sending service (it sends through Amazon SES in eu-west-1) and published its DKIM key at resend._domainkey.moarkid.com, plus two CNAME records for the return path;
    • left the root SPF record as it was. Resend uses rsend.moarkid.com as the return path, which carries its own SPF record, and SPF still aligns for DMARC because both names share the organizational domain.
  3. Tested delivery from outside: a message to Gmail and one to mail-tester.com, both on 2026-10-04.

Key decisions

  • Findings are structured data, not prose (decision 001). Every check returns a list of findings with a code, severity, title, detail and fix. The command line, the API and the web page all read that list, and the tool works without any language model.
  • Retry over DNS-over-HTTPS (decision 002). A failed lookup is retried once through a public DoH resolver, because TCP port 53 is blocked on some networks. The trade-off is that the queried name goes to that resolver, so the retry can be turned off.
  • Run independent lookups at the same time (decision 003). A check made about 25 lookups in a row, which took 3 to 4 seconds on the live site. The four checks, the 21 DKIM selectors and each level of the SPF include tree now go out together. Replaying real answers for gmail.com, stripe.com, moarkid.com and shopify.com at 120 ms per lookup took 0.24 to 0.37 seconds, against 3.1 to 3.9 seconds before, with identical output. A test fails if those lookups ever run one after another again.
  • Cap the SPF tree at 50 includes. Without a limit, a hostile zone where each include names ten more would turn one check into millions of lookups. Receivers stop at 10, so 50 still shows every tree that matters.
  • Rate limit the public API. Each client address gets 10 requests per minute across checks and bounce decoding, then a 429 with Retry-After.

What went wrong, and the fix

On 2026-10-05, before a planned reboot test, I found that ports 80 and 443 were open in the running firewall but missing from /etc/iptables/rules.v4, the file the firewall loads at boot. The next reboot would have taken the site down with healthy containers behind it. I added both rules to the file, validated it with iptables-restore --test, and rebooted with nothing done by hand afterwards. /healthz returned 200 from outside and HTTP redirected to HTTPS. The write-up is incident 001 in the repository.

Verified results

Recorded on 2026-10-04:

  • MailProof: exit code 0. The HIGH finding (no DMARC) and the DKIM warning were gone.
  • Gmail "Show original": dkim=pass for moarkid.com, spf=pass for the rsend.moarkid.com return path, dmarc=pass. The message arrived in the inbox, not spam, over TLS 1.3. Both SPF and DKIM align, so DMARC would still pass if either failed.
  • mail-tester.com: 10/10. SpamAssassin scored the message -0.1 against a threshold of 5.0, and the sending IP was on no blocklist, with reverse DNS matching its HELO name.

The engineering behind the live checker, checked on 2026-10-20:

  • 124 automated tests pass, with no network needed.
  • Every push to main that passes CI deploys to the server; the server never holds GitHub credentials.
  • An hourly job checks the live site from outside (/healthz over HTTPS, and the HTTP redirect), which catches a broken certificate, DNS record or firewall rule that the deploy check cannot see.

What I would do next

  • Move DMARC from p=none to quarantine once the aggregate reports show every legitimate sender passing. Until then, spoofed mail is still delivered.
  • Rotate to a 2048-bit DKIM key. Resend issued a 1024-bit key; 2048-bit is the current recommendation.
  • Read pasted email headers, for the case where every DNS check passes and mail still fails.
  • Add the blocklist check to MailProof itself. Today the blocklist result above comes from mail-tester, not from the tool.
  • Record the time taken. The repository's case study leaves it blank, so I make no claim about it here.

Want the same approach on your AWS bill? The AWS Cost Audit is $250 fixed, delivered in 48 hours.