Skip to content

🚀: Feature Request: Universal Bounce Tracking and Suppression #1446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
lulzkiller666 opened this issue Apr 28, 2025 · 2 comments
Open
1 task done
Labels
enhancement New feature or request

Comments

@lulzkiller666
Copy link

lulzkiller666 commented Apr 28, 2025

Which feature or improvement would you like to request?

Summary

I would like to request a universal bounce handling feature in Stalwart Mail Server to automatically track email addresses that cause permanent delivery failures (hard bounces) and prevent future sends to those addresses. This would improve deliverability, simplify operations, and avoid having to manage bounced addresses outside of Stalwart.

Additionally, it should include a GUI interface to view, search, and manage bounced addresses, with the ability to manually re-enable suppressed addresses if needed.

Why This Is Needed
• Today, bounce management must happen outside of Stalwart, creating extra complexity and risk.
• Automatically suppressing permanently bounced addresses will improve sender reputation and reduce unnecessary traffic.
• A built-in GUI for managing suppressed addresses would give full transparency and manual control when needed.
• Sending systems could remain simple — just send as usual, while Stalwart handles suppression intelligently.

Basic Idea
• Monitor delivery failures (both SMTP and DSN).
• Automatically suppress future sends to addresses that trigger permanent errors.
• Provide a GUI to view, search, and manage suppressed addresses.
• Allow manual re-enabling or suppressing addresses through the GUI.

Please vote for this feature by giving it a thumbs up, because it's something not many other email solutions provide a nice way of handling. This is something that could for now be very unique for stalwart

Is your feature request related to a problem?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@lulzkiller666 lulzkiller666 added the enhancement New feature or request label Apr 28, 2025
@stappersg
Copy link

Questions[1] as input for further discussion[1]:

  • What would be a good name for the "don't bother to send to these" database tabel?
  • "not_these column" only email addresses (like joe@example.com) or also some wildcard expression like *ple.com which matches the domains example.com and apple.com?
  • Which other columns besides when ( a date ), who and why should be in the table?
  • A better logging text as "found {dest_address} match in don't bother table"?
  • a 4yz or 5yz message back to requestor? (IMNSHO 553 Requested action not taken: mailbox name not allowed)

Footnote [1]:
Writing code is the answer.

@lulzkiller666
Copy link
Author

Thanks for the great follow-up questions! Here’s my take to help flesh out the feature more thoroughly:

Table name
Instead of something generic like not_these, I’d recommend a more descriptive name such as suppressed_addresses, bounced_recipients, or delivery_blocks. These names clearly convey the table’s purpose and work well in both GUI labels and logs.

Match types
I think it would be valuable to support different levels of matching:
• Exact email addresses (e.g., joe@example.com)
• Domain-level matches (e.g., @example.com)
• Optional pattern or wildcard matching (e.g., *ple.com) — though wildcard use should be handled carefully to avoid unintentionally broad suppression.

To support this, a simple model with two columns — match_type (address, domain, pattern) and match_value — would make matching logic flexible and extensible.

Table fields
To make this feature practical for administrators and for automation, the following fields would be useful in the suppression table:
• id
• match_type (address, domain, or pattern)
• match_value (the actual suppressed address or pattern)
• suppressed_at (timestamp of when it was added)
• last_attempt_at (optional; when it was last triggered)
• bounce_code (e.g., 550 5.1.1)
• bounce_message (the full SMTP or DSN message for context)
• source_ip (optional but helpful for auditability)
• manual_override (boolean; true if manually re-enabled or disabled)
• notes (admin comment field)

This would give full visibility into why a recipient was suppressed, while still allowing manual intervention if needed.

Logging
Instead of a log message like:
found {dest_address} match in don't bother table
I suggest a more descriptive version such as:
Suppressed delivery: {dest_address} is listed in suppression table (matched on {match_type}: {match_value})
This provides clarity and helps with debugging or reviewing logs later.

SMTP response code
Since these are permanent suppressions due to hard bounces, returning a 5xx makes sense. Two solid options are:
• 553 Requested action not taken: mailbox name not allowed — clear and fairly standard.
• Or:
550 5.1.1 Delivery to {dest_address} suppressed due to previous hard bounce — slightly more specific and informative.

If possible, including a suppression reference or ID in the message could help with support or diagnostics, e.g., #SUPP-1234.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants