The problem
When a message can’t be delivered, Stalwart sends a bounce (DSN). That bounce only contains the headers of the original message - never the body or attachments. You can see who it was for and why it failed, but not what was actually sent.
Who this affects (use case)
This matters most for mail sent programmatically - e.g. a PHP or cron script submitting over SMTP. Those messages are usually not saved to any Sent folder, so when one bounces, the bounce is often the only record of it. Headers-only means you can’t see the actual content that failed to deliver, which makes debugging (“what exactly went out?”) hard.
Current behavior
From a look at crates/smtp/src/queue/dsn.rs, the DSN builder fetches up to MAX_HEADER_SIZE bytes and attaches them as message/rfc822 - so only the headers are ever returned, never the body or attachments.
Proposed behavior
A simple opt-in setting (config key + a toggle in the admin Web UI), e.g. under report.dsn:
- off (default) → today’s behavior, headers only - fully backwards-compatible.
- on → include the full original message (
message/rfc822: headers + body + attachments) in failed DSNs.
Optionally a size cap (e.g. report.dsn.max-return-size) so large messages with big attachments stay bounded.
This lets a self-hoster decide server-side, without depending on the client to set the ESMTP RET=FULL parameter (which Stalwart doesn’t currently honor anyway). Honoring RET would be a nice extra, but a plain admin toggle is the simplest win for self-hosted setups.