Skip to content

feat: reply from the address a message was delivered to - #278

Open
salmonumbrella wants to merge 1 commit into
maathimself:mainfrom
salmonumbrella:feat/reply-as-delivered-to
Open

feat: reply from the address a message was delivered to#278
salmonumbrella wants to merge 1 commit into
maathimself:mainfrom
salmonumbrella:feat/reply-as-delivered-to

Conversation

@salmonumbrella

Copy link
Copy Markdown
Contributor

Summary

When a message arrives at an alias through a BCC delivery or a catch-all, the alias never appears in To/Cc — so the existing reply alias auto-selection (which matches To/Cc/From against the account's aliases) misses, and the reply comes from the primary address. This follows up on the #256 discussion: "MailFlow doesn't yet auto-select the alias a message was delivered to when you hit reply."

This PR captures the envelope recipient (Delivered-To / X-Delivered-To / X-Original-To / Envelope-To) at ingest and lets the reply alias matcher check it first, ahead of To/Cc/From. Selection stays transient — nothing is auto-created or persisted into aliases, per the direction in #256. Existing messages carry no capture (NULL) and behave exactly as today, then backfill on their next sync touch; the send path is untouched — the matched alias flows through the existing aliasId mechanism.

Changes

  • Migration 0035 (IF NOT EXISTS-guarded, numbered after main's current 0034): nullable messages.delivery_addresses JSONB, same shape as list_unsubscribe
  • messageParser.js: parseDeliveryAddresses() reads the four delivery headers from the already-parsed header map (reuses parseMailboxList; no IMAP fetch changes), deduped and capped at 50 addresses (sender-controlled input, capped like subject/snippet)
  • imapManager.js: persists the parsed addresses at the message INSERT sites with the same COALESCE upsert plumbing as list_unsubscribe (the synthesized sent-copy record has no delivery headers and is deliberately left out)
  • messageService.js + mail.js: expose delivery_addresses on the message shapes the frontend actually replies from (list, thread, single message, resolve)
  • MessagePane.jsx: the inline reply-alias matcher is extracted to frontend/src/utils/replyAlias.js (pure, testable) and now checks delivery addresses first; the existing To/Cc/From matching semantics — including the alias-creation-order tiebreak for multi-alias accounts — are preserved bit-for-bit and pinned by tests

Testing

  • Backend: 633/633 (vitest), lint clean (--max-warnings 0) — new parser cases: repeated/multi-line headers, angle-bracket forms, cross-header dedupe, junk lines, the size cap
  • Frontend: 1359/1359 (node --test), lint clean, production build clean — matcher tests pin: delivered-to beats To/Cc, the unchanged multi-alias tiebreak, malformed-JSON safety, case-insensitivity
  • No new dependencies; no behavior change for existing messages (NULL capture ⇒ identical selection to today)

Contributor License Agreement

By submitting this pull request I confirm that:

  • I have read and agree to the Contributor License Agreement.
  • My contribution is my own original work (or I have identified any
    third-party material and confirmed it is compatible with the CLA).
  • I have the right to submit this contribution under the terms of the CLA.

Capture the envelope recipient (Delivered-To / X-Delivered-To /
X-Original-To / Envelope-To) at ingest into messages.delivery_addresses
and let reply alias auto-selection match it first, so a message that
reached an alias through BCC or a catch-all replies from that alias even
though the alias never appears in To/Cc. Selection stays transient --
nothing is auto-created or persisted.

Existing messages carry NULL and keep today's behavior; a later re-sync
backfills them. The send path is untouched: the matched alias flows
through the existing aliasId mechanism.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant