MailRadar 📡
Know your email security posture — before attackers do.
MailRadar is an open-source command-line tool that audits the email security posture of a domain. It reads the domain's public DNS records and related endpoints, checks DMARC, SPF, DKIM, BIMI/VMC, MTA-STS and TLS-RPT, looks for a published GPG key for the domain's security contacts, and computes a score from 0 to 100.
It can also turn the result into a ready-to-send report (Italian or English) for the domain owner, and deliver it by email, GPG-encrypted when the recipient has a public key.
MailRadar only performs passive, public lookups: DNS queries and HTTPS
requests to public endpoints. It never sends email unless you run send with
SMTP credentials.
Why it matters
A domain without a strict DMARC policy, a hard-fail SPF record and DKIM signing can be spoofed: anyone can send mail that appears to come from it. For organizations that handle personal data, this is a concrete, documentable gap under GDPR Article 32 (security of processing).
MailRadar makes that gap measurable, for sysadmins who need to fix it and for DPOs who need to document it.
Installation
MailRadar requires Python 3.11 or later.
pip install mailradar
Check the installed version (--version is not yet in a published release;
see CHANGELOG.md):
mailradar --version
Docker
docker run --rm maksimtech/mailradar check example.com
In the image, report --save writes to /home/mailradar/.mailradar, the
image's volume: mount it (-v mailradar:/home/mailradar/.mailradar) to keep
the file after the container exits.
From source
git clone https://github.com/maksimtech/mailradar
cd mailradar
pip install -e .
The send command uses the gpg binary (GnuPG) to encrypt and sign reports.
Install it through your system package manager if you plan to use those
features. The Docker image already includes it.
Commands
| Command | What it does |
|---|---|
check DOMAIN |
Analyze a domain and print the score, per-check results and issues |
batch FILE |
Analyze every domain listed in a file and print a summary |
report DOMAIN |
Analyze a domain and generate a ready-to-send email report |
send DOMAIN |
Analyze a domain and deliver the report to its security contact |
discover DOMAIN |
Find contact email addresses for a domain from public sources |
Run mailradar COMMAND --help for the full list of options.
check: analyze a domain
mailradar check example.com
Real output:
╭──────────────────────────── 📡 MailRadar Report ─────────────────────────────╮
│ Domain: example.com │
│ Score: 65/100 — 🟡 MODERATE │
╰──────────────────────────────────────────────────────────────────────────────╯
╭──────────┬─────┬─────────────────────────────────────────────────────┬───────╮
│ Check │ St… │ Details │ Score │
├──────────┼─────┼─────────────────────────────────────────────────────┼───────┤
│ DMARC │ ✅ │ p=reject | pct=100 | adkim=s | aspf=s | rua=✗ | │ 45 │
│ │ │ ruf=✗ │ │
│ SPF │ ✅ │ v=spf1 -all │ 20 │
│ DKIM │ ❌ │ selector: default | key revoked │ 0 │
│ BIMI/VMC │ ❌ │ Not configured │ 0 │
│ MTA-STS │ ❌ │ Not configured │ 0 │
│ TLS-RPT │ ❌ │ Not configured │ 0 │
│ GPG │ ✅ │ uid: security@example.com | │ 5 │
│ │ │ https://keyserver.ubuntu.com │ │
╰──────────┴─────┴─────────────────────────────────────────────────────┴───────╯
⚠️ Issues found:
• No rua configured — aggregate reports disabled
• No ruf configured — forensic reports disabled
• DKIM key revoked (empty p=) on selector default and 16 more — no active key
found
• No BIMI record configured
• No MTA-STS configured
• No TLS-RPT configured
Show the raw DMARC, SPF, DKIM and BIMI records as well:
mailradar check example.com --verbose
If the domain does not resolve in DNS, check tries common TLD variants of
the name (.com, .it, .eu, .org, .de, .co.uk and others) and asks
you to pick one of those that exist.
Exit codes, useful in scripts and monitoring:
| Exit code | Meaning |
|---|---|
0 |
Grade EXCELLENT or GOOD |
1 |
Grade MODERATE or POOR, or the domain could not be resolved |
2 |
Grade CRITICAL |
batch: analyze several domains
mailradar batch domains.txt
The file contains one domain per line. Blank lines and lines starting with #
are ignored. MailRadar prints the full report for each domain, then a summary
table sorted from the lowest to the highest score. A domain that fails
analysis is reported and skipped; the others still run.
report: generate a report for the domain owner
mailradar report example.com --lang en \
--name "Jane Doe" --role "Security Officer" --org "Example Ltd" \
--save
The report is plain text, ready to paste into an email. For each check it
shows the current record and the recommended configuration (for example a
complete v=DMARC1; p=reject; ... record), followed by a short note on GDPR
Article 32 and your signature.
| Option | Description |
|---|---|
--lang, -l |
it (default) or en |
--name, --role, --org |
Sender details. If omitted, placeholders are left in the text |
--save, -s |
Also write the report to mailradar_<domain>_<YYYYMMDD>_<lang>.txt in the current directory |
send: deliver the report
export MAILRADAR_SMTP_PASS='...'
mailradar send example.com --lang en \
--name "Jane Doe" --role "Security Officer" --org "Example Ltd" \
--smtp-host mail.example.org --smtp-user jane@example.org \
--from jane@example.org
send runs the analysis, generates the report and delivers it as follows:
- The domain has a published GPG key (see GPG below): the report is encrypted to that key and sent to the key's address. Without SMTP settings, the encrypted text is printed so you can send it yourself. If encryption fails, nothing is sent, the report is printed in clear so you can deliver it manually, and the command exits with code 1. MailRadar never falls back to sending unencrypted.
- No GPG key, SMTP configured: the report is sent in plaintext to the
first of
security@,dpo@,privacy@,admin@,postmaster@that the SMTP server accepts. - No SMTP settings: the report is printed for copy-paste, with suggested recipients.
| Option | Description |
|---|---|
--smtp-host |
SMTP server |
--smtp-port |
SMTP port (default 465). The connection uses implicit TLS (SMTPS) |
--smtp-user |
SMTP username |
--smtp-pass |
SMTP password. Prefer the MAILRADAR_SMTP_PASS environment variable, so the password does not end up in your shell history |
--from |
Sender address |
--sign |
Clear-sign the report with your own GPG private key for the --from address (requires --from). The passphrase is asked interactively |
--lang, --name, --role, --org |
As in report |
SMTP delivery happens only when --smtp-host, --smtp-user, the password
and --from are all set.
discover: find contact addresses
mailradar discover example.com
discover looks for email addresses at the domain in public sources:
- Website: the home page and common contact and privacy pages of the
domain (
/contact,/contatti,/privacy,/privacy-policy,/about,/chi-siamo), plus the home,/privacyand/contattipages ofwww.and of up to 10 subdomains found in Certificate Transparency logs (crt.sh). - DNS: the SOA record's responsible-person address and TXT records.
- RDAP: the registration data of the domain.
Addresses actually found are listed by source. Common role addresses
(RFC 2142, such as security@, dpo@, abuse@) are listed separately as
unverified candidates, and only when the domain accepts email (it has MX
records and no null MX).
For every address, MailRadar checks whether a GPG public key is published
(🔐), which tells you where an encrypted report can be sent. Skip this check
with --no-gpg.
Reading the results
Score and grade
Each check awards points. The raw total (maximum 108) is scaled to 0–100.
| Score | Grade | Typical meaning |
|---|---|---|
| 90–100 | 🟢 EXCELLENT | Strict DMARC, SPF and DKIM, plus most of BIMI/VMC, MTA-STS, TLS-RPT and GPG |
| 75–89 | 🟢 GOOD | Core authentication (DMARC, SPF, DKIM) is strong |
| 50–74 | 🟡 MODERATE | Authentication is present but has gaps |
| 25–49 | 🟠 POOR | Authentication is partial or misconfigured |
| 0–24 | 🔴 CRITICAL | No meaningful email authentication |
DMARC, SPF and DKIM account for 85 of the 108 points. They decide whether the domain can be spoofed. The other checks are hardening measures.
In the table, ✅ means the check is correctly configured, ⚠️ that it is present but weaker than recommended, and ❌ that it is missing. Every point lost appears under Issues found with the action to take.
DMARC (up to 50 points)
DMARC (_dmarc.<domain>) tells receiving servers what to do with mail that
fails SPF and DKIM checks. It is the single most important record.
| Element | Points | Recommended |
|---|---|---|
Policy p= |
reject 30, quarantine 15, none 0 | p=reject: spoofed mail is refused. p=none only monitors and offers no protection |
pct= |
5 if 100 | Apply the policy to 100% of messages |
adkim= / aspf= |
5 each if strict (s) |
Strict alignment between the visible From domain and the DKIM/SPF domain |
rua= |
3 | Receive aggregate reports, to see who sends mail as your domain |
ruf= |
2 | Receive failure (forensic) reports |
A missing DMARC record means the domain is spoofable. The ✅ status is shown
only for p=reject.
Multi-level domains are resolved the way RFC 7489 §6.6.3 prescribes: if
_dmarc.<domain> has no record, one label is removed at a time up to the
organizational domain, which comes from the Public Suffix
List shipped with the package. For
asufc.sanita.fvg.it that means _dmarc.asufc.sanita.fvg.it, then
_dmarc.sanita.fvg.it: fvg.it is itself on the list, like it, co.uk or
co.at. The public suffix itself is never queried — a record on it is not the
domain's policy. Two DMARC records on the same name are reported as such:
receivers then apply no DMARC policy at all. An inherited record is marked
via <parent domain> in the table and listed under Issues found, since the
subdomain has no policy of its own; where the parent publishes sp=, that is
the policy scored for the subdomain.
SPF (up to 20 points)
SPF (a TXT record on the domain) lists the servers allowed to send mail for the domain. What matters most is how it ends:
| Ending | Points | Meaning |
|---|---|---|
-all |
20 | Hard fail: other servers are not allowed. Recommended |
~all |
10 | Soft fail: mail from other servers is only marked as suspicious |
?all |
5 | Neutral: no enforcement |
+all |
0 | Any server may send as the domain. Critical |
The record is read term by term, as RFC 7208 defines it: all with no
qualifier is +all, names are case-insensitive (-ALL is -all), and a
record without all follows its redirect=; with neither, the result is
neutral. Two SPF records on the domain, or more than 10 DNS lookups once
include: and redirect= are followed, are a permerror: receivers apply no
SPF, and the check scores 0. MailRadar also flags +a and +mx mechanisms as
too permissive.
DKIM (up to 15 points)
DKIM signs outgoing mail with a key published at
<selector>._domainkey.<domain>. The selector is not public, so MailRadar
tries 33 common ones — Google (google), Microsoft 365 (selector1,
selector2), Proton (protonmail, protonmail2, protonmail3), Fastmail
(fm1-fm3), Amazon SES, Zendesk, Mailchimp, SendGrid, Mimecast, Postmark,
default, mail, dkim and others — and asks for every one of them: a
receiver verifies a signature made with any published key, so the weakest key
found is the one measured, and the table names them all (selector1: 1024-bit RSA | selector2: 2048-bit RSA). An Ed25519 key (k=ed25519, RFC 8463) scores
like a 2048-bit RSA key. An empty p= is a revoked key (RFC 6376 §3.6.1): it
does not count, and the revocation is reported if no active key is found.
| Key size | Points |
|---|---|
| 2048 bits or more | 15 |
| 1024 bits | 10 (upgrade recommended) |
| Smaller | 3 (upgrade immediately) |
"Not found under 33 common selectors" is not "DKIM missing": Amazon,
Proofpoint customers and many others sign under selectors of their own. The
s= tag of a received message's DKIM-Signature header says which.
BIMI / VMC (up to 10 points)
BIMI (default._bimi.<domain>) lets supporting mail clients show the
organization's logo. MailRadar checks that the SVG logo is reachable (2
points) and whether a Verified Mark Certificate is referenced (8 points, or 3
without a VMC). The logo is downloaded only from an https:// URL that does
not name a private or loopback address, since the URL comes from the analysed
domain's DNS. v=BIMI1; l=; a=; declines BIMI and scores 0. BIMI is a trust and branding feature, not a protection
against spoofing by itself.
MTA-STS (up to 5 points)
MTA-STS (_mta-sts.<domain> plus the policy file at
https://mta-sts.<domain>/.well-known/mta-sts.txt) forces other servers to
deliver mail to the domain over verified TLS. enforce mode scores 5 points,
testing mode 2.
TLS-RPT (up to 3 points)
TLS-RPT (_smtp._tls.<domain>) asks other servers to report TLS delivery
failures. The table shows where the reports are sent.
GPG (up to 5 points)
MailRadar searches public keyservers (keys.openpgp.org, keyserver.ubuntu.com)
for a key published for exactly security@, dpo@, admin@, postmaster@
or privacy@ at the domain. A published key means security issues can be
reported to the organization confidentially. The first key found is the one
send uses for encryption.
The 5 points go only to a key keys.openpgp.org lists, because that server publishes an address only after its owner answered a verification mail. keyserver.ubuntu.com lists whatever anyone uploads under any name: a key found there is shown with its fingerprint and "address not verified", scores nothing, and should be confirmed with the domain before use. A keyserver that does not answer (HTTP 429 — keys.openpgp.org allows one by-email lookup a minute, with a burst of 50 — or a timeout) is not asked again during the analysis and leaves the check "not verified", which is not "no key".
Known limitations
- DKIM detection depends on the list of common selectors (see above).
- A DNS lookup that fails (timeout, SERVFAIL) leaves that check "not
verified": it scores 0 but is not reported as missing, and
reportandsendrefuse to produce a report from an incomplete analysis. - SPF macros (
%{d}and the like) are not expanded, so aninclude:orredirect=that uses one is counted but not followed, and the limit of two void lookups is not checked. - The BIMI logo host name is not checked for resolving to a private address,
and the VMC is not downloaded or validated: "VMC" means that
a=names one. - A key keyserver.ubuntu.com publishes for the address is reported as "not
verified": that server does not check who uploads a key, and
sendstill encrypts to the first key found. Confirm the fingerprint with the domain before relying on it for sensitive reports. sendsupports SMTP over implicit TLS only (SMTPS, usually port 465).- With
send --sign, if signing fails (for example a wrong passphrase) the report is sent unsigned.
Related projects
- PatchRadar — CVE monitoring for self-hosted software stacks
Built with
- dnspython — DNS queries
- cryptography — DKIM key size detection
- httpx — BIMI, MTA-STS, keyserver and discovery requests
- Jinja2 — report templates
- Typer — command-line interface
- Rich — terminal output
Contributing
Contributions are welcome. Open an issue or a pull request on GitHub. See SECURITY.md to report a vulnerability and CHANGELOG.md for the release history.
How this is built
Developed with Claude Code, reviewed and released by maksimtech. The suite is the contract: the release script runs it with the new version already written and refuses to commit if it fails, so nothing ships that it has not passed.
License
MIT — see LICENSE.
One file in the package is not MIT and not ours: mailradar/public_suffix_list.dat
is the Public Suffix List maintained by Mozilla, shipped
unmodified and distributed under the Mozilla Public License 2.0
(MPL-2.0), as its header states.
Built by maksimtech.
Metadata
Release files for mailradar 2026.44
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mailradar-2026.44.tar.gz | 385.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mailradar-2026.44-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 533.0 kB
Release files / mailradar-2026.44.tar.gz
| Download URL | mailradar-2026.44.tar.gz |
|---|---|
| Size | 385.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f8e90a4ee4eb43cd8444daf5628da61285a2e4a54b122cc85da3b64a4c3a488b
|
|
BLAKE2b-256 checksum How to use checksums |
026acb8c62ad52416f05cd0f671b45668cc6516a572c3f4f1737c7d68d4ce124
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.
Transparency logRelease files / mailradar-2026.44-py3-none-any.whl
| Download URL | mailradar-2026.44-py3-none-any.whl |
|---|---|
| Size | 147.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf815d9cc0deafb785f2778a341593d0ba2cdc5f4887b0e4d158e412d6ce13c3
|
|
BLAKE2b-256 checksum How to use checksums |
4193095c72f6b612c46963b212e3f1c4e6f527838bbd5fcde75f617780c28677
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.
Transparency log