Parse failure DSNs to mark outgoing messages bounced after acceptance
- Dominant language
- Python
- Stars
- 4
- Forks
- 0
- Avg merge
- 8h 24m
- Merged PRs (30d)
- 115
Description
## Context
Hard bounces are currently only captured **synchronously**: `deliver_message` sets `OutgoingMessage.Status.BOUNCED` when the recipient MX answers 5xx during the SMTP session (`services/email/msa/tasks.py`). A **post-acceptance bounce** — rejected later by a content filter, downstream relay, or full mailbox — arrives at relay's MTA as a plain `IncomingMessage` and is never linked back:
- the original message stays `sent` forever,
- no `SuppressionEntry` is created, so relay keeps sending to a dead address.
Relay already mints a VERP envelope sender per message (`bounce+@`, `RELAY_BOUNCE_LOCAL_PART`), so failure DSNs (RFC 3462/3464 `multipart/report` with `report-type=delivery-status`) come back to our MTA addressed to an address we can match. `reputation/tasks.py::resolve_fbl_owner` already demonstrates the matching pattern for FBL reports.
Note: success DSNs are intentionally out of scope — receivers rarely honor `NOTIFY=SUCCESS` (RFC 3461), so DSNs are only used for failures. The `delivered` status was removed from the enums for the same reason.
## Task
1. Detect DSNs on the inbound path (`multipart/report` / `report-type=delivery-status`, `message/delivery-status` body part) in the `mta` app.
2. Match the original `OutgoingMessage` from the `bounce+` local part of the envelope recipient (mirror `resolve_fbl_owner`).
3. Parse the per-recipient fields: `Action` (`failed`/`delayed`), `Status` (enhanced code), `Diagnostic-Code`.
4. On `failed`: transition the message to `bounced` (remote permanent rejection) and create a `SuppressionEntry` with reason `bounce` — same behavior as the inline 5xx path.
5. On `delayed`: record the attempt/report for visibility without changing the message status.
6. Do not match DSNs whose action is `delivered`/`relayed` (unreliable and not tracked).
7. Tests for: VERP matching, DSN parsing, failed → bounced + suppression, delayed no-op, non-DSN mail untouched.
8. Update `docs/docs/message-statuses.md` (async bounce handling) once implemented.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.