Changes for version 0.14 - 2026-10-07
- The Verifier and MessageInstance->chain_verifies walk the header history past an instance whose body Recipe is null: the body is unrecoverable there, but every lower instance's header hashes are still checked down to m=1. (t/mi-null-header-history.t)
- Validate (and bin/validate.pl): a null body Recipe no longer fails the chain; the header history below it is checked, and the lower levels report body_hash 'not-checked'. (t/validate-null-body.t)
- dkim2-milter --allow-null-body-recipe (default off): a message whose top Message-Instance has a null body Recipe is no longer signed silently; it is refused (X-DKIM2-Info not-signed=null-body-recipe) unless the option is set, and signed with X-DKIM2-Info null-body-recipe when it is.
- bin/dkim2sign no longer signs blindly over an existing chain: like dkim2-milter it verifies the upstream DKIM2-Signatures (an unsigned top Message-Instance is allowed) and that the Message-Instance chain undoes cleanly, and refuses (exit 1, nothing on stdout) otherwise. A top null body Recipe is refused unless --allow-null-body-recipe. New --dns-json (default $DKIM2_DNS_JSON) and --ignore-timestamps. The decision is the new Mail::DKIM2::Gate, shared with the milter; the Signer library itself still signs ungated. (t/sign-cli.t)
- nd= bridge: when the top DKIM2-Signature carries nd=, the Gate (and so dkim2sign and dkim2-milter) signs only if nd= equals the signing d= (case-insensitive) and otherwise refuses with "top signature nd=X names another domain". Verifier gains next_domain_ok($d) / NextDomainOK; Gate->check takes SigningDomain. Every non-nd case behaves as before. (t/sign-cli.t, t/milter-script.t)
- A Message-Instance whose r= has a "b" that is neither null nor an array (e.g. 5, "x", {}) is now a PERMERROR instead of being read as a null body Recipe; likewise an "h" that is not an object. (t/mi-null-recipe.t)
- A body Recipe's structure (integer bounds, ascending, no overlap) is validated even when a null body Recipe above it means it is never applied; a malformed one below a null now fails the chain. (t/mi-null-header-history.t)
- DKIM2_DATE is 2026-10-07: what the milter signs and how the Verifier judges a null body Recipe changed.
Documentation
Standalone DKIM2 milter for Postfix
Modules
DKIM2 signing and verification for email
Canonicalization, hashing, folding and key handling for DKIM2
DKIM2-signed Delivery Status Notifications
decide whether a front end may sign a message
Streaming message parser base for Signer and Verifier
Compute, verify and undo Message-Instance headers
Keep message snapshots keyed by Message-Instance
verify-and-reflect DKIM2 demonstration logic for dkim2.com
One DKIM2-Signature header, parsed or under construction
Sign a message with a DKIM2-Signature header
Bcc-safe recipient grouping before DKIM2 signing
The tag=value list a DKIM2 header is made of
structured per-level DKIM2 and Message-Instance report
Verify the DKIM2-Signature chain on a message
Handler class for DKIM2 signing
Handler class for DKIM2 signature verification