Summary

  • RFC 5293 makes header state action-specific: most tests and delivery actions use the current edited header, while disposition messages and an error-triggered implicit keep use the original unmodified header.
  • The edited and original forms remain the same message for duplicate suppression, so a green script result cannot by itself prove which representation was stored, signed, reported or finally delivered.

Three honest records, one careless noun

An enterprise mail gateway received a signed approval message. Its Sieve script removed an untrusted X-Approved field, added a locally issued classification, filed a copy and redirected another. The mailbox showed the new stamp. A later delivery-status notice quoted the incoming header. A verifier rejected the redirected copy because the edit changed material covered by DKIM.

The incident report said that “the message header” was contradictory. That phrase erased the mechanism.

RFC 5293 defines addheader and deleteheader as operations on a current header state. Order matters. Add a field and a subsequent header test can see it. Delete the first occurrence and every later index refers to the shortened list. Add X-Hello, then delete occurrence one of X-Hello, and the net state can be unchanged. The action log is therefore part of the evidence; the final bytes alone do not reveal every intermediate decision.

Original and current are both normative

The specification deliberately keeps more than one view alive. Actions that store, send or alter a message use the current header set. Tests such as exists and header, and actions such as vacation that inspect fields, see modifications already made in that execution. RFC 6609 extends this continuity through included scripts: edits cross the include boundary in both directions.

But an MDN, DSN or similar disposition message must use the original unmodified header. If an error terminates the script, the required implicit keep also uses the original. A routine implicit keep is conceptually executed after edits unless it was cancelled. “Implicit keep” therefore does not identify one representation without its cause.

This is not an implementation quirk. It is the contract. A receipt that records only script=success or keep=true discards the view-selection rule needed to explain the result.

Identity does not follow representation

RFC 5293 also says that a message changed by addheader or deleteheader remains the same message for weeding out duplicates. Two redundant keeps must not create two mailbox copies. The implementation may choose which redundant action it executes.

That separates three questions often collapsed in operations: Which message identity is being processed? Which header representation did this action consume? Which outcome was observed? Equality for duplicate suppression does not mean byte equality, and byte difference does not necessarily mean a new delivery identity.

Selection is a moving coordinate

deleteheader can match all occurrences or select :index n; :last reverses counting. Counting happens before a value pattern is tested. After one deletion, later indices point somewhere else. A no-match or an index beyond the available fields is not an error.

Local policy may silently ignore forbidden changes. It must never permit deletion of Received or Auto-Submitted, and it must permit edits to Subject. A successful script can therefore contain an attempted mutation that did not happen. The evidence needs the requested action, policy decision and before-and-after hash, not merely the absence of an exception.

A valid edit can weaken a valid proof

Header mutation can invalidate DKIM, including a signature that attests to the absence of a field. Adding a signed-against field is consequential even when the new value is harmless. With S/MIME or OpenPGP/MIME, a signed inner copy may remain intact while the outer header changes. The result is not necessarily cryptographic failure; it may be a dispute between two legitimate representations.

Header syntax does not establish provenance either. A hostile sender can insert an approval-like stamp before the trusted filter runs. Trusted automation must first quarantine or rename untrusted copies, preserve them as evidence, and only then issue a local assertion.

The receipt must name the view

For consequential filtering, retain the immutable original-header hash, the initial ordered occurrence list, the script revision and capability set, and an action-by-action ledger. Each entry should record selector, comparator, match pattern, policy decision, current-header hash before and after, and the representation consumed by the next test or action.

Keep signature results before and after mutation, the duplicate-equivalence key, the implementation’s redundant-action choice, and independent storage or submission acknowledgements. Final delivery remains a later observation. The script selecting redirect is not proof that the recipient accepted, stored or read the message.

Sources