Skip to content

feat: select DKIM signers by sender domain - #18

Merged
iamd3vil merged 3 commits into
mainfrom
feat/multi-domain-dkim
Sep 29, 2026
Merged

iamd3vil merged 3 commits into
mainfrom
feat/multi-domain-dkim

Conversation

@iamd3vil

@iamd3vil iamd3vil commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Hedwig currently has one global DKIM signer, so independently signing mail for multiple domains requires separate instances. This adds per-domain signing in one instance: From: alice@example.com selects the example.com key, while From: bob@another.com selects the another.com key.

  • Configure identities with [[server.dkim]]. Matching uses the message From header, is case-insensitive and exact, and rejects missing, malformed, ambiguous, or unconfigured senders with 550 5.7.1 before queue acceptance.
  • Keep existing [server.dkim] configurations signing all outgoing messages with one key. Omitting DKIM configuration retains existing relay behavior; no migration is required.
  • Prepare all signers before atomically publishing a reload. Pending messages use the current signer; removing a domain defers its queued messages under the existing bounded retry policy.
  • Add domain selection to dkim-generate, configuration examples, documentation, and a Docker verifier covering both queue backends. Key generation retains the configured algorithm when overriding a selector; overriding a single configured domain or changing the configured algorithm requires an explicit private-key path.
  • Preserve field-specific deserialization errors in TOML and HUML and include the DKIM entry index in domain-validation and key-preparation failures. Add regression tests for rejected-DATA pipelining and rejection before storage or dispatch. Both queue backends share capped retry backoff to avoid exponent overflow at high attempt counts.

To enable domain selection, replace the single table with one array entry per domain, configure each private key, and publish each domain's DKIM DNS record. SMTP users still share the configured sending identities; per-user domain authorization is outside this change. Switching to array mode applies stricter From validation to pending mail; malformed or ambiguous messages bounce, while valid unconfigured domains retry. Drain the queue first to retain the previous rules for pending messages.

Validation:

  • cargo fmt --check and git diff --check
  • cargo test --workspace: 334 unit tests and one doc test passed
  • cargo clippy --workspace --all-targets: completed with nine existing test warnings in metrics and log-queue state
  • uv run --with dkimpy --with pynacl dev/verify-multi-domain.py tmp/config-reload-e2e/target/release/hedwig: passed for log and filesystem queues; cryptographically verified RSA/Ed25519 signatures, From-based routing with a different envelope sender, rejection and session reuse, key rotation, failed-reload rollback, single-table compatibility, and deferral followed by retry exhaustion after queued-domain removal

Support multiple DKIM identities through an array of domain configurations, selecting each signer from the message From header. Preserve global signing for existing single-table configurations.

Reject ambiguous or unconfigured senders before queue acceptance, reload signer registries atomically, and defer queued messages when their signing domain is removed. Add domain-aware key generation, documentation, and an end-to-end verifier for both queue backends.

Validation: cargo fmt --check, cargo test --workspace (328 unit tests and one doc test), and cargo clippy --workspace --all-targets (existing warnings only). Docker verification passed for both log and filesystem queues, including RSA/Ed25519 signature verification, reloads, sender rejection, and queued domain removal.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Deploying hedwig with  Cloudflare Pages  Cloudflare Pages

Latest commit: 494a4b1
Status: ✅  Deploy successful!
Preview URL: https://ff3a8282.hedwig-1mq.pages.dev
Branch Preview URL: https://feat-multi-domain-dkim.hedwig-1mq.pages.dev

View logs

Preserve field-specific TOML and HUML deserialization errors and report the failing DKIM entry during key preparation. Retain configured key algorithms when generating replacement keys, and require an explicit key path when overriding a single configured domain.

Document strict validation of queued messages when switching signing modes. Correct deferred-attempt logging and share retry-delay calculation. Add regression coverage for configuration errors, key-generation defaults, rejection before queue writes, and pipelined SMTP recovery after rejected DATA.

Validation: 332 unit tests and one doc test passed; cargo fmt and cargo clippy --workspace --all-targets completed with existing warnings only. Docker verification passed for log and filesystem queues, including cryptographic DKIM verification and removed-domain deferral through retry exhaustion.
Require an explicit private-key path when changing a configured DKIM algorithm in either configuration mode. Identify invalid and duplicate domain entries in preparation errors, and document algorithm rotation requirements.

Share bounded retry backoff across filesystem and log queues to prevent exponent overflow at high attempt counts. Cover both algorithm transitions, unchanged algorithms, domain error indexes, and retry-count boundaries with regression tests.

Validation: 334 unit tests and one doc test pass; formatting and clippy complete with existing warnings only. Docker verification passes for both queue backends, including RSA/Ed25519 signatures and removed-domain retry exhaustion.
@iamd3vil
iamd3vil merged commit 1eb1a96 into main Sep 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant