LearnAnatomy of a Governed Call
The Delivery Contract
A grant can fix the digest of the output it pays for; the kernel compares the delivered value before any money moves.
Overview
A delivery contract binds a grant to the output it pays for. The grant carries output_digest_sha256, the SHA-256 digest of the value the tool must return. After the tool returns, the kernel hashes the delivered value and compares it with the grant's digest, inside the durable finalizer and before the first money decision. A match captures the budget hold and signs an Allow. A mismatch releases the hold, captures zero currency, and signs a Deny that the admission record keeps as a terminal state of its own.
The contract is generic kernel behavior for any output-bound grant. Its first consumer is the paid reveal in the Cognition Market, where a buyer receives the committed finding or pays nothing (see The Cognition Market). It adds one step to the sequence in The Mediated Call, between dispatch and reconciliation.
Model
The constraint
Constraint is an adjacently tagged enum in crates/core/chio-core-types/src/capability/scope.rs: a type tag, a value, and deny_unknown_fields. The digest variant serializes as below; the value is the placeholder the design record uses, so it is not a digest. Three variants carry delivery policy.
{"type":"output_digest_sha256","value":"<64 lowercase hex>"}OutputDigestSha256(String), wire tagoutput_digest_sha256: an Allow is valid only if the final post-transform output content hash equals this digest. The value is a canonical lowercase 64-character hex SHA-256 digest.RequireFindingPurchase(Box<FindingPurchaseMarkerV1>), wire tagrequire_finding_purchase: the grant authorizes one purchased finding reveal. The provider-signed marker names the finding and listing being sold and closes over the settlement rail.RequireFindingRecovery(Box<FindingRecoveryMarkerV1>), wire tagrequire_finding_recovery: the grant authorizes a bounded no-charge redelivery of a finding whose paid delivery already settled. Recovery never satisfies a purchase marker and never enters a payment path.
Attenuation of all three variants is structural equality: a child grant preserves the digest only by carrying the identical digest, and a purchase or recovery marker only by carrying the identical marker. A delegation cannot loosen or drop the expected output. Unlike the argument-side constraints, the request matcher only admits the digest variant as a carrier; the comparison runs at the output-aware terminal.
The hold, the operation, and the block
The hold sits on a ReversibleHold payment rail; a mismatch settles by releasing it, so the kernel admits a digest-constrained grant only where a release remains possible. The admission operation is the durable record of the governed call: AdmissionOperationState has 18 states, 7 of them terminal, and denied_after_delivery is the terminal a mismatch reaches. The receipt block is the delivery_contract key of receipt metadata, schema chio.delivery-contract.v1, with every field required and no others admitted. It feeds the receipt id and the signing body, so the enclosing receipt authenticates it and it carries no signature of its own. It appears only on a digest-constrained request, because its presence would change every other receipt id.
How it works
Refusals before dispatch
The kernel refuses a digest-constrained request it could not enforce before it mints a nonce, captures budget, or authorizes payment, and nothing is dispatched.
- A no-output authorization path. Execution-nonce preflight authorizes without producing output, so a selected grant carrying the constraint is denied there.
- A path with no release. After grant selection the kernel reads the payment adapter's rail mode. A request with no durable admission record, a rail other than
ReversibleHold(aPrepaidFinalrail settles inside the authorize call), or a governedMustPrepayrequest is denied beforeauthorize_payment_if_neededruns, so no prepayment is settled for a digest-constrained request. - A downgraded carrier. The request matcher rejects
Constraint::Custom("output_digest_sha256", ..)withInvalidConstraint; merely failing that grant would let an unconstrained sibling serve the same call. A kernel that does not know the variant fails to deserialize the token, because the tag is adjacent and unknown fields are denied, so it denies the call.
Hold, dispatch, hash, compare
The kernel captures one invocation of quota and commits dispatch before the tool runs; that capture is not reversed by a later mismatch. After the tool returns, the operation moves to finalizing and finalize_durable_tool_return in crates/kernel/chio-kernel/src/kernel/admission_coordinator/terminal.rs refuses to start from any other state. It runs the post-invocation transforms, then computes the content hash of the final output: receipt_content_for_output takes the SHA-256 of the canonical JSON bytes of the whole response value. That hash is the observed digest.
evaluate_delivery then calls delivery_denies_settlement in chio_kernel_core::formal_core, a pure function that denies unless the output is a single value whose digest delivery_contract_admits finds byte-identical to the expected digest. The verdict becomes the terminal decision, the decision is folded into post_guard_decision_digest, and only then does durable_payment_disposition plan the payment. Nothing financial happens before the comparison.
docs/adr/ADR-0019-kernel-delivery-contract.mdat fe56570The two outcomes
On a match the operation completes, the hold is captured for the realized cost, and the kernel signs an Allow whose delivery_contract block reads result: matched. On a mismatch the operation moves to denied_after_delivery, which is legal only from finalizing. The settlement disposition is ContractualZeroCharge: the open hold is released, zero is captured, and realized spend reconciles to zero. The kernel signs a Deny whose decision.guard is delivery_contract, with the block reading result: mismatched. A mismatch therefore consumes one invocation of quota and zero currency; the Deny receipt's financial.cost_charged is zero. A replay of the terminal that finds the same release already recorded in the payment journal issues no second release. The variant's own doc comment reads: The delivered output did not match a grant's committed output digest. A signed Deny is persisted; the open hold is released and zero is captured.
pub enum AdmissionOperationState {
Prepared,
BrokerAttemptRegistered,
ApprovalRequired,
BudgetAuthorized,
ApprovalReserved,
ReadyToDispatch,
CapturePending,
DispatchCommitted,
Finalizing,
Completed,
CompensatedBeforeDispatch,
NotAcceptedAfterDispatchCommit,
OutcomeUnknownAfterDispatch,
/// The delivered output did not match a grant's committed output
/// digest. A signed Deny is persisted; the open hold is released and
/// zero is captured.
DeniedAfterDelivery,
MutationReady,
MutationSubmitted,
EconomicMutationApplied,
EconomicMutationNotApplied,
}A mismatched Deny does not confirm the payload
content_hash and observed_digest are a domain-separated commitment keyed by the expected digest, so a candidate payload cannot be checked against the Deny. The kernel merges the block last and rejects caller or hook metadata that already carries a delivery_contract key. The block below shows the schema's shape; its two digest values are the schema's own pattern, not a digest.{
"schema": "chio.delivery-contract.v1",
"expected_digest": "^[0-9a-f]{64}$",
"observed_digest": "^[0-9a-f]{64}$",
"result": "matched"
}Two error URNs in spec/errors/registry.yaml name the refusals. urn:chio:error:kernel:delivery-contract-unsupported-carrier is a request that carried a delivery contract the evaluator cannot enforce; urn:chio:error:kernel:delivery-contract-digest-mismatch is a delivered output that did not hash to the frozen expected digest.
Guarantees and limits
Status: shipped on the durable admission path, in finalize_durable_tool_return and the replay path that rebuilds the same receipt from persisted state; the non-durable path enforces nothing after dispatch, so it refuses the constraint before dispatch. The formal claim is narrower than the shipped behavior; the table states its boundary, and three limits follow it.
| Field | Value |
|---|---|
| Status | proved |
| Claim | Settlement is admitted only on an Allow, an Allow under a digest requirement implies the expected and observed digests are equal, and a deniedAfterDelivery decision never opens the settlement gate. Over the Rust functions, a constrained delivery that is not denied is a single-value delivery with byte-identical digests. |
| Subject | The bounded Lean model Chio.Proofs.DeliveryContract over four opaque digest identities, and the pure functions delivery_contract_admits and delivery_denies_settlement in chio_kernel_core::formal_core. |
| Evidence | Theorems settlement_admission_requires_verified_evidence, allow_requires_verified_evidence, and denied_after_delivery_cannot_settle in formal/lean4/Chio/Chio/Proofs/DeliveryContract.lean; the Kani harness public_delivery_contract_allow_implies_digest_match registered in .kani/harnesses.toml. |
| Assumptions | Digest identities are opaque and already validated. The model's finding-purchase Boolean is an input that records the composition boundary with purchase admission; it is not a claim that the model verifies a purchase. |
| Limit | The model does not implement SHA-256, canonical JSON, signatures, persistence, payment rails, or external settlement, and it is not a refinement proof of the Rust finalizer. The Kani harness covers the comparison functions, not the state transition or the hold release around them. |
- One value, not a stream. A digest can be committed only against a single canonical JSON value. A
Streamoutput hashes the concatenated per-chunk digests the provider authors, so under a constraint it denies even when the hashes collide. A no-output Allow hashes tosha256_hex(b"null"), which the admission gate rejects as an expected digest. - The comparison is transform-aware. It runs over the final post-transform value, so a post-invocation transform that changes the output produces a mismatch and a zero-charge Deny.
- It records that the value differed, not why. The block is generic delivery evidence. A finding reveal attaches a sibling
finding_deliveryblock for the purchase-specific checks, and a generic digest denial alone triggers no seller sanction.
Next steps
- Paid Reveal · the reveal and settlement path the contract was built for
- Receipts · the signed record the
delivery_contractblock sits in - Budgets & Metering · holds, invocation capture, and reconciliation
- Authoritative Spend · the four checks a receipt passes before its spend counts