mailautopsy
The same bytes in produce the same verdict out, forever, with no network and
no service to call. Unknown is an answer this tool is willing to give.
mailautopsy reads a reported email message and reports named, evidence backed
signals instead of an opaque reputation score. It never declares a message
safe. The best result it produces is that none of the named checks fired on the
evidence present.
Written in Go with the standard library only. No third party modules, no
telemetry, no account.
The replay contract
- Offline by default. Nothing leaves the machine unless you pass
-live, which
uses the system resolver and follows redirects.
- Deterministic. A given message file produces the same report on any machine,
today or in a year. The rules are in this repository and nowhere else.
- Sealed. Every report carries a SHA256 digest over its own JSON. Every engine
ships a
Verify function to recheck a report, and the link and combined
engines also ship VerifyAgainst to recheck a report against the original
bytes.
- Honest about ignorance. Insufficient evidence is a distinct outcome with its
own exit code. It's never rounded up to clean or down to dangerous.
- Untrusting of carried claims. An
Authentication-Results header inside a
saved file is trivial to forge, so it isn't trusted by default.
What it does
Three engines, each usable as a library or through the one CLI:
mailauth reads a raw RFC 5322 message and reports sender authenticity
signals: Authentication-Results handling, From alignment, display name
deception, reply address divergence, lookalike sender domains, the Received
chain, and DNS backed signals when you ask for them.
linktriage reads a plain text snippet, an HTML fragment, or a full message
and reports link and attachment danger signals: deceptive link text, userinfo
tricks, punycode and digit swap lookalikes, dangerous schemes, bare IP hosts,
shorteners, double extension attachment names, and more. It embeds a snapshot
of the Public Suffix List so registrable domain comparisons are correct.
reportedemail composes both into one sealed, offline report with a single
triage standing.
Install
With Go:
go install github.com/Parallax-Intelligence-Partnership-LLC/mailautopsy/cmd/mailautopsy@latest
With Docker, built from this repository and offline at runtime by
construction:
make docker && docker run --rm --network none -v "$PWD:/work:ro" mailautopsy:dev report /work/message.eml
From a release: download the binary for your platform, together with
SHA256SUMS and SHA256SUMS.bundle, then follow the verification steps below
before you run it.
Use
Every example below runs against a synthetic fixture that ships in this
repository, so the output is reproducible on any checkout.
Both engines composed into one sealed offline verdict:
mailautopsy report mailauth/testdata/spf_dmarc_fail.eml
Sender authenticity signals only, compared against the domain you expected to
hear from:
mailautopsy mail -brief -expect example.com mailauth/testdata/display_name_deception.eml
Link and attachment signals read from standard input, as JSON for a script:
mailautopsy links -json < linktriage/testdata/full_walk.eml
mail exit codes: 0 authenticated, 2 suspect, 3 misaligned, 4 insufficient
evidence, 1 usage or runtime error.
links exit codes: 0 no named signals, 2 dangerous, 3 suspect, 4 insufficient
evidence, 1 usage or runtime error.
report exit codes: 2 danger signals present, 3 review signals present, 4
insufficient evidence, 1 usage or runtime error. It has no code that means
clean, because it makes no such claim.
Pass -attest-authserv only after you have independently verified both that
the file came unchanged from that receiving system and that the system strips
forged headers claiming its own ID. Never copy the ID out of the suspicious
message itself and treat that as verification.
Library
import "github.com/Parallax-Intelligence-Partnership-LLC/mailautopsy/reportedemail"
report, err := reportedemail.Analyze(raw, reportedemail.Options{
ExpectedDomains: []string{"example.com"},
})
Browser
cmd/mailautopsy-wasm builds the offline composed analysis to WebAssembly and
exposes it as one global function, so a pasted message can be checked entirely
on the client. It performs no network request, no DNS lookup, and no link
request.
make wasm
The function is __mailautopsyAnalyze(base64Message, expectedDomainsJSON) and
it returns a JSON string.
Verify it yourself
Release artifacts are signed with cosign keyless signing, using a short lived
GitHub OIDC identity rather than a stored key. Checking a download takes two
commands, with the release tag in place of TAG:
cosign verify-blob SHA256SUMS \
--bundle SHA256SUMS.bundle \
--certificate-identity "https://github.com/Parallax-Intelligence-Partnership-LLC/mailautopsy/.github/workflows/release.yml@refs/tags/TAG" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum --check --ignore-missing SHA256SUMS
The first command proves the checksum file came from a release build of this
repository. The second proves the binary you hold matches that checksum.
Verifying a report rather than a binary is a separate thing, and it's the
point of the tool: keep the original message file, keep the report, and rerun
the analysis. Step by step:
# 1. Rerun the analysis on the message the report claims to describe.
mailautopsy report -json message.eml > replay.json
# 2. Same bytes in, same bytes out. Byte identical means same verdict,
# same findings, same everything.
cmp report.json replay.json
# 3. The report is bound to the exact message bytes. Recompute the digest
# yourself and compare it to message_sha256 inside the report.
shasum -a 256 message.eml
If cmp is silent and the digests match, the report is exactly what this
analyzer says about exactly that message. If the message file changed by one
byte, message_sha256 will not match it. If the report was edited after the
fact, its report_sha256 will not match its own body.
Test fixtures
Every fixture in this repository is synthetic; no captured real mail exists
anywhere in the corpus. Legitimate addresses use the reserved example.com,
example.net, example.org, and .example names. Hostile fixtures also use
typosquat domains such as paypa1.com as pretend senders, because detecting
exactly that shape is the tool's job. Real brand names appear only as the
protected target of a lookalike check, never as a sender and never as
captured mail.
Public Suffix List
linktriage/data/public_suffix_list.dat is an unmodified snapshot of the
Mozilla Public Suffix List, used under the MPL 2.0. See the README beside it.
License
MIT. Copyright 2026 Parallax Intelligence Partnership.
https://postmortem.report