Issue Description
When a DKIM rotation is due and writing the new key’s DNS record fails, or the propagation check does not confirm it, the new key is stored as Pending, but the current Active key of that algorithm is still moved to Retiring in the same task run. Only Active keys are used for signing, so that algorithm stops signing. If publication fails for all algorithms being rotated and no other Active keys remain, subsequent outgoing messages carry no DKIM-Signature header.
Code (v0.16.24):
- crates/services/src/task_manager/dkim.rs L214-257: when set_rrset fails or wait_for_txt_propagation returns false, the new key is set to Pending and rescheduled (“Something went wrong, reschedule”).
- crates/services/src/task_manager/dkim.rs L372-385: every due Active key collected in retiring_signatures is set to Retiring unconditionally, without checking that a successor for the same algorithm became Active.
- crates/common/src/cache/principals.rs L917-924: signers are built only from keys whose stage is Active.
Repeated publication failures can lead to six-hour retry intervals, prolonging the period without an Active signing key.
Expected Behavior
As documented in DKIM key rotation | Stalwart : while the new key is Pending, “Signing continues with the current active key”, and only when DNS propagation is confirmed does the new key become active and the previous key become retiring. A failed publication should leave the current key Active, and the failure should be logged at WARN or ERROR.
Actual Behavior
After a rotation in which publication or the propagation check fails for every algorithm: the new keys are Pending, the previous keys are Retiring, no key is Active, and messages submitted afterwards carry no DKIM-Signature header. No WARN or ERROR reports that the domain has been left without an Active signing key.
Reproduction Steps
- Domain with DKIM management Automatic (Ed25519 + RSA) and DNS management Automatic with a DNS provider (Cloudflare), with DKIM included in the records to publish (publishRecords).
- Starting state: the existing keys are Active and a control message submitted through the submission listener carries their DKIM-Signature headers.
- Make the DNS write fail while the DNS provider configuration stays valid, so the updater is built and the failure happens at set_rrset (e.g. no network access to the provider’s API). A configuration so broken that the updater cannot be built ends the task earlier with “Failed to build DNS updater” and does not reach this code path.
- Trigger the documented on-demand rotation: set nextTransitionAt of the Active keys to now and create a DkimManagement task for the domain (x:Task/set).
- After the task runs, x:DkimSignature/get shows the new keys Pending, the previous keys Retiring and no Active key.
- Submit another message: it is delivered without any DKIM-Signature header.
Stalwart Version
v0.16.x
Installation Method
Binary (Linux)
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
Suggested fix: retire an Active key only after a successor for the same algorithm has become Active; log at WARN or ERROR when publication fails or when a domain is left with no Active signing key.
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