Disabled MTA-STS still advertises DNS records and panics serving mta-sts.txt (Mode::None unreachable!() in Display)

Issue Description

(Assistance from Claude)
With MTA-STS disabled (mtaSts.mode = disable), Stalwart v0.16.13/v0.16.14 still (a) builds an in-memory MTA-STS policy object with Mode::None, (b) advertises mta-sts. / _mta-sts. records for every domain in its DNS zone output, and (c) panics whenever a remote MTA fetches https://mta-sts.<domain>/.well-known/mta-sts.txt:

thread ‘tokio-rt-worker’ panicked at crates/common/src/config/smtp/resolver.rs:365:27:
internal error: entered unreachable code
In production this was a continuous panic storm (Gmail’s policy fetchers retry in bursts of ~4, all day, on both cluster nodes).

Expected Behavior

No crashes/errors.

Actual Behavior

Chain (v0.16.13 source)
Policy::try_parse (crates/common/src/config/smtp/resolver.rs:261): when mta_sts.mx_hosts is empty it falls back to SystemSettings.mail_exchangers / default_hostname — effectively never empty — and maps PolicyEnforcement::Disable → Mode::None, returning Some(policy) even though the feature is disabled. So session.mta_sts_policy is Some with mode: None.
The DNS record generator (crates/common/src/network/dns/records.rs:123, DnsRecordType::MtaSts) only checks is Some — it emits mta-sts. CNAME + _mta-sts. TXT “v=STSv1; id=…” for every domain, advertising MTA-STS that the operator disabled.
The HTTP handler (crates/http/src/request.rs:328, (“mta-sts.txt”, GET)) calls policy.to_string(); impl Display for Policy (resolver.rs ~line 360) has:
match self.mode {
Mode::Enforce => f.write_str(“enforce”)?,
Mode::Testing => f.write_str(“testing”)?,
Mode::None => unreachable!(),
}
→ panic on every fetch. Fetchers get a dropped connection and retry.

Expected
mode = disable should mean: mta_sts_policy = None, no mta-sts./_mta-sts. records emitted, and /.well-known/mta-sts.txt returning 404. Alternatively (RFC 8461 §5 offers mode: none as a real value for graceful shutdown), Display could legitimately serialize mode: none — but then the config needs to distinguish “publish mode:none” from “feature off”, and unreachable!() must go either way.

Reproduction Steps

mtaSts settings: {“mode”:“disable”,“mxHosts”:{}} (defaults otherwise), at least one domain + mail exchanger configured.
Observe the generated DNS records still include _mta-sts.<domain>.
curl -sk https://<server>/.well-known/mta-sts.txt → connection dropped; server log shows the panic above.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

FoundationDB

Blob Storage

S3-compatible

Search Engine

Internal

Directory Backend

Internal

Additional Context

Workaround
Set mode = testing — Display becomes serializable and the panics stop; but disabled-yet-advertised remains the underlying bug. Also note the _mta-sts TXT id= in already-written DNS zones is not refreshed when the policy changes.

Environment: Stalwart v0.16.13 (also present in v0.16.14 source), FoundationDB build, 2-node cluster, Linux x86_64.

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

Your analysis matches the current source. In crates/common/src/config/smtp/resolver.rs, Policy::try_parse maps PolicyEnforcement::Disable to Mode::None and still returns Some(policy) (falling back to SystemSettings.mail_exchangers/default_hostname when mx_hosts is empty, so it’s effectively never empty), and the Display impl for Policy only handles Mode::Enforce/Mode::Testing and hits unreachable!() for Mode::None:

match self.mode {
    Mode::Enforce => f.write_str("enforce")?,
    Mode::Testing => f.write_str("testing")?,
    Mode::None => unreachable!(),
}

That’s a genuine defect: a disabled MTA-STS policy shouldn’t produce a Some(Policy) with Mode::None in the first place, and even if it can, Display panicking instead of handling none (a valid RFC 8461 value) is wrong. Your workaround of using mode = testing to stop the panics is correct as a stopgap, but it doesn’t fix the DNS records still being advertised when the feature is meant to be off.

This is a code-level bug rather than a documentation or configuration issue, so it isn’t something the docs can resolve. Flagging it here for a human/staff follow-up on the fix.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.15 (yet to be released).