Issue Description
The documentation describes trustContacts as causing a message from an
address-book contact to bypass the spam decision and reach the inbox
whatever its score. The implementation applies a further condition that
is not documented anywhere: the sender must be authenticated, which for
inbound SMTP means the message passed DMARC. When the sending domain
publishes no DMARC record, the override never fires — even though the
contact is found and every other condition holds.
To be clear about the scope: I believe the condition itself is deliberate
and sound, and I am not asking for it to be removed (evidence in
Additional Context). This report is about the documentation describing
different behaviour, and about the operator having no way to observe or
configure the condition.
Expected Behavior
Per the Trusted senders page (Trusted senders | Stalwart)
and the SpamSettings reference (SpamSettings | Stalwart), both of which present
the feature as unconditional, a message whose sender is present in the
recipient’s address book should be delivered to the inbox regardless of
the score assigned by the filter.
Actual Behavior
The message is filed into Junk with “X-Spam-Status: Yes”.
The card-exists override at crates/email/src/message/ingest.rs:262-296
is skipped because is_sender_authenticated is false, which follows from
the sending domain having no DMARC record.
Worth noting separately: the deviation is invisible. When the override
fires, the header carries “reason=card-exists”. When it is skipped, no
header, log entry or trace event records that the override was
considered and rejected, so there is nothing to debug from.
Reproduction Steps
- Leave trustContacts = true (the default).
- Add a contact to the recipient’s default address book whose email
address belongs to a domain that publishes NO DMARC record. Confirm
the server sees it: ContactCard/query with filter
{“inAddressBook”: “”, “email”: “”} returns the card id. - Have that sender deliver a message over inbound SMTP that scores at
or above scoreSpam. SPF and DKIM may both pass — only the DMARC
record must be absent. - The message is filed into Junk. Re-run the ContactCard/query from
step 2: the card is still there.
Step 3 works with any domain lacking a DMARC record; I reproduced it
with a Microsoft 365 tenant.
Relevant Log Output
Authentication-Results: ;
dkim=pass header.d=.onmicrosoft.com;
spf=pass smtp.mailfrom=@;
iprev=pass;
dmarc=none header.from= policy.dmarc=none
X-Spam-Result: DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20),
DMARC_NA (1.00), HELO_IPREV_MISMATCH (1.00),
MID_RHS_MATCH_FROM (1.00), MIME_BASE64_TEXT_BOGUS (1.00),
PYZOR (3.50)
X-Spam-Score: spam, score=7.10
X-Spam-Status: Yes
server-side confirmation that the contact is visible to Stalwart
ContactCard/query {“inAddressBook”: “d”, “email”: “”}
→ {“ids”: [“bmc”], “total”: null}
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
Two pieces of evidence that the DMARC condition is deliberate rather
than an oversight, which is why I am asking for documentation rather
than for a code change:
-
CHANGELOG.md, 0.12.5 (2025-06-25), under Fixed:
“Antispam: Skip card-is-ham override when sender does not pass
DMARC (#1648).”
The feature shipped in 0.12.0; the condition was added a month later. -
tests/src/system/delivery.rs:275-333 exercises the override using a
contact at[email protected]. The “dmarc-” prefix exists solely
to trigger the #[cfg(feature = “test_mode”)] branch at
crates/smtp/src/inbound/data.rs:749-754, which forces
dmarc_pass = true. The test had to construct a DMARC-passing sender
for the override to fire; the preceding case in the same test, using
[email protected], lands in Junk.
Code path, v0.16.20 (6d5c658914006a25a8a58e11d3cc3afc8474ee3f):
- email/src/message/ingest.rs:262-296 — override requires
is_sender_authenticated - smtp/src/outbound/local.rs:48-50 — sender_authenticated =
flags & (FROM_UNAUTHENTICATED_DMARC | FROM_AUTHENTICATED) != 0 - smtp/src/inbound/data.rs:746-761 — unauthenticated inbound sessions
get MessageSource::Unauthenticated { dmarc_pass } - smtp/src/inbound/data.rs:384 — dmarc_pass needs SPF or DKIM to
return DmarcResult::Pass; with no DMARC record the result is None
What I would ask for, in order of usefulness:
- Document the condition on the Trusted senders page and in the
SpamSettings reference. - Record the skipped override somewhere observable — a reason on
X-Spam-Status, or a trace event. - Possibly a trustContactsRequireDmarc field on SpamSettings,
defaulting to true, for operators who want to make the trade-off
explicitly. I am happy to open this separately under Feature Ideas
if that is the better place for it.
A substantial share of legitimate domains still publish no DMARC record,
so in practice trustContacts is inert for a fair number of
correspondents with no signal to the operator that this is the case.
Client: Thunderbird with all its own junk filtering disabled, so the
classification is Stalwart’s.
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on