Parse DMARC aggregate reports; model deliverability findings and delivery outcomes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-27 16:56:57 -06:00
1 parent 6663b268c8
commit 5c1737bad3
3 files changed
+483

No files matched your search

+52
View File
@@ -241,6 +241,58 @@ type MailCheck struct {
HeloResolves bool `json:"heloResolves"` // the HELO name resolves back to the source IP
Port25 string `json:"port25"` // "open" or why not
DKIM map[string]string `json:"dkim"` // key ID -> "published", "missing" or "mismatch"
Domains map[string]DomainCheck `json:"domains,omitempty"` // sending domain -> its DNS checks
Blocklists []string `json:"blocklists,omitempty"` // DNS blocklists that list the source IP
Listable int `json:"listable,omitempty"` // how many blocklists answered, so "none listed" means something
Outcomes *MailOutcomes `json:"outcomes,omitempty"` // what happened to the mail this egress sent
}
// Finding statuses.
const (
FindingPass = "pass"
FindingWarn = "warn"
FindingFail = "fail"
FindingNone = "none" // nothing published
)
// MailFinding is the result of one deliverability check, with what to do about it.
type MailFinding struct {
Status string `json:"status"`
Detail string `json:"detail"`
Hint string `json:"hint,omitempty"`
}
// DomainCheck is how the DNS of one sending domain looks to a receiving server.
type DomainCheck struct {
SPF MailFinding `json:"spf"`
DMARC MailFinding `json:"dmarc"`
MTASTS MailFinding `json:"mtaSts"`
}
// MailOutcomes counts what became of the mail an egress handled over a window.
type MailOutcomes struct {
Since time.Time `json:"since"`
Delivered int `json:"delivered"`
Deferred int `json:"deferred"` // 4xx: the receiver asked us to try later
Failed int `json:"failed"` // 5xx: refused for good
ByDomain map[string]OutcomeCount `json:"byDomain,omitempty"` // recipient domain
Recent []OutcomeFailure `json:"recent,omitempty"` // latest problems, newest first
}
// OutcomeCount is delivered/deferred/failed for one recipient domain.
type OutcomeCount struct {
Delivered int `json:"delivered"`
Deferred int `json:"deferred"`
Failed int `json:"failed"`
}
// OutcomeFailure is one deferral or refusal, for the dashboard to explain.
type OutcomeFailure struct {
Time time.Time `json:"time"`
Domain string `json:"domain"`
Code int `json:"code"`
Text string `json:"text"`
}
// ---------- edge <-> hammerhead ----------