Summary

  • RFC 9661 separates uploading script bytes, validating them, storing a SieveScript object and activating one script for an account.
  • A successful SieveScript/set response and isActive=true prove an object-state transition, not that every final-delivery worker loaded the named blob or that a particular message received the intended action.
  • A defensible receipt joins the blob hash, validation capability set, conditional state token, activation response, cross-interface readback, worker generation, test-message trace and observed mailbox result.

The change window ended cleanly. A client uploaded a new Sieve blob, validated it, created the script, and used onSuccessActivateScript to make it active. The response reported the old script inactive and the new one active. The control plane had every reason to turn green. Minutes later, a test message still landed in the folder selected by the old rule.

That does not make the JMAP response false. It exposes the boundary of what the response can say.

RFC 9661 defines a JMAP data model for Sieve scripts. A SieveScript carries a server-set identifier, a unique user-visible name, a blobId pointing to the raw script and a server-set isActive flag. At most one script may be active. The specification makes custody and administrative state much easier to automate. It does not define a universal receipt from the object store through every mail-delivery process to a particular message outcome.

Four acts that dashboards often collapse

The first act is upload. Script content is a blob. Receiving that blob proves possession of bytes under an identifier; it does not make those bytes a stored SieveScript or the active rule.

The second act is validation. SieveScript/validate checks the submitted content without storing it. The method can confirm grammar and the required extensions against the advertised capability set. RFC 5228, however, distinguishes syntax-time from runtime failures. A script that parses can still encounter a transient failure or an invalid combination only when it runs.

The third act is object mutation. SieveScript/set creates, updates or destroys records. The generic JMAP contract in RFC 8620 allows ifInState to reject a write when the collection has changed since the client observed it. That closes a concurrency race at the object boundary. The state token is opaque; it is not a cryptographic statement about what a delivery worker has loaded.

The fourth act is activation. RFC 9661's onSuccessActivateScript makes activation contingent on the requested creates, updates and destroys succeeding. The response must report the newly active script and the one it displaced. This is a valuable atomicity rule. Its scope is the JMAP transaction.

Consistent interfaces can still feed inconsistent workers

RFC 9661 was designed so the same scripts can be reached through JMAP and ManageSieve. That compatibility is useful because administrators need one logical inventory rather than two unrelated stores. It also creates an important test: after activation, both interfaces should identify the same active name and content.

Cross-interface agreement still does not prove execution. A service may persist the right state and publish it through both APIs while delivery workers refresh on a timer, consume an invalidation queue, read replicas with lag, or hold compiled scripts in memory. During a partial rollout, one worker generation may use the new blob while another continues with the old one. The standard does not choose that architecture, so an operator cannot infer its convergence from the protocol response.

The evidence object is therefore more specific than “Sieve is enabled.” It is a tuple: account, script id, blob hash, active-state generation, delivery-worker identity, message trace and result. Without that join, two truthful logs can describe different states of the same service.

The message outcome is a separate state

Sieve runs at final delivery. Its actions can keep a message, file it into a mailbox, redirect it or discard it. Each outcome needs careful proof. The presence of a message in a target folder is stronger than a set response, but it still needs a trace showing that the tested account, recipient, worker and rule matched. The absence of a message is especially weak: upstream rejection, delay, spam processing, routing error or unrelated storage failure can resemble discard.

A robust test injects a uniquely tagged message through the real ingress path, retains the SMTP or submission acceptance record, captures the delivery worker and script generation, and observes the intended mailbox, redirect or discard decision. Where content privacy limits payload capture, a salted test identifier and narrowly scoped outcome code can preserve the join without storing the message.

The result should classify failures instead of flattening them into “rule did not work”: capability mismatch, upload failure, validation error, state mismatch, activation failure, worker not converged, runtime Sieve error, mailbox action failure, redirect failure, quota exhaustion or missing observation.

Special ownership reveals why state needs provenance

RFC 8621 defines JMAP Mail's VacationResponse. RFC 9661 allows a server to represent that response as a Sieve script, but draws a custody line: JMAP Sieve may fetch and activate it, while content changes and destruction through SieveScript/set must be refused. The owning interface remains VacationResponse/set.

That rule prevents two administrative surfaces from silently becoming equal writers. It also shows why a blob needs provenance. An operator should know whether a script was authored through a Sieve editor, generated from a vacation object, restored from backup or changed through ManageSieve. A successful activation without an ownership record can put a legitimate object under the wrong change process.

RFC 9404 adds JMAP blob-management methods, while RFC 9425 exposes quota information. These improve transport and resource visibility. They do not erase the distinction between stored bytes and executed policy. Likewise, the IANA JMAP registry records the Sieve capability, data type and error codes; registration coordinates meaning but cannot attest to a deployment.

Build the receipt from the message backward

Start with the outcome that matters. Identify the test message and the intended action. Capture the worker that handled final delivery and the script generation it reports. Resolve that generation to the active script id and exact blob hash. Retain the activation response, its old and new JMAP states, the ifInState precondition, the validation result and the capability snapshot used during validation. Read the same script back through JMAP and, where supported, ManageSieve.

Then test negative paths. Attempt to activate a nonexistent id and confirm the current script remains unchanged. Attempt to destroy the active script and require sieveIsActive. Exercise a script that is syntactically valid but produces a controlled runtime error. Confirm that a vacation-owned script rejects an update from the wrong surface. Test quota pressure and worker restart. A receipt chain earns trust by preserving failures as well as successes.

Heng Lu's Minimum Initial Specification supplies the right limit: standardize the small interoperable contract, then keep local implementation choices answerable to evidence. Reality Layers keeps an administrative label from borrowing the authority of an operational result. Running-Code Primacy places the decisive observation where the system actually acts—at final delivery.

RFC 9661 gives operators a disciplined way to manage Sieve state. Leadership should neither understate that achievement nor inflate it. The protocol can say which object became active. Only the delivery path can say which rule ran.

Sources