Fix Reply-To parsing and RUF dashboards (release 10.0.3)#784
Merged
seanthegeek merged 5 commits intoMay 24, 2026
Merged
Conversation
parse_email() read mailparser's underscored reply_to/delivered_to keys,
but mail_json names those headers reply-to/delivered-to, so every
Reply-To/Delivered-To address was silently dropped (reply_to was always
[]). Read the hyphenated keys and store under the underscored name
consumers expect, dropping the raw key so the body carries a single
representation (matching to/cc/bcc).
Dashboards:
- Splunk failure dashboard showed empty from/reply_to columns (mis-cased,
array-of-array header paths). Now reads structured
parsed_sample.from.address, parsed_sample.subject,
parsed_sample.reply_to{}.address.
- OpenSearch failure dashboard's "reply_to" column aggregated
sample.headers.in-reply-to.keyword (the In-Reply-To threading header).
Repointed to sample.headers.reply-to.keyword and added that field to the
dmarc_f* index pattern. The ES/OS failure writer now flattens the
Reply-To header to a display string on sample.headers["reply-to"],
mirroring From/To.
Tests assert on the indexed document and on parsed reply_to addresses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Grafana Elasticsearch dashboard's Failure Samples panel already read sample.headers.reply-to.keyword, but that field held the raw [[name, addr]] array until the failure-writer flattening added in this PR; no dashboard change is needed there — the existing ReplyTo column now renders a clean string. The Grafana PostgreSQL dashboard's Failure Reports panel surfaced neither the message From header nor Reply-To (only the envelope Mail From / Rcpt To). Add a "From" column (from sample_from JSONB) and a "Reply To" column (aggregated from dmarc_failure_sample_address where address_type='reply_to', the path parse_email now populates). Add a postgres regression test asserting a parsed Reply-To address is inserted with address_type 'reply_to'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a derived `display` field to every parsed address dict — "Display
Name <addr>", or the bare address when there is no display name — and
point the Splunk failure dashboard's From/Reply-To columns at it
(parsed_sample.from.display, parsed_sample.reply_to{}.display).
The Elasticsearch/OpenSearch and Grafana-ES panels already render this
format via the flattened sample.headers.* fields, and the Grafana-PG
panel builds it in SQL; Splunk reads the structured parsed_sample, so it
needed a ready-to-render field rather than fragile multivalue SPL.
EmailAddress TypedDict and the parse_email_address tests updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous revision pointed the From column at parsed_sample.from.display,
a field that only exists on reports parsed by 10.0.3+. Reports indexed by
earlier versions carry parsed_sample.from.{display_name,address} but no
.display, so their From rendered blank.
The column now coalesces parsed_sample.from.display -> a display_name/address
pairing -> the bare address, so it renders the same "Name <addr>" / address
form on both historical and new data. The < / > are XML-entity-escaped in the
SimpleXML query.
Reply-To has no such fallback by design: earlier versions dropped it at parse
time (parsed_sample.reply_to was always []), so older reports have no Reply-To
to display regardless of query; it appears only for reports parsed by 10.0.3+.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per review, remove the parse_email_address `display` field (and its EmailAddress TypedDict entry and tests). The dashboards now assemble the "Name <addr>" / bare-address form entirely at query time from display_name and address — fields present on historical and new reports alike — so no stored field is required and pre-10.0.3 data renders too. The Splunk failure panel builds both `from` and `reply_to` with an eval that coalesces `display_name <address>` down to the bare address. A multi-address Reply-To degrades to addresses-only (a Splunk multi-value concat limitation, not data loss); the common 0/1-address case renders fully. ES/OS/Grafana-ES (flattened sample.headers.*) and Grafana-PG (sample_from / dmarc_failure_sample_address) already build the same form without the field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
seanthegeek
added a commit
that referenced
this pull request
May 24, 2026
PR #784 was stacked on the #783 branch and its base was never retargeted to master, so it merged into fix/mailsuite-2.2.1-empty-address instead of master. master therefore has 10.0.2 (#783's squash) but is missing the 10.0.3 changes. This re-lands exactly that delta — the Reply-To/Delivered-To parser fix, the ES/OS Reply-To header flattening, and the Splunk/OpenSearch/Grafana failure dashboard fixes, with the version bumped to 10.0.3. No mailsuite re-bump (the >=2.2.1 floor is already on master from 10.0.2). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #783 (base will retarget to
masteronce #783 merges). Release10.0.3.Follow-up to #783. The mailsuite 2.2.1 bump fixed the upstream phantom empty-address junk, but parsedmarc-side bugs remained that left
Reply-ToandFromempty/wrong in the failure (RUF) dashboards. This PR fixes them across all four dashboards and makes address rendering uniform.1.
Reply-To/Delivered-Toalways dropped (parser)parse_email()looked up mailparser's underscoredreply_to/delivered_tokeys, butmail_jsonnames those headersreply-to/delivered-to. The lookup always missed, soparsed_sample["reply_to"]was always[]regardless of the message. Now reads the hyphenated keys and stores under the underscored name consumers expect, dropping the raw key so the body carries a single representation (matchingto/cc/bcc). Independent of the mail-parser version — 4.2.1 still names the keyreply-to.This one fix flows everywhere
parsed_sampleis consumed: JSON/CSV output, the Elasticsearch/OpenSearch nestedsample.reply_to, and the Postgresdmarc_failure_sample_addresstable (address_type='reply_to').2. Uniform address rendering (works on historical data)
Every displayed address (
From,To,Reply-To) now renders the same way on every dashboard:Display Name <addr>, or the bare address when there is no display name. The format is assembled at query time fromdisplay_name/address— fields that already exist on previously-indexed reports — so the panels render historical data, not only reports stored after upgrading. No new stored field is introduced.One unavoidable exception: a report's
Reply-Toonly appears for reports parsed by 10.0.3+. Earlier versions discarded it at parse time (§1), so it is absent from older stored reports; only re-parsing the originals recovers it.3. Dashboards
parsed_sample.headers.from{}{}/...reply-to{}{}— mis-cased and array-of-array shaped, so the columns were empty. Now buildsfromandreply_towith anevalthat coalescesdisplay_name <address>→ bareaddress. (A multi-addressReply-Tofalls back to addresses-only — a Splunk multi-value-rendering limitation, not data loss.)reply_tocolumn aggregatedsample.headers.in-reply-to.keyword— theIn-Reply-Tothreading Message-ID, not Reply-To. Repointed tosample.headers.reply-to.keyword, field added to thedmarc_f*index pattern. The ES/OS failure writer now flattensReply-Tointosample.headers["reply-to"], mirroring the existingFrom/Toflattening.sample.headers.reply-to.keyword, but that field held the raw[[name, addr]]array until the writer flattening above. The existingReplyTocolumn now renders a clean string — no dashboard change required.FromnorReply-To(only envelopeMail From/Rcpt To). Added aFromcolumn (fromsample_from) and aReply Tocolumn (aggregated fromdmarc_failure_sample_address), both in the sameName <addr>format.Tests
parse_email()with single + multipleReply-To→ all captured;Delivered-To→ captured..save()—sample.headers["reply-to"]is the flattened string and nestedsample.reply_tocarries the address.Reply-Tois INSERTed withaddress_type='reply_to'.Full suite:
633 passed.ruff check/ruff format --checkclean.Not verified against a live instance
The Splunk / OpenSearch / Grafana dashboard edits were verified structurally (field paths, parsed visState/index-pattern, valid SQL, XML re-parses, SPL un-escapes), not rendered against live backends. Re-import the dashboards (or refresh the
dmarc_f*index pattern) to pick up the new OpenSearch field.🤖 Generated with Claude Code