We require all those who participate in this repo to agree and adhere to the Mozilla Community Participation Guidelines.
Small utilities for checking DNS records.
Licensed under MPL-2.0. Contributing (especially adding a DNS
provider)? See CONTRIBUTING.md.
https://thunderbird.github.io/dns-scripts/
The same checks as the verify_thundermail_dns.py CLI below, but from a web
browser: enter a domain, optionally pick your DNS provider, and get pass/fail per
record plus exactly what to enter for any that are missing. All lookups run
client-side via DNS-over-HTTPS (Cloudflare or Google) — no server, nothing stored.
Run it locally with any static file server:
python3 -m http.server 8000 # then open http://localhost:8000/Single source of truth: the record set, value templates, and per-provider remediation strings live in
records.json, which is read by both the CLI and the web page (index.html+app.js). Add a record or a provider there once and both front-ends pick it up.
Managed with uv:
uv syncVerifies that a domain's DNS is configured for Thundermail: the MX record, the JMAP/CalDAV/CardDAV/IMAPS/submission SRV records, the SPF / MTA-STS / TLSRPT / DMARC TXT records, and the three DKIM CNAMEs.
uv run verify_thundermail_dns.py glamrocnamecheap.comExit status is 0 only when every expected record is present and correct, so it's
safe to use in scripts or CI. One deliberate exception: the SRV weight is not
compared. Priority, port and target must match exactly, but any weight passes,
because weight only distributes load between competing targets at the same priority
and Thundermail publishes a single target per service — while some panels (Plesk /
METANET) offer only a fixed dropdown of weights in steps of 5, making the documented
1 unenterable. See #14. It queries 1.1.1.1 by default (override with
--resolver or the DNS_RESOLVER environment variable) to avoid stale local
caches. The resolver accepts an IP address or a hostname — e.g. point it at an
authoritative nameserver to bypass public-resolver caching entirely:
uv run verify_thundermail_dns.py glamrocnamecheap.com --resolver dns1.registrar-servers.comPoint it at whichever nameserver is actually authoritative for the domain — check
with dig NS <domain> first, since a registrar may run several nameserver sets and
querying the wrong one just returns empty answers. For example, a domain hosted on
Cosmotown's DNS is usually delegated to ndns1.cosmotown.com / ndns2.cosmotown.com
(Cosmotown: What are Cosmotown's nameservers?):
uv run verify_thundermail_dns.py <domain> --resolver ndns1.cosmotown.comA domain hosted on Bunny DNS is delegated to kiki.bunny.net / coco.bunny.net
(bunny.net: DNS records):
uv run verify_thundermail_dns.py <domain> --resolver kiki.bunny.netPass --provider to print, for each failing record, exactly what to enter in
that DNS provider's control panel — including provider-specific quirks such as how
the Host/Name field is written. Supported: namecheap, squarespace, cosmotown,
bunny, spaceship, godaddy, ionos, ovh, hover, digitalocean, porkbun,
metanet-plesk, generic. bunny.net's add-record form has a single
Value field (no separate Priority/Weight/Port), so the bunny MX/SRV output puts
the whole record string in Value; the Hostname field is left empty for the root
(bunny.net: DNS records). spaceship
is unverified — its field conventions were inferred from a zone-import screenshot
and Spaceship's own Spacemail docs (a different record set), not confirmed against
the live manual add-record form, so its headers flag this and the SRV host handling
is uncertain (see RELEASE_NOTES.md). godaddy is also
unverified — its field conventions come from GoDaddy's help docs plus a screenshot
of the live SRV add-record form, not yet confirmed end-to-end on a live GoDaddy-hosted
domain, so its headers flag this. GoDaddy is the one panel that splits the SRV
_service._protocol label into separate Service and Protocol fields
(the record Name is then @ for the root). ionos is also unverified — its
field conventions come from IONOS's help docs plus a screenshot of the live SRV
add-record form, not yet confirmed end-to-end on a live IONOS-hosted domain, so its
headers flag this. IONOS also splits the SRV _service._protocol label, but its
Protocol is a TCP/UDP/TLS dropdown (hard-coded to TCP, since every checked
SRV record is _tcp), and its SRV form has both a Host name (record location)
and a separate Points to (target). ovh is also unverified — its field
conventions come from OVHcloud's help docs plus a screenshot of the live TXT/SPF
add-entry wizard, not yet confirmed end-to-end (the MX/SRV wizards weren't
screenshotted), so its headers flag this. OVH's quirks: the Sub-domain field is
left empty for the root (not @), and FQDN targets need a trailing dot (OVH
appends your domain otherwise), so the ovh MX/SRV/CNAME output ends targets with a
.. hover is also unverified — its SRV field layout comes from a screenshot of
the live Edit DNS Record form, but the MX/TXT/CNAME field labels come from Hover's
Managing DNS Records at Hover docs
(not a live add-record screenshot), so its headers flag this. Hover splits the SRV
_service._protocol label into separate Service and Protocol fields (like GoDaddy/IONOS),
but its optional Subdomain field is left empty for the root (not @ — the hover
SRV output uses a blank subdomain, driven by a {srvsubhost} token); MX/TXT use @ for the
root. digitalocean is also unverified — its SRV field layout comes from a screenshot
of the live Create a record dialog, while the MX/TXT/CNAME field labels come from
DigitalOcean's Create, Edit, and Delete DNS Records
docs (not a live add-record screenshot), so its headers flag this. DigitalOcean keeps the
whole SRV _service._protocol label in one Hostname field (it does not split it like
GoDaddy/IONOS/Hover), uses @ for the root (MX/TXT), and appends your domain to any
target entered without a trailing dot — so the digitalocean MX/SRV/CNAME output ends
targets with a . (like ovh). This trailing-dot behaviour is confirmed live on
digitalocean.example.com, whose SRV targets were entered without the dot and came back as
mail.thundermail.com.digitalocean.example.com. porkbun is also unverified — its MX/TXT/CNAME
field layout comes from Porkbun's KB article
How to Add DNS Records
and its SRV layout from the
SRV record guide
(screenshots, but not confirmed end-to-end on a live panel), so its headers flag this.
Porkbun (Cloudflare-backed) uses a single Add-record dialog with a Type dropdown: the
Host field is left blank for the root (not @), MX and SRV have a separate
Priority field, and SRV keeps the whole _service._protocol label in one Host with
Weight/Port/Target packed into the Answer field. Unlike ovh/digitalocean, targets do
not need a trailing dot — confirmed live on porkbun.example.com, whose MX and DKIM CNAME
targets came back verbatim (Porkbun stores them as entered). metanet-plesk is also
unverified — it covers METANET's Plesk DNS panel
(Websites & Domains → DNS-Einstellungen → Eintrag hinzufügen), whose SRV field layout
is confirmed from a screenshot of the live form on a METANET-hosted zone, while the
German MX/CNAME field labels come from Plesk's docs rather than a live add-record form,
so its headers flag this
(METANET: Plesk DNS-Verwaltung).
The panel is German-language, so the emitted field labels are German with an English
gloss (Zielhost (Target host)). Plesk's quirks: Domainname is left empty for the
root (never @); it splits the SRV label into Service-Name and Protokoll like
GoDaddy/IONOS/Hover but wants them without the leading underscore (imaps, tcp —
the {bareservice}/{bareprotocol} tokens exist for this); Priorität and Relative
Gewichtung are dropdowns rather than free text, and the weight dropdown only offers
0/5/10…50, so Thundermail's weight of 1 cannot be entered (hence the ignored-weight
matching above, and #14);
targets are stored verbatim with no trailing dot (Plesk adds the root dot itself —
confirmed live); TXT values are auto-quoted by the panel, so they're emitted
unquoted (like cosmotown); and nothing goes live until you click Aktualisieren
(Update) on the pending-changes banner. METANET also sells a paid Premium Service:
Individuelle DNS-Einstellungen where support enters the records for you — the emitted
headers mention it as a fallback. Note that
Cosmotown's customer panel has no SRV section, so the five SRV
records can't be self-served — the cosmotown output routes you to Cosmotown support
for those. See Cosmotown's docs for
changing/saving DNS records,
adding a host name,
and updating the MX record.
uv run verify_thundermail_dns.py glamrocnamecheap.com --provider namecheapFor example, a missing DKIM CNAME prints:
• CNAME tm1._domainkey
Namecheap → Advanced DNS → Add New Record → CNAME Record:
Host: tm1._domainkey
Value: tm1.glamrocnamecheap.com.dkim.thunderhosted.com (no trailing dot)
Verifying Thundermail DNS for glamrocnamecheap.com (resolver 1.1.1.1)
MX:
OK @ 10 mail.thundermail.com
SRV:
OK _jmap._tcp 0 1 443 mail.thundermail.com
...
Result: 13 passed, 0 failed.