mailrs-outbound-queue
Outbound mail queue primitives for Rust MTAs — DKIM signing, DSN generation, MTA-STS lookup, retry/backoff, MX/DANE-aware delivery, plus a pluggable store trait and a Postgres reference implementation.
Extracted from mailrs so any Rust MTA project can reuse the parts that are actually painful to get right: signing forwarded mail with ARC, generating standards-compliant Delivery Status Notifications, looking up MTA-STS policies, computing exponential backoff with jitter, and the long tail of "did this 5xx mean I should give up or try later" logic.
Highlights
- Trait-pluggable store — [
QueueStore] + [Notifier] decouple delivery state from any particular backend. An [InMemoryQueueStore] ships in-box for tests + pilots. - Postgres reference — [
PgQueueStore] + [RedisNotifier] (behind the defaultpgfeature) target the schema mailrs uses, so a real production-grade queue is one constructor call away. - Pure primitives, no DB required —
dkim_sign,dsn,mta_sts, andretryare pure logic. Disable thepgfeature and they all still compile and work. - ARC sealing for forwarded mail —
dkim_sign::arc_seal_messageadds an ARC chain alongside DKIM so downstream filters can trust the forwarding hop (RFC 8617). - Bundled delivery worker — [
DeliveryWorker] runs the poll-and-deliver loop over MX records (with DANE TLSA enforcement viamailrs-smtp-client). PG-only in v1.0.0; a generic worker over the trait surface is planned for v2. - Battle-tested — extracted from a production Rust mail server.
Quick start (PG-backed)
use ;
use Arc;
# async
See examples/in_memory_queue.rs for a no-DB example that drives the trait surface end-to-end.
Feature flags
| Feature | Default | What it enables |
|---|---|---|
pg |
on | PgQueueStore, RedisNotifier, and the bundled DeliveryWorker. Pulls in sqlx (Postgres) and redis. |
Disable pg for a trait-only build:
= { = "1", = false }
In that mode you get QueueStore + Notifier + InMemoryQueueStore + InMemoryNotifier + the pure primitives (dkim_sign, dsn, mta_sts, retry). Write your own worker on top.
Module overview
| Module | Always | Notes |
|---|---|---|
store |
yes | QueueStore, Notifier, InMemoryQueueStore, InMemoryNotifier, StoreError. |
queue |
yes | QueuedMessage, QueueStatus, is_hard_bounce. PG free fns (gated by pg). |
dkim_sign |
yes | RFC 6376 DKIM signing + RFC 8617 ARC sealing. |
dsn |
yes | RFC 3464 / 6533 Delivery Status Notification generation. |
mta_sts |
yes | RFC 8461 MTA-STS policy lookup. |
retry |
yes | Backoff schedule + bounce decision. |
pg_store |
pg |
PgQueueStore + RedisNotifier. |
worker |
pg |
DeliveryWorker poll-and-deliver loop. |
Two paths through the API
The crate exposes two parallel public surfaces over the same queue semantics:
- Trait API (
QueueStore,Notifier) — the portable surface. Use it when you want to plug a non-PG backend in, or when you want full control over the delivery loop. - PG free functions in
queue::— convenience for the common case where you already hold asqlx::PgPooland just wantqueue::enqueue(pool, ...). These back the bundledDeliveryWorkerand are what mailrs itself uses internally.
Both are stable for v1.x. The v2 plan is to consolidate around the trait surface and ship a generic worker, but no v1 user code is expected to break.
What this crate does NOT do
- No SMTP server — see
mailrs-smtp-protofor the inbound state machine. - No DKIM verification — outbound only (signing). Verification lives in
mail-auth. - No SPF / DMARC enforcement on inbound. Those belong upstream of this crate.
- No message storage / threading. See
mailrs-mailbox/mailrs-maildirfor those.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.