← Back to Home

Zero-Knowledge Mandates (ZKM) Protocol Specification

Privacy-preserving spend authorization for autonomous agents

Version 0.1.0-draft | Status: Draft | License: Apache 2.0 | Domain: ZKM.dev

Date: 2026-08-04 | Proving System: Noir / UltraHonk (BN254)

Abstract

Zero-Knowledge Mandates (ZKM) is the authorization layer of the ZK protocol family: it lets an autonomous agent prove that a spend satisfies a hidden mandate's cap, expiry, policy, and non-revocation constraints while conserving total issued authority. ZKC decides whether parties may interact; ZKM decides whether a spend is authorized; ZKA decides whether it settles.

Introduction (informative)

This introduction explains why ZKM exists and the design boundary it occupies. It adds no conformance requirements; the numbered sections below define the protocol.

Delegating payment execution creates an authorization problem: control of spendable value is not, by itself, evidence that a particular spend falls within authority granted by the party that owns or governs that value. A counterparty can trust the agent or its operator, or demand a conventional mandate. The former is unverifiable. Conventional mandate and attenuated-capability presentations may be linkable or disclose authority constraints and delegation relationships; deployments over transparent rails can additionally expose settlement data. ZKM exists to make authorization verifiable at settlement without making the principal or its authority graph public.

The central design fact is that a spending cap is consumable state, not a static credential claim. A mandate must support partial spends and parallel delegation while conserving total authority, enforcing expiry, policy, and revocation at execution time, and revealing neither the original cap nor the remaining balance or spend history. ZKM therefore represents mandate authority as private committed state: in the spend-conjoined form, a spend proof consumes mandate authority atomically with ZKA value movement, while non-spend transitions split, delegate, rotate, or retire authority without moving value (§§4–5).

A ZKM mandate is self-issued: the base issuance prover demonstrates knowledge of issuance authority, not an attestation from an external issuer. In the base profile, the principal is a protocol role; the base issuance proof neither authenticates a human or external principal nor requires the principal and agent to be different. Deployments that need a credentialed principal use the credential-bound issuance statement zkm:issuance-zkc:v1 (§5). Non-revocation means absence at an accumulator root accepted under the deployment's disclosed freshness policy, not instantaneous recall (§7).

ZKM fills one boundary in the protocol family. ZKC decides whether parties may interact; ZKM decides whether a spend is authorized; ZKA decides whether it settles. AFP's bilateral negotiated limit envelopes may compose with ZKM's unilateral, principal-issued mandates, but neither subsumes the other (§8). Once effective under the disclosed freshness policy, revocation withdraws authority to make new spends; it does not freeze, seize, redirect, or otherwise encumber value (§9).

Conformance Language

The key words MUST, MUST NOT, REQUIRED, SHALL, SHOULD, SHOULD NOT, MAY, and OPTIONAL are to be interpreted as described in RFC 2119 / RFC 8174 when, and only when, they appear in all capitals.


1. Overview

1.1 Design Goals

  1. Principal-hidden authorization. A verifier learns only: valid mandate · unrevoked · within remaining cap · unexpired · policy satisfied — never the principal's identity, the original cap, or prior spend history.
  2. Stranger verifiability. Mandate proofs verify outside any federation or bilateral agreement, including on-chain (Solidity/UltraHonk verifiers).
  3. Conservation of authority. No sequence of operations — spends, splits, delegations, in any interleaving — can consume more value authority than the principal issued (§4).
  4. Principal-only revocation. Only the holder of the mandate's issuance authority can revoke it; revocation kills the entire delegation subtree (§7) and can never be exercised by an operator or third party.
  5. Reuse by reference. ZKC v0.4.1-draft supplies epoch-pattern and envelope/encoder conventions, but not ZKM issuance or accumulator semantics (§2); key derivation is KDC's; nullifier and note machinery, recursion, and settlement are ZKA's. ZKM's novel normative surface is the mandate artifact, the consumption state model, and the authorization predicate.

Positioning against prior art: AP2 v0.2 is selectively disclosable rather than structurally clear-text. It uses SD-JWT VCs and enforces constraint narrowing at the authorization layer: an agent discloses only the constraints needed to evaluate a closed mandate, and the verifier checks that the closed mandate does not exceed them. SEPA-SDD is a clear-text mandate scheme. AP2 presentations nevertheless remain linkable, and AP2 samples over transparent rails such as x402 expose settlement data publicly. Appendix A works [AP2] through field by field, including the trust boundary at which an AP2 mandate may be bridged into a ZKM self-issuance without reintroducing an external attestor; the attenuated-capability family that the DIF delegated-authority reports identify as the leading substrate for agent delegation (ZCAP-LD, UCAN, Macaroons, Biscuit) enforces per-hop narrowing but discloses the delegation chain to the parties that verify it — "authorized parties" in the DIF framing [DIF-THREATS], which under ZKM's stranger-verifiability goal would mean every counterparty. ZKM is the zero-knowledge counterpart of that family: narrowing and execution-time evaluation are preserved — in-circuit — while the principal, the original and remaining caps, the policy parameters, and the delegation graph are never disclosed (the §5 public inputs — asset_id, settlement context, current_time, the roots, the transition kind with its successor arity, and the pseudorandom nullifier and commitment values — are the whole disclosure; kind is priced as §4.6 residual surface (d), and the per-spend amount left this list in rev 7). Goals 1–4 answer the authority-state core of what [DIF-THREATS] names Governance TOCTOU ("the authority checked at issuance is not the authority present at execution"): for the inputs ZKM evaluates — cap, expiry, revocation, policy — the execution-time evaluation is carried in the settled artifact itself, proven at spend time rather than checked by an enforcement point at session establishment. The threat model's broader runtime governance signals are out of scope (§1.3).

1.2 Freedom Safeguards

ZKM inherits the six Freedom Safeguards of ZKA §1.6 in full. Gating value is ZKM's purpose; §9 carries the analysis of why this is safeguard-consistent (the principal is the holder of the authority being revoked — an owner controlling their own delegation, never an operator controlling another's funds). Per Safeguard 4, every ZKM circuit — including every mandate policy predicate — MUST be published as inspectable, versioned Noir source with a public review period; an SDK MUST refuse by default to prove against a policy predicate the agent cannot inspect.

1.3 Non-Goals


2. Cryptographic Preliminaries

By reference, not restatement:

Field encodings and serialization. A BN254 field element serializes as its canonical 32-byte big-endian representation (the ZKA convention; ZKA §2.1 / KDC §A.3 baseline); in JSON envelopes it is carried as a lowercase-hex, 0x-prefixed, 64-hex-character string (the ZKC schema convention). Public-input vectors flatten per-circuit into a sequence of field elements in main's pub-parameter declaration order — booleans as 0/1, unsigned integers zero-extended to one element — following ZKC's per-circuit encoder-document convention; ZKM ships one encoder document per VK alongside the §6 deployment registry entry.

Tag ownership (resolves the registration item). Per KDC §A.4's tag-collision rule, a referencing specification's additional domain tags are owned by the defining specification, not by KDC — there is no central registration step. The zkm/* tags above are therefore ZKM-owned: they MUST NOT collide with the KDC registry or with ZKA/ZKC operational tags (checked against both), and their constant derivation follows ZKA's deployed operational-separator convention (the Blake2b512 reduction above, as ZKA's published zka/* values do) rather than KDC §A.3's UTF-8-BE encodeTag — a divergence ZKA's own published separators already carry; ZKM mirrors deployed practice and flags the discrepancy as ZKA's to reconcile. The KAT-guarded values in noir/zkm_lib/src/lib.nr are no longer provisional-pending-registration; they are the defined values.


3. The Mandate Artifact

A mandate is a principal-issued, scoped grant of spend authority, represented on the wire and in state only as a commitment. The opening (known to the holder) has the following fields:

Field Type Meaning
root_id Field Stable identifier of the original issuance, preserved across every successor and sub-mandate (§4); the revocation anchor (§7). Derived H_{zkm/mandate_root_id/v1}(principal_issuance_key, issuance_randomness), fields in exactly that order.
settlement_domain Field The settlement target this mandate authorizes (chain / contract / protocol id). Bound into the commitment, inherited by successors, and asserted equal to the public settlement_context at transition — a mandate issued for one domain is unwitnessable on another, so cross-domain authority multiplication is impossible regardless of nullifier-set topology.
asset_id Field ZKA registry asset encoding (zka-registry canonical form).
remaining_cap u128 Remaining spend authority, in the asset's atomic units.
expiry u64 Settlement-layer timestamp bound; MUST NOT extend across successors.
policy_hash Field Hash of the versioned, published policy predicate circuit + parameters (Safeguard 4).
holder_key Field The agent key authorized to consume this mandate (KDC-derived). Rebound on delegation (§6).
depth u8 Delegation depth; 0 at issuance, +1 per delegation (§6).
randomness Field Commitment blinding. Fresh per successor state.

Commitment: cm = H_{zkm/mandate_commitment/v1}(root_id, settlement_domain, asset_id, remaining_cap, expiry, policy_hash, holder_key, depth, randomness).

Policy hash: policy_hash = H_{zkm/policy/v1}(version, parameters…, policy_blinding) — the predicate circuit + parameters, blinded. The blinding term is REQUIRED: without it the preimage is low-entropy (version, a small ceiling, a boolean, a set root), so the public, lineage-stable policy_hash would disclose the parameters by dictionary attack and act as a cross-spend correlator (§4.6). policy_blinding MUST be non-zero. See §5 for ZKM Standard Policy v1.

Nullifier: nf = H_{zkm/mandate_nullifier/v1}(holder_nullifier_secret, cm, settlement_context) — mirroring ZKA's note-nullifier shape so settlement layers can enforce uniqueness with the machinery they already run. holder_nullifier_secret MUST be deterministically derived (KDC, domain tag zkm/mandate_nullifier_key/v1) from the private key corresponding to holder_key, and every transition proof MUST prove this derivation. Without this binding, a holder could emit distinct nullifiers for the same cm under different secrets and defeat spend serialization (§4.4). settlement_context is the public settlement-domain binding (§5): folding it into the preimage makes nullifiers domain-separated, so a transition proven for one settlement target cannot be replayed on another. holder_key itself is derived from the same private key under zkm/holder_key/v1. Zero is reserved as the "field absent" sentinel throughout: asset_id, policy_hash, root_id, and every holder key MUST be non-zero, and conforming circuits MUST refuse to transition a mandate violating this.

Issuance binds root_id to the principal's issuance key without revealing it — and the binding fact is preimage knowledge, not a signature. ZKC-Core's issuance-signature primitive (ECDSA/secp256k1 circuit profile, ZKC v0.4.1-draft §2.3) exists because a ZKC issuer and holder are different parties; ZKM's issuer is the principal (§1.1), so there is no second party to sign for. The earlier by-reference framing is corrected accordingly.

Issuance proof statement (implemented: noir/zkm_issuance, public vector (cm, settlement_context)): the inserted commitment opens to a well-formed depth-0 note (depth is a circuit constant — issuance can never mint mid-chain depth, which would forge a delegation hop) whose fields satisfy every §3 sentinel rule, whose settlement_domain equals the public settlement context, and whose root_id is H_{zkm/mandate_root_id/v1} of issuance material the prover knows. The settlement layer MUST insert a mandate leaf only against a verifying issuance proof. Preimage knowledge is what §7.1 calls "the principal's issuance authority", so this proof is the guarantee that every accepted leaf has a living revocation authority. root_id is deliberately not a public input: publishing it at issuance would hand observers the revocation-watch handle for the subtree (§4.6); it surfaces only if the principal revokes (§7.1). The proof deliberately does NOT establish who the principal is or that the principal differs from the agent — self-issued authority means an agent MAY be its own principal; a counterparty needing "a principal holding credential X" uses the current ZKC v0.4.1-draft same-subject issuance composition (zkm:issuance-zkc:v1) described under §5 ZKC same-subject composition. Nothing bounds the issued cap: authority is only exercisable over value the holder custodies at spend time (§5), so an inflated cap authorizes spending of nothing.

Ceremony and principal key lifecycle. The issuance key SHOULD be KDC-derived from the principal's seed under zkm/issuance_key/v1 (§2; defined and KAT-guarded in zkm_lib; the issuance circuit takes the key as a witness however derived — the tag is the SHOULD-level derivation convention, not an in-circuit assertion). issuance_randomness MUST be fresh per mandate — reuse links root_ids across mandates if the key is ever disclosed, and weakens §4.6's cross-mandate unlinkability. The principal MUST retain (principal_issuance_key, issuance_randomness) per outstanding mandate: losing them is losing the ability to revoke — the revocation-side analogue of the §6 burn hazard, and like it, silent until exercised. Key rotation is prospective: new issuances derive under the new key; outstanding mandates keep their root_ids, so the principal retains old preimages until the last mandate under them expires — or pre-revokes before destroying key material.


4. Cap-Consumption State Model (normative direction)

4.1 The problem

A cap is a consumable, not a fact. "Agent may spend up to 1000" must support many partial spends, by concurrent sub-agents, verified by strangers, with no trusted party tracking a balance — while revealing neither the original cap, the remaining balance, nor the spend history, and while making over-consumption cryptographically impossible, not merely detectable.

4.2 Model: the mandate note tree (consumable-note / change-note model)

A mandate is held as a mandate note — a commitment in a mandate state tree maintained by the settlement layer, structurally parallel to ZKA's value-note tree. Consuming authority follows the UTXO change pattern:

Tree custody (resolves §10.5): the mandate state tree is per settlement target, maintained by the target alongside its ZKA note tree — never a shared cross-target tree. The reasons are already load-bearing elsewhere: §4.3-4's atomic nullifier-plus-successor acceptance requires the tree to live where transitions settle; §4.3-4/§5 require roots recognized in the target's own history; and settlement_domain binding (§3) already makes a mandate unwitnessable off its target, so a shared tree would add a cross-target coordination layer while buying nothing. Mandate transitions are settlement-layer events; consuming mandate authority inside payment channels (APL) is out of scope for v0.x and tracked in §10.

4.3 Transition invariants (normative)

Every transition proof MUST enforce, in-circuit:

  1. Conservation of authority. Σ remaining_cap(successors) + spent_amount + retired_amount = remaining_cap(input). For SPEND: one successor, spent_amount = a, retired_amount = 0. For SPLIT: two successors, both other terms zero. For RETIRE: no successors, spent_amount = 0, retired_amount = remaining_cap(input) — authority is destroyed, never spent. For DELEGATE (§6): full rebind — one successor with remaining_cap(successor) = remaining_cap(input), both other terms zero; partial delegation — two successors as SPLIT, both other terms zero. For ROTATE (§6): one successor with remaining_cap(successor) = remaining_cap(input), both other terms zero. No transition consumes or destroys authority except SPEND (settled) and RETIRE (explicit).
  2. Bound check. Every cap and amount value — a, remaining_cap(input), and each remaining_cap(successor) — MUST be range-checked to the u128 domain with assertion-bearing constraints before any comparison or subtraction (beware the known dead-code-elimination hazard: a range check that carries no assertion can be silently eliminated), and a ≤ remaining_cap(M) MUST be asserted.
  3. Monotone narrowing. Successors preserve root_id and asset_id; expiry(successor) ≤ expiry(input); policy_hash is preserved or replaced by a narrowing of the input policy, proven in-circuit against the §5 narrowing registry (enforced on the mandate-only VK as of rev 6; the spend-conjoined VK deliberately preserves strictly — narrowing accompanies authority distribution, not spending); depth is preserved (SPEND/SPLIT/ROTATE) or incremented (DELEGATE, §6), and a DELEGATE transition MUST assert depth(successor) ≤ depth_max, where depth_max is a deployment-disclosed bound (≤ 255 — no u8 wrap), realized as a constant baked into the verification key — the deployment discloses the bound by shipping the VK; it MUST NOT be a prover-supplied input, which would make the bound vacuous. Successor commitments MUST be pairwise distinct and distinct from the consumed input commitment (a successor identical to the consumed note re-inserts a leaf whose nullifier is already spent — silently destroyed authority), and a DELEGATE or ROTATE MUST rebind to a non-zero holder key different from the consumed note's; a full-rebind DELEGATE and a ROTATE additionally carry the §6 possession companion proof.
  4. Membership + nullification. M is proven a member of the mandate state tree at a recent root, and nf(M) is emitted; the settlement layer MUST reject any transition or spend whose mandate nullifier has been seen, and MUST apply nullifier insertion and successor-commitment insertion atomically — a transition is accepted in full or not at all, so authority can never be consumed without its successor existing. Of two conflicting transitions of the same note, the settlement layer's canonical ordering accepts exactly one. Under chain reorganization, mandate nullifiers and successor commitments carry the SAME finality semantics as the target's ZKA note nullifiers — ZKM defines no finality model of its own; a deployment MUST apply the settlement target's ZKA finality policy (per the ZKA adapter certification's finality-policy enforcement) to mandate state, and downstream consumers SHOULD treat a pre-finality successor as provisional exactly as they would a pre-finality ZKA note.
  5. Non-revocation. The proof carries non-membership of root_id in the revocation accumulator at a root the verifier recognizes (§7); the verifier derives that root's epoch from its own accumulator history — the root states the epoch, never a prover-supplied label — and applies its freshness policy (§7.2). The revocation slot MUST be a canonical field decomposition of root_id; a non-canonical decomposition would let a prover open a different (empty) slot for a revoked mandate.
  6. Temporal validity. current_time < expiry(M), where current_time is the settlement target's clock (block timestamp or equivalent). ZKM defines no clock of its own.

4.4 Why nullification is the serialization mechanism

Two concurrent spends of the same mandate note both emit nf(M); the settlement layer accepts at most one. Over-consumption by racing is therefore impossible by construction — the loser's transition is simply rejected, and no balance oracle, sequencer, or coordinator is needed. The cost is that a single mandate note serializes its consumers: parallelism is obtained by pre-splitting (§4.5), never by relaxing nullification.

4.5 Concurrency by SPLIT

An orchestrator expecting parallel sub-agents SPLITs its mandate into disjoint sub-mandate notes ahead of dispatch (a split tree of any shape). Each sub-agent then consumes its own note without contention. Unspent remainders flow back only as new authority issued by the principal — v0.1 deliberately has no MERGE transition, and (resolving §10.4) the exclusion is now permanent-by-default rather than provisional: a cross-lineage MERGE breaks per-root_id conservation attribution and revocation semantics outright (which subtree does the merged note die with?), and a same-lineage MERGE publicly links two sibling notes (two nullifiers consumed by one transition) — a §4.6 cost purchasing nothing, since SPLIT granularity is the prover's choice in the first place. Replenishment (subscription-style caps) is resolved as an artifact-v2 direction instead: a not_before validity-floor field in the §3 opening lets a principal pre-issue N period mandates in one ceremony with staggered [not_before, expiry) windows — per-period root_ids, so one compromised period revokes without touching the others. not_before is a commitment-preimage change (every VK re-blesses), so it ships in the coordinated artifact-v2 break (§10), not as a point change.

4.6 Privacy properties

Successive states are unlinkable to observers: each successor commitment uses fresh randomness, nullifiers are pseudorandom, and neither cap values nor the tree position of predecessors appear in public inputs. What a verifier of one spend learns is exactly the §1.1 statement for that spend — nothing about siblings, predecessors, or the principal.

The unlinkability claims, stated precisely (normative direction; formal game-based proofs remain future work, §10). All three hold against an observer seeing the full public settlement record, under the hiding of the Poseidon2 commitment scheme and the pseudorandomness of the nullifier construction in the holder secret:

  1. Transition unlinkability. Two settled transitions of the same lineage are distinguishable from two transitions of different lineages only through coarse public attributes — shared asset_id, shared settlement context, current_time proximity, and the transition kind with its successor arity (surface (d) below) — never through any protocol identifier. Two fine-grained exceptions have been closed since this claim was first written: policy_hash was a public, lineage-stable value in the spend vector until rev 5, and the per-spend amount was public until rev 7 (§5). Amount was the sharpest of these while it lasted — values are distinctive, and a sequence of them fingerprints a payer far more readily than an asset or a timestamp does — and it is now absent from the public record entirely. asset_id remains public by function (the verifier is being paid in that asset) and kind by structural necessity; both are attributes shared by arbitrarily many lineages, not identifiers of one. The limit of this, stated precisely: the counterparty still learns the amount, from the ZKA note opening it must receive in order to spend the note at all. Rev 7 hides the amount from observers, not from the party being paid.
  2. Cross-mandate principal unlinkability. Two mandates issued by the same principal carry independent root_ids (fresh issuance_randomness per issuance, §3 MUST) and independent commitments; issuance publishes only (cm, settlement_context) (§3). No public value correlates them — including after revocation: two revoked root_ids are two independent hash images, and revealing both preimages would be the principal's own act.
  3. Revocation disclosure, priced. Revocation publishes exactly one root_id (§7.1) — the deliberate disclosure of the design. An observer learns that some lineage was killed, and every subsequent transition proves non-membership against the updated root without revealing whether it was in the killed subtree. What revocation does NOT reveal: the subtree's size, its transitions (past ones referenced the root_id only privately), its holders, or its principal.

Residual correlation surfaces, enumerated: (a) asset_id + settlement context at spend — inherent to being paid in a named asset on a named target. Amount left this surface in rev 7 and the reason is worth recording, because the old text called its publicity "inherent to being paid" and that was wrong: ZKA's own internal shielded spends publish only commitments, so a public amount put ZKM below its settlement layer's baseline rather than at it. Nothing needed the value — no settlement effect reads it, and the payee verifies against zka_cm_recipient from the note opening it must receive regardless; (b) timing; (c) settlement-layer metadata outside ZKM's control (transaction origin, fees); (d) transition kind and successor arity, below. Deployments SHOULD document (c) for their target.

(d) Transition kind and successor arity, priced. Both are public per transition and were unpriced before rev 7. kind is a §5 wire value, and the zero sentinel in cm_out_2 discloses arity independently — padding kind away would not hide how many successors a transition created. What an observer learns is the shape of activity on a settlement target: the counts and timing of SPEND, SPLIT, RETIRE, DELEGATE_FULL, DELEGATE_PARTIAL and ROTATE transitions, and of revocation insertions. What it does not learn: who transacted, which lineage any transition belongs to, whether any two transitions share a subtree, how deep a chain runs, or how many mandates exist. The leak class is the one [CRSET] names for credential revocation — "operational information such as the number or timing of revocations" — reproduced one layer up, at the delegation layer. The distinction ZKM holds is that the activity is unattributable, not invisible.

This is structural, not an omission awaiting a fix. kind cannot be hidden while the §5 companion-matching rule stands: a settlement layer must know whether a transition is DELEGATE_FULL or ROTATE in order to require the possession companion at all. Arity cannot be hidden while successor slots carry per-kind semantics (§4.3-3). And padding every transition to a uniform shape would not close it — the zero sentinel still separates a live successor from an absent one (§5 wire values), and the two proof forms are distinguishable by public-vector length alone (9 elements against 14) before any element is read. A deployment needing kind-privacy needs a different transition encoding, not a padding scheme; none is proposed here.

Voluntary disclosure adds no residual surface (rev 7). Stated here because this document prices every disclosure and the omission would otherwise read as an oversight: the zkm:disclosure:v1 machinery (§5) adds nothing to the list above. An envelope is holder-assembled and delivered out-of-band to a party the principal chose; no envelope, no control attestation, and no fact about an envelope ever reaches settlement, so an observer's view is byte-identical whether or not any disclosure was ever made — which is also why no observer can tell that one was. Within an envelope the recipient learns root_id, caps, history and counterparty material, all of which the note openings deliver anyway; the control attestation's lineage_tag is per-envelope (§5), so two recipients comparing envelopes cannot establish that they hold disclosures of the same lineage. Claims 1–3 above are unaffected: they quantify over observers of the public settlement record, and disclosure never touches it.

Formal games (sketches — closes the §10.2 residual at sketch strength; machine-checked proofs remain future work). Let A be a PPT adversary seeing the full public settlement record and choosing all non-secret inputs.

Assumptions, stated honestly: Poseidon2-based commitments hiding under fresh randomness, and the KDC-derived nullifier/root_id constructions PRF-like in their secret inputs — standard for this hash family but not reduced to a named hardness assumption here.

4.7 Rejected alternative: monotonic counter binding

A counter model (nf_i = H(mandate_key, i), cumulative-spent carried in a proof chain) was considered and rejected as the normative direction: it serializes all consumption with no SPLIT-equivalent, leaks a linkable per-mandate nullifier sequence to any party that learns mandate_key-adjacent material, and re-derives chain-of-custody machinery that the note model gets from ZKA for free. It remains documented here so the decision is not relitigated without new evidence.


5. Authorization Predicate and Proofs

The authorization proof conjoins, over shared inputs (asset_id, settlement context, mandate tree root, revocation accumulator root, revocation accumulator scheme, current_time — all public — and the amount, shared as a private wire since rev 7): the §4.3 transition statement, the policy predicate evaluated on the spend context, and the ZKA spend statement via shared-public-input composition. Recursive carriage in a ZKA bundle is not part of the current zka:bundle:v1 profile and remains deferred to the canonical ZKA/ZKM integration profile; implementations MUST NOT claim it today. The proof MUST establish that the policy circuit and parameters actually evaluated hash to the policy_hash committed in M — a proof against any other policy MUST NOT verify. The settlement layer MUST accept the mandate transition and the conjoined ZKA spend atomically: neither may take effect without the other.

The three legs share one holder. A single holder secret owns both legs: the mandate's holder_key, the consumed ZKA value note's recipient address, and the ZKA nullifier key all derive from it. An agent's mandate authority and its value custody are provably the same key material. The ZKA leg's consumed note MUST be in the mandate's asset_id; its recipient leg carries the amount, its change leg returns the remainder to the holder; value conservation (in_value = amount + change) is proven in-circuit against the private amount witness.

Two proof forms exist:

Every proof of either form MUST bind in its public inputs and settlement payload: the transition kind, the emitted mandate nullifier, each successor commitment, the mandate tree root, the revocation accumulator root, revocation_scheme, current_time, and the settlement context (the spend-conjoined form adds asset_id, the ZKA note-tree root, the ZKA nullifier, and the two ZKA output commitments — 14 elements total against the mandate-only form's 9). The mandate's policy_hash is NOT in either vector (removed in rev 5): the policy leg opens it privately in-circuit, so a public copy served no verifier function — and being preserved across successors (§4.3-3) it was a lineage-stable cross-spend correlator (§4.6). The input commitment is deliberately not a public input — exposing it would link successor states (§4.6); the input note is bound privately through its membership proof and publicly through its nullifier. The revocation epoch is likewise not a public input: a prover-supplied epoch label is unconstrained, so a stale root could be published under a fresh label — the verifier derives the epoch from the root it recognizes.

Wire values (normative). The kind element encodes: SPEND = 0, SPLIT = 1, RETIRE = 2, DELEGATE_FULL = 3, DELEGATE_PARTIAL = 4, ROTATE = 5. revocation_scheme MUST equal 2 for the depth-254 full-identifier exclusion tree; values 0, 1, and 3..255 are invalid. An unused successor slot is the zero sentinel (cm_out_2 = 0 for SPEND / DELEGATE_FULL / ROTATE; both zero for RETIRE); a live successor commitment is never zero (§4.3-3), so the sentinel is unambiguous. Both public vectors carry fixed slots in the declaration order of the reference circuits' main — independent encoders MUST reproduce that order (the §2 encoder documents restate it per VK).

Because nf binds only the consumed note and settlement context — not the proof form or verification key — a settlement layer MUST maintain a single mandate nullifier set per settlement context, shared across every ZKM verification key it accepts; per-VK nullifier sets would let one note be consumed once per key.

Envelope: zkm:proof:v1 — mirroring the frozen zkc:proof:v1 conventions (ZKC v0.4.1-draft §6.1): a JSON object with version: "zkm:proof:v1" (closed literal discriminator); metadata { generatedAt, expiresAt, context } where context is the settlement context as a 0x-hex field element; proof as the 0x-hex UltraHonk blob; publicInputs as a named object flattened to the verifier ABI by the VK's §2 encoder document; and a statement discriminator naming which proof form the envelope carries (zkm:mandate-only:v2 | zkm:spend-conjoined:v2 | zkm:possession:v1 | zkm:issuance:v1 | zkm:revocation-auth:v1 | zkm:control-attestation:v1 | zkm:issuance-zkc:v1 | zkm:spend-zkc-binding:v1) — the ZKM analogue of ZKC's proofType. The §6 deployment registry entry MUST map each of those eight statement values to exactly one VK per settlement target, which is what makes VK selection deterministic while the VK itself stays out-of-band (the ZKC convention: the envelope names what is proven, the registry pins what verifies it). The one-VK-per-statement rule became load-bearing rather than merely tidy in rev 7: it is the registry-level half of keeping a control attestation from being accepted as a revocation authorization (Voluntary Disclosure, below). zkm:receipt:v1 serializes the §5 receipt fields under the same conventions, adding the settlement inclusion reference as the target's canonical-order coordinate. Legacy zkm:mandate-only:v1 and zkm:spend-conjoined:v1 statements are valid only under pre-cutover v1 keys and roots. A post-cutover registry MUST omit them and MUST NOT reinterpret or upgrade their proofs. [Authenticated-demand transport: to be specified — the demand standing rule (§5 Receipts) is normative now.]

Statement registry and version discipline (normative). Those eight statement identifiers, their public vectors and their verification keys are published in machine-readable form as protocol-family/zkm-statement-registry-v1.json, whose statement_set_id digests exactly that surface together with the accepted revocation scheme and the pinned proving toolchain. That artifact, not this document's version string, is ZKM's citable identity for composition. 0.1.0-draft has been constant since rev 1. Rev 12 moves the pinned toolchain to nargo 1.0.0-beta.25 and Barretenberg 5.0.0, and re-blesses all eight verification keys. Family epoch zka:protocol-family:2026-09-10.1 pins the rev-12 label and statement-set identity together. A revision that adds, retires or renames a statement, changes any public vector, moves any verification key, changes the accepted revocation scheme, or moves the pinned toolchain is breaking: it MUST bump the registry revision, append a revision_history entry whose statement_set_id differs from its predecessor's, and set last_breaking_revision to that revision; a non-breaking revision MUST carry its predecessor's statement_set_id unchanged. A consumer holding only a family epoch, a bare version string and no current registry pin MUST treat ZKM's statement surface as unpinned. docs/zkm_v1_to_v2_cutover.md is the normative cutover note for consumers crossing rev 10 — the ZKA §5.4.1 settlement interface first.

ZKM Standard Policy v1

The first concrete policy schema, which makes "a proof against any other policy MUST NOT verify" real. Parameters: version (= 1), max_per_spend (u128 ceiling on the recipient-leg amount), recipient_restricted (boolean), recipient_set_root (depth-16 Merkle root of an allowlist, used only when restricted), and policy_blinding (§3). The predicate MUST assert: the parameters open to the mandate's committed policy_hash; version is supported; amount ≤ max_per_spend; recipient_restricted is boolean; and when restricted, membership of the recipient address in the allowlist at a non-zero recipient_set_root. Additional schemas are versioned successors, selected by the version field.

ZKM Standard Policy v2 adds one parameter: depth_limit (u8), a per-mandate delegation depth ceiling — a DELEGATE transition MUST assert depth(successor) ≤ depth_limit; depth_limit = 0 forbids delegation outright (a delegated successor's depth is always ≥ 1). It composes with the VK-baked depth_max (§4.3-3): the effective bound is the tighter of the two. depth_limit sits before the blinding in the preimage (7 slots against v1's 6 — length-domain-separated, so the schemas cannot collide even before version is compared) and is not consulted at spend time. v1's delegation semantic is normative and permit-all: a v1 policy carries no delegation parameter and permits delegation bounded only by depth_max; a principal wanting per-mandate delegation control issues under v2. ROTATE (§6) is not delegation and is gated by neither.

Narrowing registry v1 (§4.3-3). A successor's policy_hash MAY be replaced only by a policy that narrows it under a relation in this registry, which is versioned with this specification:

From → To Narrowing relation
v1 → v1 max_per_spend′ ≤ max_per_spend; recipient_restricted may go 0→1, never 1→0; when both restricted, recipient_set_root′ = recipient_set_root (set shrinking requires subset proofs and is deliberately excluded from registry v1).
v2 → v2 The v1→v1 rules, plus depth_limit′ ≤ depth_limit.
v1 → v2 The shared-parameter rules, any depth_limit (v1 delegation is unbounded-to-depth_max, so every v2 limit narrows it).
v2 → v1 Forbidden — it would widen delegation.

Fresh policy_blinding′ is REQUIRED on any narrowed successor (reusing the blinding would link the two policies if either opening is ever disclosed). Enforced in-circuit as of rev 6 on the mandate-only VK (assert_policy_narrows): a live successor whose committed policy hash differs from the input's opens both policies and proves the relation; a preserving successor pins its opening witnesses to zero. The spend-conjoined VK enforces strict preservation — the deliberate line is that narrowing accompanies authority distribution (SPLIT / DELEGATE / ROTATE), never spending.

ZKC same-subject composition (normative)

Binding an independently generated ZKC proof only to a transaction context is insufficient: a prover could borrow another holder's valid credential proof. ZKM therefore defines two same-subject statements, one at issuance and one at spend. Credentials, predicates and issuers remain optional; a policy or deployment that does not request ZKC composition continues to use the base statements above.

Canonical subject derivations. Let S be the private KDC entity seed (a standalone seed or AFP/KDC per-entity sub-seed), and use the ZKC v0.4.1-draft/AFP-KDC v1.0.3 §A.6 order exactly; the derivation and values remain the v1.0.2 cryptographic profile:

cbk                = H(zkc/credential, S)
holderCommitment   = H(zkc/binding, cbk)
presentationHandle = H(zkc/presentation-scope:v1, cbk, scopeId)
issuanceKey        = H(zkm/issuance_key/v1, S)
recipientAddress   = H(zka/address, H(zka/viewing, S))

This does not publish S, cbk, the issuance key, the principal, or the ZKA recipient address. The ZKA address and ZKC credential commitment remain unlinkable without S.

Composition binding and exact contexts. zkcProofDigest is the BN254 field reduction of Blake2b-512("ZKM-ZKC-Proof-v1" || JCS(zkcEnvelope)), with the digest interpreted little-endian. JCS is RFC 8785 canonical JSON and the envelope includes the proof bytes, version, proof type, all public inputs and presentation binding. The target obtains zkcVkHash from its accepted-ZKC-VK registry entry; neither value is prover-interpreted metadata. Profile encodings inside the ZKM circuit are anonymous = 0, scoped = 1, linkable = 2.

For digest purposes, zkcEnvelope MUST be the complete JSON object validated against ZKC v0.4.1-draft's schemas/compliance-proof-v1.schema.json or schemas/compliance-proof-v2.schema.json, not a projection assembled by ZKM. Its canonical top-level member names and JSON types are:

Every member whose upstream type is ZKProof (proofs[*].proof and aggregation.proof when present) MUST encode the exact proof bytes as unpadded RFC 4648 §5 base64url: only A-Z, a-z, 0-9, - and _, no = padding or whitespace, and canonical zero pad bits. A verifier MUST decode then re-encode and require byte-for-byte equality with the input string. Standard base64 (+ or /), padded base64url, hexadecimal, mixed representations and non-canonical pad bits MUST be rejected. Upstream Hash members retain their lowercase 0x plus 64-hex-digit encoding. Implementations MUST NOT rename or omit schema members; an extension member is permitted only when the accepted proof type or a future envelope version declares it, and it is included in the JCS input.

The cross-implementation known-answer vector uses proof bytes fb ff, whose only accepted proof string is -_8 (standard base64 would be +/8=), and this exact one-line RFC 8785 value:

{"metadata":{"context":{"profile":"zkc:context:transaction:v1","timestamp":1785801600,"value":"0x0000000000000000000000000000000000000000000000000000000000000001"},"expiresAt":"2026-08-04T00:05:00Z","generatedAt":"2026-08-04T00:00:00Z"},"presentationBinding":{"profile":"zkc:presentation:scoped:v1","pseudonym":"0x0000000000000000000000000000000000000000000000000000000000000002","scopeId":"0x0000000000000000000000000000000000000000000000000000000000000003"},"proofs":[{"principalConsent":true,"proof":"-_8","proofType":"zkc:proof:example:v1","publicInputs":{"presentationPseudonym":"0x0000000000000000000000000000000000000000000000000000000000000002","proofContext":"0x0000000000000000000000000000000000000000000000000000000000000001","scopeId":"0x0000000000000000000000000000000000000000000000000000000000000003"}}],"version":"zkc:proof:v2"}

For that value, Blake2b-512 of the domain prefix plus the UTF-8 JCS bytes is 4bf2a36e5f21f6949fe41133675dc631b31bb441a1f54481477b023514f9d479a9c4586b495a73f4a91847277b627beaa07b45328370e4da28029dbce9ca5a3e; little-endian reduction modulo BN254.Fr is 0x13db598ebfff2a1dc254965eba1a9d14559423d3be40344f76898f0bc450e48a. scripts/check-zkc-envelope-kat.py is the executable reference check.

issuanceContext = H(zkm/zkc-issuance-context/v1, cm, settlementContext)
spendContext    = H(zkm/zkc-spend-context/v1,
                    nf, zkaCmRecipient, settlementContext)

zkcCompositionBinding = H(zkm/zkc-composition/v1,
                           zkcVkHash, zkcProofDigest, exactContext,
                           profile, subjectHandle, scopeId)

The last hash is the fixed seven-field Poseidon2 preimage in that order. A ZKC proof used here MUST carry proofContext == exactContext. Consequently a proof cannot move between issuances, spends, recipient notes, settlement targets, VKs or profiles. zkcVkHash, zkcProofDigest and the resulting binding MUST be non-zero.

Issuance statement — zkm:issuance-zkc:v1 (current). The public vector is (cm, settlementContext, zkcCompositionBinding). In addition to every base issuance constraint, the circuit proves that principalIssuanceKey and the ZKC subject handle derive from the same S. The settlement layer MUST select the zkm:issuance-zkc:v1 outer VK from its immutable ZKM statement registry and the named inner ZKC VK from its accepted-ZKC registry, verify the named ZKC proof, recompute its canonical digest and composition binding, require that recomputed value to equal the outer ZKM proof's public zkcCompositionBinding, verify the ZKM proof, and insert cm only if all checks succeed as one atomic acceptance. This equality check is mandatory even though the outer circuit also constrains its public binding from its private ZKC inputs. This establishes that the credential holder is the principal issuance authority; it does not require the mandate's initial agent holder key to equal the principal.

Spend statement — zkm:spend-zkc-binding:v1 (current). This is a non-settling companion with public vector (nf, zkaCmRecipient, assetId, settlementContext, zkcCompositionBinding). It proves that the public recipient commitment opens to (assetId, amount, recipientAddress, randomness) and that recipientAddress and the ZKC subject handle derive from the same S. A target MUST select the companion and matched zkm:spend-conjoined:v2 outer VKs from its immutable ZKM statement registry, and MUST accept the companion only alongside a zkm:spend-conjoined:v2 proof whose nf, zkaCmRecipient, assetId and settlementContext match exactly, plus the ZKC proof whose accepted-registry VK and recomputed binding match. It MUST verify all three before applying any of §5's five state effects and MUST apply those effects atomically. The companion alone carries no note-tree root, value-note nullifier or mandate successor and can settle nothing.

The current conforming path is direct atomic settlement composition: verify the ZKC proof, the ZKM proof and (for spend) the recipient-control companion in one settlement transaction. Current zka:bundle:v1 has no standardized ZKM member, so a deployment MUST NOT claim bundle carriage as a current conforming alternative. A future, separately versioned ZKA bundle extension may carry these members only after it standardizes the ZKM member and enforces the same VK, digest, exact-context, profile and public-vector equalities and the same atomic state transition. Merely placing two unrelated proofs in one envelope is not composition. Recursive targets MAY replace the host-side inner-proof check with recursive verification; the equality rules do not change.

Profile acceptance. The ZKC v0.4-series privacy ladder, pinned here at current v0.4.1-draft, creates a hard capability boundary:

These rules preserve predicate pluralism. No ZKC credential schema, issuer or predicate is mandatory. A deployment may accept any open predicate VK, but its registry MUST name the exact proof types, VK hashes, profiles, issuer-policy roots, digest algorithm and composition path it accepts. Each ZKM statement still maps to exactly one ZKM VK per target; the accepted inner ZKC VK set is a separate allowlist and membership is not a trust claim.

Envelope, receipt and conformance rules. A composed zkm:proof:v1 names the new statement, carries the outer proof/public vector, and adds a zkcComposition object containing the full ZKC envelope, zkcVkHash, zkcProofDigest, profile, subject handle, scope and exact context. A receipt MUST retain both VK identifiers, the complete outer ZKM proof envelope including proof bytes, the complete inner zkcEnvelope exactly as digested including every proof byte, both proof digests/public vectors, the matching fields, the settlement inclusion reference, and linkable consent when applicable. Receipt verification MUST be possible from the retained receipt and immutable registry versions without cooperation from the prover, counterparty or settlement operator: it MUST re-verify both proofs and all matching equations. Recording only zkcCompositionBinding, a digest, or a mutable retrieval URL is insufficient evidence.

Conformance suites MUST reject at least: a substituted entity seed/holder, recipient commitment, principal issuance key, exact context, profile, scope, proof digest, VK hash, or outer issuance binding unequal to the recomputed inner binding; an anonymous proof on a companion path; a scoped proof with zero or foreign scope; a linkable proof with non-zero scope or absent consent; a VK absent from the target registry; and any non-atomic settlement. The reference Noir packages exercise scoped and linkable positive shapes plus the holder, recipient, issuance-key, context, profile, proof and VK negatives.

The only new settlement-visible value is the interaction-unique composition binding (and the already-public matching fields). Scoped/linkable handles are already public in the selected ZKC proof; ZKM does not republish them in its outer vector. Anonymous shared-witness composition publishes no handle. Thus principal hiding, ZKA note privacy and the §4.6 unlinkability claims are preserved modulo the disclosure the holder explicitly selected in ZKC. The six Freedom Safeguards continue to apply: composition never chooses a canonical credential, never makes credentials ambient, never creates operator custody, requires open verifier logic, leaves participation optional, and produces a self-verifiable holder receipt. The current ZKC baseline is v0.4.1-draft. Its anonymous and scoped presentations use zkc:proof:v2; its explicitly consented linkable profile retains zkc:proof:v1. Context binding is necessary but not sufficient: the composition proves that the ZKC credential subject is the same subject as the ZKM issuance principal (for a principal-credential policy) or the ZKA recipient (for a counterparty-credential policy). Merely accepting a valid context-bound proof from an unrelated credential holder would permit borrowed-proof substitution. zkm:issuance-zkc:v1 and zkm:spend-zkc-binding:v1 are the current registry-selected ZKM statements that provide this same-subject proof for their respective paths; the direct atomic-settlement rules above are their current carriage.

Reference implementation status: noir/zkm_mandate_transition implements the mandate-only form (kinds SPLIT / RETIRE / DELEGATE_FULL / DELEGATE_PARTIAL / ROTATE; it rejects SPEND, and its public vector carries no ZKA value leg and no asset_id, making that VK structurally incapable of authorizing value movement). As of rev 5 it opens the policy on DELEGATE_* kinds and enforces the §6 delegation gate in-circuit (v1: permitted; v2: depth ≤ depth_limit); the six policy witnesses are pinned to zero on every other kind. noir/zkm_spend_conjoined implements the spend-conjoined form: shared-public-input composition of the §4.3 SPEND transition, the ZKA value-spend leg, and the Standard Policy (v1 or v2) predicate in one proof, so the atomicity is structural. noir/zkm_issuance implements the §3 issuance statement. noir/zkm_issuance_zkc and noir/zkm_spend_zkc_binding implement the current registry statements zkm:issuance-zkc:v1 and zkm:spend-zkc-binding:v1; the latter is the matched recipient-control companion for scoped/linkable ZKC proofs. Anonymous composition remains predicate-specific by construction; there is no sound generic handle companion for a profile that exposes no handle. noir/zkm_delegate_possession implements the §6 possession statement as a companion proof — public vector (cm_out_1, nf, settlement context), all three already public in the matched transition, so the companion adds zero disclosure (§4.6); it carries no cap, no amount, and no membership proof, so accepting it alone settles nothing. The delegate proves, with its own secret and never the delegator's, that the successor commitment opens to a holder_key derived (zkm/holder_key/v1) from a key it knows. Companion-matching rule: a settlement layer MUST NOT accept a DELEGATE_FULL or ROTATE transition (and SHOULD NOT accept the delegate leg of a DELEGATE_PARTIAL) unless it also verifies a zkm_delegate_possession proof whose public vector matches the transition's (cm_out_1, nf, settlement context). Binding nf makes each possession proof single-use per consumed note (§4.3-4); an SDK MUST refuse by default to prove a full rebind or rotation before the counterpart's companion proof is in hand. noir/zkm_revocation_auth implements the §7.1 authorization statement (public vector: root_id, settlement context). noir/zkm_control_attestation implements the rev-7 zkm:control-attestation:v1 statement (public vector: lineage_tag, settlement context, disclosure_bindingno root_id, which is the safety property, see Voluntary Disclosure below). The §5 still-unenforced list is empty as of rev 6. (The earlier zkm_mandate_predicate scaffold and the interim zkm_mandate_spend_unconjoined package are deleted, the latter superseded by zkm_spend_conjoined.)

Receipts and Verifier Accountability (Safeguard 6)

ZKM inherits Safeguard 6 by reference; this section fixes what its artifact classes MUST contain. Serialization follows the §2 pending conventions — the normative content here is the field set and one rule: a receipt MUST suffice, with no other party's cooperation, to (a) re-verify the proof against the stated VK and (b) open every commitment the receipt claims to open. A record that cannot do both is not a receipt.

Transition receipt — zkm:receipt:v1. For every transition it proves, a holder's tooling MUST retain: the full §5 public vector as settled; the VK identifier and proof bytes; the settlement inclusion reference — the transaction/position in the target's canonical order, which is the §7.2 ordering key and is what makes the receipt adjudicable against a revocation collision; the openings of the successor notes the holder retains; and — REQUIRED as of rev 7 for spend-conjoined transitions — the opening of the ZKA recipient note it created (amount, recipient address, recipient randomness). That last item used to be redundant: amount was a public input, so a receipt established the settled value by carrying the public vector. Rev 7 made amount private (§4.6), so without the opening a receipt could no longer discharge its own rule — "open every commitment the receipt claims to open" — for zka_cm_recipient, and a holder could not later demonstrate what it paid. Receipts are holder-side state: nothing here puts any of this on an observable surface.

Delegation record. For each DELEGATE transition, the delegator's receipt MUST additionally bind the counterparty: the delegate identity material the §6 ceremony used, the delegate's §5 companion possession proof where one accompanied the transition (always, for a full rebind; for a partial delegation that used the §6 small-cap probe instead, the record of the probe transition stands in), and the delegated successor's opening as created. Acceptable identity material is either out-of-band identity evidence or a ZKC v0.4.1-draft presentation under the following profile rule:

This is the §9 pairwise-accountability record — it lets a delegator later demonstrate to whom it delegated what, without the protocol disclosing either to anyone else. Even the linkable profile remains holder-side state: it changes what that consenting pair can later disclose, not any settlement public input or observable surface. The §4.6 transition-unlinkability and cross-mandate principal-unlinkability claims, including TL-IND and PM-IND, therefore continue to rest on their existing commitment, nullifier, and fresh-issuance assumptions rather than on a ZKC presentation profile.

Verifier obligations. A party that accepted a mandate proof (a counterparty, or a settlement-layer operator) MUST retain the accepted public vector, VK identifier, and inclusion reference for its disclosed retention period, and MUST answer an authenticated demand about a specific settled transition, identified by its nf. Standing to demand is proven in-lineage: knowledge of a successor opening of that transition (holder), knowledge of the consumed note's opening (predecessor holder), or due-process authority — which, to resolve a reference that dangled from rev 4: is not a protocol role. ZKM defines no adjudicating authority, recognizes no credential that confers one, and gives a verifier no way to check one in-band. The term means whatever compulsory process the deployment's jurisdiction supplies, exercised against the verifier out-of-protocol; a deployment that expects such demands MUST disclose in its §6 registry entry what it will honor and on whose authority, because nothing in this specification tells a holder that in advance. The artifact a holder produces when it chooses — or is compelled out-of-protocol — to answer is zkm:disclosure:v1 (below). ZKM supplies the evidence format; it deliberately supplies no mechanism that compels its production, and no protocol behavior conditions on whether it was produced. There is deliberately no principal-wide query: a verifier cannot associate transitions with a root_id it never sees, so a principal reaches its subtree's activity through the §9 pairwise chain — its own delegation records, hop by hop — never by fishing the verifier (§4.6 is not weakened by accountability).

(Serialization of both artifact classes is specified with the zkm:proof:v1 and zkm:receipt:v1 envelopes above; only the authenticated-demand transport remains open — the demand standing rule is normative now.)

Voluntary Disclosure — zkm:disclosure:v1

Half of this already existed implicitly. The receipt discipline above plus the per-hop delegation records mean a principal can already reconstruct its own subtree and show it to whomever it likes — the pairwise chain of §9 is exactly that capability. Rev 7 names the posture and standardizes the artifact, because an unstandardized capability is one every deployment reinvents incompatibly, and because a protocol that intends to carry real value needs its compliance surface designed rather than bolted on.

It is safeguard-consistent for the same reason revocation is (§9): the disclosure is the owner's own act over the owner's own authority. What would not be safeguard-consistent — and is therefore absent — is any protocol mechanism by which someone else obtains, compels, or detects a disclosure.

The artifact. zkm:disclosure:v1 is a holder-assembled JSON envelope following the §2 conventions:

{
  "version": "zkm:disclosure:v1",
  "metadata": { "generatedAt": "...", "expiresAt": "...", "context": "0x...",
                "disclosureBinding": "0x..." },
  "scope": { "kind": "transition | lineage | portfolio",
             "bounds": { "notBefore": "...", "notAfter": "..." } },
  "contents": {
    "controlAttestations": [ /* zkm:proof:v1, statement zkm:control-attestation:v1 */ ],
    "receipts":            [ /* zkm:receipt:v1 */ ],
    "delegationRecords":   [ /* §5 delegation records */ ],
    "noteOpenings":        [ /* mandate-note and ZKA-note openings */ ]
  }
}

Five rules are normative.

1. Self-verification. Extending the receipt rule in spirit: a disclosure MUST suffice, with no other party's cooperation, for the recipient to (a) re-verify every included proof against its stated VK; (b) open every commitment the disclosure claims to open; (c) check every settlement inclusion reference against settlement state; and (d) check chain consistency across the disclosed transitions — root_id constant within a lineage, cap conservation across each hop, expiry monotone; and (e) bind each included control attestation to the lineage, domain and envelope it speaks for, by recomputing lineage_tag = H_{zkm/control_attestation/v1}(root_id, settlement_context, disclosure_binding) from the root_id the envelope's own note openings disclose and the context and disclosureBinding in its metadata, and checking it against the attestation's public vector. Without (e) the attestation verifies as a proof but is not tied to this envelope — it would establish only that the assembler controls some lineage somewhere. A record that cannot do all five is not a disclosure. Note what (c) implies: a disclosure is checkable against reality, not merely internally consistent, so a fabricated envelope fails against the settlement record rather than against the recipient's credulity.

2. Control attestation MUST NOT be replayable as revocation. This is the hazard that shapes the design, and it is worth stating plainly because the obvious implementation walks straight into it. The natural way to prove "I am the principal of this lineage" is a zkm_revocation_auth proof — it proves exactly that. But its public vector (root_id, settlement_context) is the statement that authorizes revocation insertion (§7.1). An envelope carrying one would hand its recipient a live capability to kill the lineage: the principal would disclose its books and enclose the means of destroying what they describe.

ZKM therefore defines a distinct statement, zkm:control-attestation:v1, with its own circuit (noir/zkm_control_attestation) and its own VK. Its public vector is (lineage_tag, settlement_context, disclosure_binding) where lineage_tag = H_{zkm/control_attestation/v1}(root_id, settlement_context, disclosure_binding). Note that settlement_context is in the preimage, not merely a bare public input alongside it as in ZKM's other statements: the tag therefore identifies (lineage, domain, envelope) together, and rule 1(e)'s single equality check covers all three. Carrying the context outside the preimage would have been sound — the proof binds every public input either way — but it would have made domain-matching a rule the recipient has to remember rather than a property its own recomputation enforces. The resolution is structural: revocation is root_id insertion, so a settlement layer needs a public root_id to act on, and this vector does not contain one — the same species of safety argument §5 already makes for the mandate-only VK carrying no ZKA value leg. Three things hold together, and a reviewer should check them in this order: the vector carries no root_id; the §6 registry maps zkm:control-attestation:v1 and zkm:revocation-auth:v1 to different VKs; and a settlement layer MUST accept revocation insertion only under the revocation-auth VK. The third alone suffices against a correct implementation — the first is what survives an incorrect one.

The converse is an SDK obligation, since no circuit can enforce it: an SDK MUST NOT place a zkm:revocation-auth:v1 proof in a disclosure envelope, even though it would convince a recipient of the same fact.

disclosure_binding binds the attestation to one envelope and one audience. It is opaque to the circuit — no in-circuit check can establish that an off-circuit digest was computed honestly — but the proof is bound to it, so an attestation does not verify once lifted into a different envelope. It SHOULD be derived under zkm/disclosure_binding/v1 over the audience identifier and the envelope's canonical serialization. Folding it into lineage_tag additionally makes the tag per-envelope: two attestations of one lineage to two audiences share no public value, so recipients comparing envelopes cannot tell they describe the same lineage.

3. Disclosure MUST NOT transfer authority. The envelope MUST NOT carry the issuance-key preimage (principal_issuance_key, issuance_randomness): handing that over hands over revocation power. Control is proven, never transferred. Note openings and delegation records do reveal caps, history, and counterparty identity material to the recipient — that is the entire point — but no key material crosses and no capability crosses. Every other artifact class in an envelope is replay-inert by construction, and rev 7 checked rather than assumed this: settled transition proofs are one-shot because the settlement layer's nullifier check-and-insert rejects a second presentation (§4.3-4); an issuance proof re-presented re-inserts an existing leaf, a no-op; a possession companion is bound to an nf already consumed. The control attestation was the only artifact whose replay would have been live, which is why it is the one that needed a new circuit.

4. Scope is the minimal-disclosure instrument. Three kinds — a single transition (one receipt plus its openings), a lineage (one root_id subtree, as far as the assembler's own pairwise records reach), or a portfolio (several lineages the principal selects). Envelopes compose: each delegator can assemble an envelope for its own hops, so a full-subtree disclosure is a chain of pairwise envelopes rather than a global view someone holds. This mirrors §9's pairwise-accountability model exactly, and it means the protocol never requires — and never creates — a party with a god's-eye view. A principal whose delegate declines to disclose gets an envelope that stops there, and the boundary is visible in the scope bounds rather than silently elided.

5. SDK rules (added to the §9 Safeguard 4 list). An SDK MUST require an explicit act to assemble a disclosure — never a default, never a background behavior, never a consequence of some other operation. It MUST default to the narrowest scope satisfying the request. It MUST warn that disclosure is irreversible information release: an envelope cannot be un-sent, and expiresAt bounds the envelope's claimed freshness, not the recipient's memory. And per rule 2 it MUST NOT substitute a revocation-auth proof for a control attestation.

Deployment posture (normative direction). What this enables: voluntary auditability to parties the principal chooses — the shape a regulated rail actually needs, where a principal stays identifiable to its own PSP while hidden from the merchant and from every other verifier. What ZKM still deliberately does not provide, and what no conforming deployment may add while claiming conformance: compulsory identification; any operator-exercisable or third-party-exercisable disclosure; any protocol mechanism that reads, detects, or conditions on disclosure state. Nothing about a mandate's validity, a spend's settlement, or a revocation's effect depends on whether a disclosure was ever made. That independence is the whole safeguard argument (§9, Safeguard 3), and it is what separates a disclosure format from a surveillance hook.


6. Delegation

DELEGATE takes one of two normative forms, both subject to the mandate policy permitting delegation and to §4.3 monotone narrowing:

Delegation chains are therefore state transitions, not certificate chains — depth-N delegation costs N transitions, not an N-deep recursive verification at spend time. §4.3 invariants 1 and 3 enforce in-circuit what [DIF-THREATS] states as the foundational chain requirement — attenuation at every hop, a delegate never passing more than it holds — whose non-enforcement it calls "broken by design or by omission". That in-circuit enforcement today covers cap conservation, expiry and depth monotonicity, and policy_hash preservation — not a policy that enforcement points apply but a property no valid proof can violate. Policy narrowing (replacing a policy with a strictly tighter one) is enforced in-circuit on the mandate-only VK as of rev 6 (§5 narrowing registry v1) — attenuation of the policy dimension is preservation-or-proven-narrowing, closing the last conventionally-enforced gap in the chain requirement.

Recursion (ZKA §4.2) is needed only where a spend must attest the full chain to a verifier in one proof; on targets without in-circuit recursion, deployments MUST bound depth and disclose the bound. (Citation corrected in rev 5: ZKA reaches its Coord level on non-recursive targets only in a "scoped form (bounded coordination depth...)" — ZKA §12.1 — and defines no disclosure registry for it; the earlier reference to a "ZKA-Coord scoped-target policy" named a construct that does not exist. The disclosure vehicle is therefore ZKM's own, below.) The in-circuit depth_max assertion is §4.3 invariant 3. (Resolves the §10.6 residual:) a recursion-capable target MAY bake a larger depth_max into its per-target VK build — the constant is a build parameter, the registry entry (below) is its disclosure, and the u8 domain caps every build at 255 — but no target may make it prover-supplied, and the reference default remains 16. A depth bound is a redelegation prohibition at depth depth_max, and prohibiting redelegation is a documented anti-pattern: a delegate denied delegation shares credentials or proxies requests instead, destroying attribution and attenuation [DIF-PROBLEM] [DIF-THREATS]. In ZKM that failure is strictly worse than in certificate systems, because sharing the holder_key private key also shares the nullifier-secret derivation (§3) — impersonation hands over the entire mandate, including serialization control. A deployment choosing depth_max MUST weigh this: a bound tight enough to be hit by legitimate orchestration is a key-sharing incentive, and the disclosed bound SHOULD exceed the deepest delegation the deployment expects to be legitimate.

Delegate key binding (normative direction). DELEGATE is consumptive: the delegator's note is nullified in the same atomic transition that creates the rebound successor (§4.3-1, §4.3-4). A rebind to a key no one controls therefore silently destroys the delegated authority — a delegator-side loss equivalent to RETIRE, but unintended and undetectable until the delegate first fails to transition. This hazard is specific to conservation-based delegation and appears nowhere in the prior-art threat catalog [DIF-THREATS], for a structural reason: in the copy-based substrates the DIF reports survey (ZCAP, UCAN, Macaroons, Biscuit, OAuth token exchange), issuing a delegation never diminishes the delegator's own authority, so a mis-bind is discovered at the delegate's first invocation and repaired by re-delegating [DIF-PROBLEM]. Conservation (§1.1 goal 3) is what closes the amplification threats those catalogs do name; the burn hazard is its cost. Accordingly:

Possession-proof form (decided). The statement is a separate proof by the delegate (noir/zkm_delegate_possession, §5), conjoined with the transition at settlement by shared public inputs — the same composition idiom as the spend-conjoined form. It is deliberately not a witness inside the transition proof: the transition witness is the delegator's, so witnessing delegate_sk there would require the delegate to hand over its secret — the exact key-sharing collapse forbidden above. And it is not a bare signature, which would introduce a second signature scheme over KDC-derived keys where a proof of key derivation is the native idiom. The ceremony is one round: the delegator sends the delegate the intended successor opening and the transition's nullifier; the delegate returns the companion proof over that successor's commitment, the nullifier, and the settlement context; the delegator proves the transition and submits both. The delegator alone cannot fabricate the companion — its own secret does not open the delegate-bound successor — and neither party's secret crosses the boundary.

Cross-holder handoff (ROTATE). Specified and implemented (§4.2, noir/zkm_mandate_transition kind 5): the holder consumes its note and rebinds the full cap to a new key at the same depth — key rotation as a first-class transition rather than a self-delegation that burns depth budget. ROTATE is not gated by the policy's delegation permission and consumes no depth_limit headroom. Be precise about what ROTATE is: same-holder-ness is not provable in-circuit — a key is the protocol's only notion of identity, and rotation's purpose is changing it — so cryptographically ROTATE is an unrestricted clean handoff of the whole note, and a "rotation" to a third party's key is a full transfer at unchanged depth. This is deliberate, and priced: a holder denied rotation would share its key instead (the §6 anti-pattern), which transfers the mandate AND leaves the transferor with access — strictly worse on every axis (attribution, severance, nullifier-secret hygiene). A no-delegation (v2 depth_limit = 0) policy therefore binds the chain-extension behavior of honest holders; it does not — cannot — prevent authority transfer by a holder willing to hand over control, with or without ROTATE. Principals for whom that residual is unacceptable have short expiry and the §5 receipt discipline; a policy v3 rotation gate is noted in §10 should use cases demand one. What ROTATE shares with DELEGATE is the rebind hazard — a rotation to an uncontrolled key burns the holder's own authority with no counterparty to notice — so the §5 possession companion proof is REQUIRED, proven by the new key's controller. Expiry may narrow on rotation (§4.3-3); cap and depth cannot change; rotating to the consumed note's own key is rejected in-circuit.

Depth-bound registry (deployment registry). Every ZKM deployment MUST publish a registry entry, mirroring zka-registry conventions (allowlist JSON, versioned in-repo, CI-validated), disclosing per settlement target: the target identifier (CAIP-2-style, per zka-registry §2); the accepted ZKM VK identifiers with their vk_hash values (the §5 structural goldens are the reference source); depth_max; the §7.2 parameters (T, staleness_max, and the derived revocation-effectiveness bound); the accepted policy schema versions; and, when ZKC composition is enabled, the accepted ZKC proof types, inner VK hashes, profiles, issuer-policy roots, proof-digest algorithm, authenticated-scope policy, linkable-consent handling, and the selected direct-atomic-settlement or recursive-verification composition path. A recursive deployment MUST disclose the outer statement/VK that performs or carries the inner verification and MUST enforce the same VK, digest, exact-context, profile, public-vector equality and atomicity rules as direct settlement. An entry MUST NOT identify current zka:bundle:v1 as a ZKM composition path; a future, separately versioned bundle member/profile would need to be disclosed separately and enforce those same rules. The registry is the single disclosure vehicle this specification's MUST-disclose obligations refer to; an entry change is a protocol-visible event, not a configuration detail. [Registry file format and CI validation: mirror zka-registry's sdd_registry_encodings.md conventions when the first deployment lands.]


7. Revocation

7.1 Mechanism

Revocation is by root_id insertion into a version-2 exclusion accumulator, exercisable only by the principal's issuance authority. Because every successor and sub-mandate preserves root_id (§4.3), one revocation kills the entire subtree.

The accumulator is ZKM-owned normative surface. ZKC-Core's accumulator remains an issuer-maintained active-set membership tree; ZKM retains the opposite polarity because principals are hidden and self-issued. Registering every live root_id would require an issuer-like directory and refresh channel. Instead, an unrevoked mandate proves that its exact leaf remains empty.

Version 2 construction (normative): the accumulator is a depth-254 sparse Poseidon2 Merkle tree. The path is all 254 bits of the unique canonical BN254 field representative of root_id, consumed least-significant bit first. Implementations MUST obtain the canonical low/high limbs with the BN254 field decomposition constraint; an unconstrained 254-bit decomposition is invalid. An empty leaf is 0; revocation writes the non-zero full root_id as the leaf value at that same full-id path. Siblings are ordered leaf-to-root and use the same left/right Poseidon2 child convention as the mandate state tree.

The former version-1 tree used only the low 128 bits as its path. Two distinct identifiers could therefore share a slot and revoking either co-revoked both. Version 2 has an injective address function over canonical field elements, so such identifiers diverge in the high path and remain independently revocable. The witness changes from 128 to 254 siblings, and each transition public vector adds revocation_scheme = 2; the circuit rejects every other value. Version-1 roots, witnesses, proofs, and verification keys MUST NOT be interpreted under version 2. What ZKM takes from ZKC is the reviewed Poseidon2/Merkle convention and the epoch pattern — a per-epoch root history, a staleness bound, and unconditional rejection of future epochs (§7.2).

Revocation discloses root_id — the design's one deliberate disclosure, priced in §4.6 claim 3. Revocation authorization: a revocation insertion MUST be authorized by a proof of knowledge of the root_id preimage (principal_issuance_key, issuance_randomness) — the same material the §3 issuance proof guarantees exists for every accepted leaf. Implemented as of rev 6 (noir/zkm_revocation_auth, public vector (root_id, settlement_context)): a settlement layer MUST accept a revocation insertion only against a verifying proof under that VK whose public root_id is the id being inserted — without it, insertion is either operator-permissioned (a Safeguard-3 shape) or open to griefing with observed root_ids.

Cutover and recovery: an operator MUST export the complete version-1 set of revoked full root_ids from its authoritative insertion log, run scripts/migrate-revocation-v2.py, independently reproduce the emitted v2 root, and publish that root at a new activation epoch together with the v2 transition and spend verification keys. The switch is atomic: before activation only the v1 profile is accepted; at and after activation only scheme 2 and its v2 keys are accepted. No legacy root is relabelled, no proof crosses the boundary, and every previously revoked identifier is inserted into the v2 tree before it can be activated. If the authoritative revoked-id log is incomplete or the independent roots disagree, migration MUST stop; rebuilding from a partial log would revive authority. Because this remains an exclusion set, unrevoked mandates require no registration and cannot be stranded by omission. The published migration manifest is the recovery source: replay its sorted full-id leaves to reconstruct the root, then resume authorized insertions at later epochs. The operational sequence is pinned in zkm_revocation_v2_migration.md.

Revocation is therefore deliberately principal-only and subtree-total: an intermediate delegator has no mechanism to revoke its own delegate's sub-mandate — the delegate holds a live note bound to its key, and only root_id insertion (the principal's act) or expiry bounds it. This does not satisfy per-hop separate revocability, which the prior art treats as a requirement of delegation as such — each delegation "separately revocable from any other delegation" the delegator makes [DIF-PROBLEM]. The trade is deliberate: per-hop revocation requires per-hop revocation handles registered where a verifier can check them, and a per-hop handle is lineage — publishing it re-creates the delegation-graph disclosure ZKM exists to remove (§4.6). The mitigations are the delegator's, at DELEGATE time: a short expiry on the delegated successor (§4.3-3 permits any narrowing) combined with partial delegation — the retained note both bounds the delegate branch's cap and funds renewal, so short-expiry re-delegation from the retained note is a soft revocation lease (with a full rebind there is no retained note, and expired authority is renewable only by the principal). A privacy-preserving per-hop mechanism EXISTS and is now the artifact-v2 design direction (resolves §10.7 at design strength): delegation revocation handles. At DELEGATE, the delegator samples a pseudorandom handle h = H(delegator_revocation_secret, nonce) and the delegate successor's commitment carries it in a per-hop handle vector (one slot per ancestor hop, bounded by depth_max; slots inherit down the chain — a depth-d note carries d handles). Every transition then proves non-membership of each carried handle in the same §7.1 exclusion accumulator (worst case depth_max extra sparse-tree proofs — at the reference depth_max = 16, an estimated low-thousands of extra constraints; measured, not assumed, before v2 ships). A delegator revokes exactly its delegate's subtree by inserting h under a handle-preimage authorization proof (the §7.1 pattern at hop scope). Privacy: handles are pseudorandom and carried only inside commitments — publishing one at revocation links nothing, exactly the §4.6 claim-3 analysis at hop granularity; the delegation graph stays hidden. This is a commitment-preimage change, so it ships in the coordinated artifact-v2 break (§10) bundled with not_before (§4.5). Until then the v0.1 substitutes above are normative.

7.2 Freshness

Every transition — SPEND, SPLIT, RETIRE, both DELEGATE forms, and ROTATE — proves non-revocation at an accumulator root (§4.3-5); a revoked mandate cannot transition at all. The epoch is the one the verifier associates with that root in its own history — not a prover-supplied label, which would be unconstrained and could pair a stale root with a fresh epoch. The verifier chooses the staleness window it accepts. Note the inherent race this creates: a proof honestly generated against the epoch-E−1 root can be accepted after root_id is inserted at epoch E if the verifier's window permits. The race is not a ZKM defect but structural to revocation itself — [DIF-THREATS] names it directly ("revocation and request are concurrent in the Lamport sense — there is an unavoidable race between invocation and revocation") — so the obligation is to bound and adjudicate it, not to pretend it away. The specification MUST therefore define a maximum staleness bound and a settlement-layer ordering rule for revocations and transitions (of any kind) landing within the same epoch, and — per the [DIF-GOV] requirement that a framework define "whether in-flight actions are permitted to complete and how the outcome is recorded" — that ordering rule MUST make the outcome of a same-epoch collision deterministic and reconstructible from settlement state alone (which of the two took effect is a fact of the record, not of either party's log). A verifier that cannot obtain an accumulator root within its staleness window MUST reject rather than fall back to an older root: continuing under unverifiable authority is the fail-open failure mode [DIF-THREATS] [DIF-GOV] — absent a positive freshness determination, the default is denial. Those obligations are discharged as follows (normative direction):

Operational guidance for offline verifiers, by posture (closes the §10.1 residual): (1) Settlement-coupled (on-chain contracts, or services reading settlement state directly) hold the current root by construction — no guidance needed beyond the window. (2) Connected but detached verifiers SHOULD subscribe to the target's root publication and treat a failed refresh older than staleness_max × T as a hard stop for new acceptances, not a degraded mode. (3) Long-offline verifiers (point-of-sale during outage, air-gapped audit) have exactly two honest options: reject, or accept as credit — and a credit acceptance SHOULD be bounded by what expiry and the disclosed effectiveness lag price (worst case: the mandate was revoked the moment the verifier went offline), SHOULD be capped per-proof and cumulatively, and MUST be re-verified against settlement state before any dependent irreversible action. The §5 receipt makes the deferred audit possible: it carries the referenced root, so a later reconciliation can establish exactly what was knowable at acceptance time.

(The former ZKC-alignment item is resolved by the version-2 construction: the accumulator is ZKM-owned and exclusion-polarity, while the Poseidon2 child ordering and epoch-root lifecycle remain aligned with the reviewed shared surface. No ZKC issuer directory is required.)


8. Relationship to AFP §14

AFP §14 (Introduction Tier) grows bilateral, negotiated limit envelopes between federated tenants, enforced by counterparty policy inside a session. ZKM defines unilateral, principal-issued mandates, enforced in-circuit and verifiable by strangers outside any federation. They compose — an AFP tenant's agents MAY carry ZKM mandates whose caps sit inside the bilateral envelope — and neither subsumes the other.

Worked example. Tenants A and B hold an AFP §14 introduction-tier provisional agreement: envelope {asset: USDC, max_per_interaction: 1 000, max_cumulative_outstanding: 5 000, ZKA-direct only}, enforced at AFP's agreement/channel-policy layer (AFP §14.5). Independently, A's principal issues a ZKM mandate to its orchestrator agent: cap 800, expiry T+24h, Standard Policy v2 {max_per_spend: 200, depth_limit: 1}. The orchestrator DELEGATE_PARTIALs 300 to a sub-agent (successor depth 1 — the policy's limit, so the sub-agent cannot re-delegate; the possession companion accompanies the rebind). The sub-agent buys a 150-unit service from B: one spend-conjoined proof (§5) — cap 300→150, expiry, policy amount ≤ 200, non-revocation, ZKA value leg — settled ZKA-direct inside the AFP session. Composition points, each load-bearing: (1) B verifies the ZKM proof as a stranger — nothing in the mandate leg depends on the AFP agreement; the session is AFP's, the authorization is ZKM's. (2) The two limit systems bind independently — the 150 spend had to satisfy the AFP per-interaction envelope (≤ 1 000) at the channel-policy layer AND the ZKM policy ceiling (≤ 200) in-circuit; neither implies, contains, or must contain the other. Note where each learns the number, which rev 7 made load-bearing: the AFP envelope check happens inside the session, where B is the counterparty and has the recipient-note opening, so it knows the amount is 150. Nothing in this composition ever needed the amount to be on the settlement record — which is why removing it from the public vector (§4.6) leaves the worked example intact rather than breaking the AFP §14 leg. (3) A's principal revoking the mandate kills the sub-agent's spends at the non-revocation check — subject to the §7.2 window: a spend proven against a still-acceptable pre-insertion root can settle (prospective ordering), and the kill is total once the disclosed effectiveness lag elapses — regardless of the AFP agreement's health; an AFP downgrade shrinks the envelope without touching ZKM authority. (4) The AFP reputation growth (§14.6) sees settled interactions, not mandate structure: ZKM discloses nothing to the bilateral trust curve beyond what settlement already shows.


9. Safeguard Analysis

ZKM inherits ZKA §1.6's six Freedom Safeguards as normative invariants (§1.2). This section discharges the required analysis: how each safeguard binds a protocol whose purpose is gating value — the apparent tension the skeleton flagged at birth — followed by the threat-model anchors.

Safeguard 1 — Predicate Pluralism. Mandate policy predicates are the ZKM analogue of ZKC predicates, and the same rule binds: no policy provider, schema, or circuit is canonical. Standard Policy v1/v2 (§5) are reference schemas, privileged only in being published first; a deployment MAY accept any policy circuit that satisfies Safeguard 4's publication rule, and MUST NOT hardcode an "official" policy set beyond the VKs its registry entry (§6) discloses.

Safeguard 2 — Minimal Disclosure. A mandate proof discloses exactly the §1.1 statement; the public vectors are enumerated in §5 and the residual correlation surfaces in §4.6. There is no composite disclosure to consent to: the mandate statement is already atomic. Where a deployment uses one of §5's current same-subject ZKC statements, §5's direct-atomic-settlement rule governs that leg.

Safeguard 3 — No Revocability (of value). The load-bearing safeguard, because gating value is ZKM's purpose. The resolution is the authority/value distinction, made precise: revocation encumbers authority (mandate notes — including successors held by delegates, the intended §7.1 subtree kill), never value. ZKA note validity never depends on mandate state; a delegate whose mandate was revoked holds ZKA notes exactly as spendable and withdrawable as before — what ends is the ability to make new spends of the principal's value under the revoked authority. Revocation is exercisable only by the principal — the owner of the authority being revoked, never an operator or third party (the §3 issuance proof is what makes "only the principal" cryptographic: the revocation authority is the preimage holder). No ZKM mechanism may enable third-party or operator freeze, seizure, or redirection; §7.2's prospective-only ordering extends this to time (no clawback of settled transitions).

Safeguard 4 — Open-Source Predicates. Every ZKM circuit — the six reference packages and every policy predicate — is published, versioned Noir source with a public review period (§1.2); the structural-goldens gate makes an unreviewed circuit change loud. An SDK MUST refuse by default to prove against a policy the agent cannot inspect (§1.2), to prove a full rebind or rotation without the counterpart's possession companion (§5), and to answer an unauthenticated demand (§5 Receipts). Rev 7 adds the disclosure rules (§5 Voluntary Disclosure): an SDK MUST require an explicit act to assemble a disclosure, MUST default to the narrowest scope satisfying the request, MUST warn that disclosure is irreversible information release, and MUST NOT substitute a zkm:revocation-auth:v1 proof for a control attestation — that substitution would enclose a live revocation capability in the envelope.

Safeguard 5 — Agent Exit Rights. Custody operations remain permissionless: no mandate state gates a ZKA withdrawal, and revocation cannot strand value (Safeguard 3 analysis). Within ZKM itself the exit right is RETIRE — a holder may always surrender remaining authority unilaterally, with no counterparty, policy, or principal consent in the transition statement.

Safeguard 6 — Verifier Accountability. Specified concretely in §5 (Receipts and Verifier Accountability): authenticated per-nf demands with in-lineage standing, holder-retained self-contained receipts, no operator custody of receipts, and no principal-wide query surface.

Voluntary disclosure against the six safeguards (rev 7)

zkm:disclosure:v1 (§5) is the one rev-7 addition that could plausibly cut against the safeguards, so it gets its own pass rather than a clause.

S1 — Predicate Pluralism. The format is defined; no recipient is. There is no canonical auditor, no accepted-auditor set, no registry of who may receive a disclosure, and no notion of an attestation being "recognized" by anyone but the party the principal handed it to. A deployment MUST NOT hardcode an auditor identity or condition any protocol behavior on one, exactly as it MUST NOT hardcode an official policy set.

S2 — Minimal Disclosure. The scope kinds (§5) are the instrument, and the rule is that an SDK defaults to the narrowest scope satisfying the request. What is disclosed is precisely what the included openings contain — there is no derived, composite, or summarized claim in an envelope, so a recipient learns facts, never a computed judgment about the principal. Composition by pairwise envelopes means even a full-subtree disclosure is assembled from parts each of whose discloser consented.

S3 — No Revocability (of value). The load-bearing one, and the reason the posture paragraph in §5 is normative rather than advisory. Disclosure encumbers nothing. No settlement behavior, mandate validity, spend authorization, or revocation effect depends on disclosure state — the protocol has no way to represent disclosure state at all. No operator or third party can exercise a disclosure, demand one in-protocol, detect that one occurred, or automate any consequence of one. A deployment that made settlement conditional on a disclosure — a whitelist of attested lineages, say — would not be a stricter ZKM deployment; it would be outside this specification, and §5's posture paragraph says so.

S4 — Open-Source Predicates. noir/zkm_control_attestation is published, versioned, inspectable Noir source under the same review obligation as every other circuit, and its structural golden is in the same gate. The disclosure-side SDK rules are enumerated in §5 and listed under Safeguard 4 below.

S5 — Agent Exit Rights. Untouched. RETIRE never gates on disclosure, a ZKA withdrawal never gates on disclosure, and no holder can be blocked from surrendering authority or moving value by declining to disclose. Refusing to assemble an envelope has no protocol consequence whatsoever — which is what makes the disclosure voluntary in more than name.

S6 — Verifier Accountability. The envelope is the principal-side complement of the verifier-side demand path, not a replacement for it. The per-nf demand rule and the no-principal-wide-query rule are unchanged; the only party who can assemble a principal-wide view remains the principal, via its own pairwise chain, and now it has a standard format in which to present one.

Threat-model anchors ([DIF-THREATS] / [DIF-PROBLEM]):


10. Open Questions

The original nine questions carry positions taken as of rev 6; the ledger below records each position, followed by what actually remains open. The wording is deliberate and was corrected in rev 7. A position is a design decision this specification commits to and argues for — it is not a battle-tested outcome. Some positions are backed by working circuits (items 6 and 8), one is a refusal that could be reopened on new evidence (item 9), and the rest are reasoned designs that no deployment, adversary, or machine-checked proof has yet tried to break. Reading the ledger as a list of settled questions would overstate what this draft has earned; the §4.6 games are still sketches (§10.2 below) and artifact v2 is still unbuilt.

# Question Position
1 Revocation freshness rev 4 (§7.2 window/lag/ordering) + rev 6 offline-verifier posture guidance (§7.2).
2 Principal unlinkability rev 5 claims + rev 6 formal game sketches (§4.6: TL-IND, PM-IND, RV-1).
3 Counterparty-referencing policies rev 6 design position (§5): permitted, stranger-verifiable, recursion-gated, deferred.
4 MERGE / replenishment rev 6 (§4.5): MERGE excluded permanently-by-default; replenishment = artifact-v2 not_before.
5 Tree custody rev 6 (§4.2): per settlement target; APL interior deferred (below).
6 Depth bounds rev 5 (depth_limit + registry) + rev 6 per-target depth_max builds (§6).
7 Per-hop revocability rev 6 design direction (§7.1): delegation revocation handles, artifact v2; expiry leases normative until then.
8 Narrowing + revocation-auth circuits rev 6, implemented (§5, §7.1). The still-unenforced list is empty.
9 Policy v3 rotation gate DECLINED (rev 6) — the §6 key-sharing-displacement argument is the position; reopen only on new evidence.

Remaining open surface:

  1. Artifact v2 — the one planned protocol break: not_before (§4.5 replenishment) + delegation revocation handles (§7.1), shipped together as a coordinated all-VK re-bless with migration guidance for outstanding v1 mandates.
  2. Formal proofs — the §4.6 games at full (ideally machine-checked) strength, and a named-assumption reduction for the Poseidon2 PRF/hiding claims.
  3. APL / channel-interior transitions — consuming mandate authority inside payment channels (§4.2 custody note).
  4. The two narrow brackets: authenticated-demand transport (§5) and the deployment-registry file format at first deployment (§6).
  5. Association-set / provenance proofs — a possible future policy schema in which a holder proves its authority chain sits inside a stated "good" set without revealing which member it is, after the [PRIVPOOLS] association-set construction. It is the natural companion to rev 7's disclosure posture: disclosure proves who to a chosen party, an association-set proof would prove provenance to a stranger, and the two answer different compliance questions. Explicitly out of scope for rev 7 — no schema, no circuit, and no position taken on whether ZKM should have one; the §5 policy-version machinery is the extension point if it is ever wanted.

Appendix A (Informative). AP2 Mandates → ZKM Mandate

This appendix is INFORMATIVE. Nothing in it is normative, no RFC 2119 keyword in it binds an implementation, and ZKM defines no AP2 dependency. It exists because "how would this interoperate with the deployed agent-payment stack?" is the first question the design draws, and leaving it unanswered invites worse answers. [AP2] is the most developed selectively disclosable, but linkable, agent-mandate scheme and the closest direct counterpart, so it is the one worth mapping.

Read it as a positioning document with a worked field mapping, not as a profile.

The drift this appendix warned about happened, and the mapping below is the re-verified one. The rev-7 text was written against the AP2 v0.1 line and said its field names might not survive; they did not. AP2 v0.2 (released 2026-04-28, and the version this appendix is now written against) retires the v0.1 Intent Mandate and Cart Mandate entirely. In their place are two mandate types, each in an open and a closed state: a Checkout Mandate (mandate.checkout.open.1 / mandate.checkout.1, verified by the Merchant) and a Payment Mandate (mandate.payment.open.1 / mandate.payment.1, verified by the Credential Provider, Network and Merchant Payment Processor). The open Payment Mandate is the artifact the v0.1 Intent Mandate became for bridging purposes: it is the user-signed, pre-authorized, constraint-bearing object an agent later closes on its own, and it is what v0.2's Human Not Present (autonomous) mode runs on. Constraints moved out of named fields into a typed, extensible constraints array. The credential format is now SD-JWT VCs chained per the Delegate SD-JWT draft, not the JSON-LD Verifiable Credential shape the v0.1 line implied. Standardization has moved into the FIDO Alliance following Google's donation of the protocol, continuing in its Agentic Authentication and Payments Technical Working Groups.

Two cautions carry forward. First, this mapping is verified against the v0.2 specification text itself; most secondary coverage still describes the v0.1 Intent/Cart model, so a bridge implementer reading a summary rather than the specification will map to fields that no longer exist. Second, the same warning still applies to what follows — vct values carry an explicit numeric schema suffix precisely so that an incompatible revision can be introduced, and one eventually will be. The structural argument did not move across the v0.1 → v0.2 break, which is the point of having made it structurally.

A.1 The hard constraint, stated first

Never verify AP2's signatures in-circuit — and v0.2 does not loosen this. AP2 mandates are SD-JWT VCs signed with ECDSA over P-256: the specification's own worked examples sign with ES256, and the proof-of-possession key an open mandate endorses in its cnf claim is a P-256 JWK. A closed mandate therefore carries two such signatures — the issuer's over the open mandate and the agent's key-binding JWT over the closed one — and a longer delegate chain adds one more per link. That curve does not match BN254, and P-256 ECDSA verification inside a BN254 circuit costs on the order of two million constraints — against roughly a hundred thousand for the most complex circuit in a production shielded-payments system, and against ZKM's own spend-conjoined circuit at ~1.5 thousand ACIR opcodes. It is orders of magnitude off, per signature. A ZKM circuit that verified an AP2 credential would not be a slower ZKM; it would be a different and unusable artifact, and no amount of engineering makes the curve mismatch go away.

v0.2 also closes the obvious escape. To keep the checkout_hash from being guessable it requires the Checkout JWT to be signed with a non-deterministic scheme — ECDSA is the named example, Ed25519 the named exclusion — so the one widely deployed alternative curve is ruled out by the specification rather than merely absent from it. Ed25519 is in any case non-native to BN254 and would not have helped.

So the AP2 credential is verified off-circuit, at a trust boundary — and which boundary is the entire design question.

The boundary should be the principal's own stack — and v0.2 now gives that boundary a name. AP2 defines a Trusted Surface: a UI role that obtains informed user consent before a user-signed mandate is created, and the one role the specification requires to be non-agentic. That is exactly where a ZKM bridge belongs. In v0.2's User Credential delegation model the Trusted Surface holds the principal's own credential and mandates are delegated through an OpenID4VP presentation of it; a bridge sitting there verifies the open Payment Mandate it itself helped create, and then parameterizes a fresh ZKM self-issuance (§3) from what it read. The bridge consumes AP2 context and signs nothing to anyone. Self-issued authority is preserved exactly, because the thing that issues the ZKM mandate is the principal, as it always was.

The anti-pattern, named so it is refused explicitly: a third-party bridge service that verifies users' AP2 mandates and issues ZKM mandates on their behalf. That service is an external attestor — precisely the role ZKM exists to remove (§1.1: the issuer of a mandate is the principal itself, with no external attestor). It would reintroduce a party that knows every principal's identity and every mandate's parameters, sitting exactly where AP2's Credential Provider sits, while the ZK machinery downstream created an appearance of privacy that party's existence destroys. If a counterparty genuinely needs "this agent acts for a principal holding credential X", the correct construction is not a bridge but the §5 counterparty-referencing policy direction — ZKC-conjoined, recursion-gated, and deliberately deferred.

v0.2 makes the anti-pattern easier to build into, so it is worth locating precisely. The second delegation model, Trusted Agent Provider, has the agent's own provider operate the Trusted Surface and sign mandates with its backend keys, with verifiers trusting a list of such providers instead of a user credential. That model is entirely reasonable inside AP2 — it trades a broader trust list for a simpler integration — but a ZKM bridge built on it is the anti-pattern instantiated, not a variant of it: the party that verifies the mandate and parameterizes the issuance is the agent provider, so the ZKM mandate's effective issuer is a service, and every principal it serves is legible to it. The distinction to hold onto is not "first-party versus third-party company" but who holds the credential being verified. A bridge is acceptable when it verifies a credential the principal holds; it is an external attestor when it verifies a credential it holds about the principal.

As of this revision no third-party AP2-to-ZK bridge service has been publicly announced, so this remains a prospective warning rather than a criticism of a shipped product. Two things would change that and should be read as triggers to re-examine, not as safe intermediate forms: a Credential Provider offering "privacy-preserving mandates" as a hosted feature, and any Trusted-Agent-Provider-model service that issues on a user's behalf. Both are the same construction under different names.

A.2 Field mapping

Against AP2 v0.2. The source artifact is the open Payment Mandate unless stated otherwise: its constraints array is where the v0.1 Intent Mandate's spending limits went, and it is the only AP2 object that authorizes a class of future spends rather than one settled transaction.

AP2 v0.2 element ZKM field Notes
User (the User Credential, or the Agent Provider's user record) Principal issuance key Identity ends at the bridge. Nothing derived from it enters the mandate; root_id is a hash image (§3). v0.2 is agnostic about how the user is identified, so what ends at the bridge varies by deployment — but it always ends there.
Agent proof-of-possession key (cnf.jwk of the open mandate) holder_key KDC-derived by the agent itself and freshly generated (§6): a key supplied by the bridge or the principal collapses delegation into impersonation. v0.2 agrees on the direction of key flow — the agent's key is endorsed into the mandate it will later close, not issued to it — so this row is now a convergence rather than a correction. ZKM additionally requires KDC derivation and freshness.
exp (open Payment / Checkout Mandate) expiry Convert to the settlement target's clock. Where both exist, take the tighter bound. v0.2 RECOMMENDS the smallest exp that lets the agent finish the task, which is the same discipline §3 expects.
payment.budget max (+ currency) remaining_cap Converted to asset_id atomic units. Enforcement differs, and the difference is the point. AP2 evaluates a budget by requiring the verifier to track the running total across every prior closed Payment Mandate; ZKM consumes the cap as settlement state, in-circuit (§4). Note also that payment.budget is defined for use with payment.agent_recurrence — a non-recurring open Payment Mandate carries no total, and the faithful bridge sets remaining_cap from payment.amount_range max instead.
payment.amount_range max Standard Policy max_per_spend v1 or v2 (§5). The constraint's min has no ZKM analogue — ZKM has no spend floor and a bridge cannot synthesize one; if a mandate's min is load-bearing, that is a reason not to bridge it, not a rounding error.
payment_amount.currency (ISO 4217) / payment_instrument.type asset_id zka-registry canonical form. AP2's currency-plus-instrument pair does not determine a settlement asset by itself; the bridge chooses the asset_id and, in doing so, chooses the rail.
payment.allowed_payees (and checkout.allowed_merchants) Policy recipient_restricted + recipient_set_root Translation gap — name it, and v0.2 widens it. AP2 restricts by merchant identity — the Merchant object is id / name / website, carrying no settlement address at all — while ZKM restricts by recipient settlement address. The bridge must resolve merchants to addresses at issuance time, so a merchant that later changes addresses falls outside the allowlist. Identity-level restriction is the deferred counterparty-referencing-policy class, not something a bridge can synthesize.
payment.execution_date (not_before / not_after) not_afterexpiry; not_beforenone in v1 The half-gap the rev-7 text predicted, now a concrete named field rather than an inference from "Human Not Present". not_after maps cleanly onto expiry; not_before maps to artifact-v2 not_before (§4.5, §10), which is unbuilt, so a v1 bridge must either refuse a mandate whose not_before is in the future or hold it unissued until then — and should say which.
payment.agent_recurrence (frequency, max_occurrences) No analogue, and none approximable. ZKM's authorization predicate has no notion of period or occurrence count; the nearest construction is staggered pre-issuance of not_before-bearing artifact-v2 mandates, which is unbuilt. A bridge that drops the recurrence constraint and keeps only the budget has issued a strictly more permissive mandate than the user approved.
Delegation Policy v2 depth_limit AP2 v0.2 still has no depth concept: agent-to-agent delegation is called out as conceptually possible and explicitly out of scope. depth_limit = 0 — forbidding delegation outright — is therefore the faithful default when bridging a v0.2 mandate; any larger value is authority the principal did not express in AP2 and must be chosen deliberately.
payment.allowed_payment_instruments, payment.allowed_pisps No analogue. ZKM models an asset, not an instrument or a service provider; both constraints are discharged (or not) by the bridge's choice of settlement rail, outside the mandate.
payment.reference (conditional_transaction_id) No analogue. It binds a Payment Mandate to one Checkout; ZKM authorizes a bounded class of spends and has nothing to bind to.
Checkout Mandate (checkout_jwt / checkout_hash, checkout.line_items) No analogue — this is what the v0.1 Cart Mandate became. ZKM authorizes a bounded class of spends, not one exact cart, and models no line items. Nearest construction: a single-use mandate per cart — remaining_cap = exact total = max_per_spend, recipient set = {merchant}, short expiry.
Closed Payment Mandate Network-facing; it exists to give the Credential Provider, Network and Merchant Payment Processor visibility. Out of scope by construction: it is the artifact whose purpose is the disclosure ZKM removes.
Mandate Receipt (iss, result, reference) §5 receipt discipline New in v0.2 and worth naming: both schemes now end an authorization with a retained receipt. They are not the same object. AP2's is signed by the verifier and is what makes its dispute evidence non-repudiable; ZKM's is holder-assembled and is what the holder can later disclose (§5). The direction of trust is reversed, which is exactly the trade A.3 prices.

A.3 What survives, what is gained, what is lost

Survives the bridge: the cap, the expiry, the per-spend ceiling, the recipient allowlist (as addresses), and the delegation bound. These are the parts of an Intent Mandate that are constraints on spending, and they carry over cleanly because both schemes are ultimately expressing the same thing.

ZKM's gain is settlement-layer privacy: its authorization proof and ZKA settlement do not create a publicly linkable record of the principal, mandate caps, spend history, or delegation graph. This is stronger than, and distinct from, AP2 v0.2's selective-disclosure guarantee.

Two claims the rev-7 text made about AP2 no longer hold against v0.2 and are corrected here rather than quietly dropped. AP2 v0.2 does have selective disclosure, and requires it: mandates are SD-JWT VCs, open mandate constraints MUST be selectively disclosed so that irrelevant user intent does not leak, an agent MUST present only the disclosures a verifier's evaluation needs, and the Trusted Surface MAY insert decoy digests. And AP2's trust model is not only a curated registry: the User Credential model roots trust in an issuer of the user's credential, with the registry-shaped alternative — a trust list of Agent Providers — being the other of two options, not the whole design.

What selective disclosure does not provide is unlinkability, and the gap is structural rather than a matter of maturity. Every presentation of an open mandate carries the same issuer signature over the same salted digests and the same cnf key, so verifiers that see two presentations can link them and the issuer can link all of them; and the closed Payment Mandate deliberately discloses payee, amount and instrument to the Credential Provider, Network and Merchant Payment Processor, because authorizing the payment is what it is for. Hiding which constraints applied is not the same as hiding that this payer paid this payee this amount, and only the first is on offer. ZKM's claim is the second.

Cap enforcement diverges in the same direction. In v0.2 a budget is enforced by the verifier accumulating spend across presentations, the agent is expected to narrow its own open mandate's scope once it holds a receipt, and the defence against an agent closing several mandates against one open mandate is a MUST on the agent's non-LLM code plus a MAY for verifiers to reject overlaps. Those are reasonable rules for a scheme whose verifiers are known, identified institutions. ZKM's cap consumption is a nullifier: it is enforced by settlement state, against a delegate assumed hostile, with no verifier holding a running total — which is the only form that survives the verifier being a stranger.

Revocation is now an open gap on the AP2 side rather than a leak, and the rev-7 text's claim that AP2 inherits the VC status-list correlator is withdrawn: v0.2 specifies no revocation or status mechanism at all, only exp and the receipt-driven scope reduction above. The correlator hazard returns the moment one is adopted — a per-credential status index is a stable linkable identifier, the same operational-leak class §4.6 surface (d) discusses via [CRSET], but attributable rather than merely countable — so this row should be re-read, not treated as settled, when the FIDO working groups specify one.

Lost: AP2's chained, non-repudiable audit trail to the network. This is a real loss and should not be minimized — it is the property that makes AP2 tractable for liability and dispute handling, and it is the exact property unlinkability destroys. The two cannot both hold: a trail that lets a network reconstruct who authorized what is a trail that identifies the payer. v0.2 states the property outright rather than leaving it implicit: mandate-and-receipt tuples are retained by the Shopping Agent, Merchant, Credential Provider, Network and Merchant Payment Processor, and joined at dispute time by the hash linking the Checkout and Payment Mandates. That is a designed, distributed, cross-party evidence store, and ZKM has nothing like it and does not want one.

ZKM's replacement is holder-side and consent-based rather than network-side and automatic: the §5 receipt discipline retains everything needed to reconstruct a transition, and zkm:disclosure:v1 (§5) is the standard artifact for showing it. The cross-reference matters — a bridged principal shows its disclosure envelope to its own PSP, which is where AP2's compliance function actually lived. What changes is not whether the principal can be identified for compliance, but who decides and who holds the trail by default.

A.4 Selectively disclosed authorization can sit above transparent, low-cost settlement

§1.1 compares ZKM with AP2's selectively disclosed authorization model and with deployed transparent settlement. The comparison is no longer only against a specification; it now includes a running system, and an appendix that did not say so would be arguing against the weaker version of its counterpart.

x402 is the concrete instance. AP2 v0.2's own repository ships sample scenarios — human-present and human-not-present — that settle over x402, and the A2A x402 extension is reported production-ready for crypto payments. Public reporting through 2026 H1 puts cumulative x402 volume in the hundreds of millions of transactions across Base and Solana, at roughly $0.001 per payment in facilitator gas on Base with no protocol fee. Figures vary between sources and are cited here as public reporting, not measurements from this repository — the same discipline [ZKA-ECON] §6.5 applies to the same comparators.

What an x402 payment discloses is the whole of it. Settlement is an ordinary public stablecoin transfer: payer address, payee address, amount and timing are permanent public record, to everyone, indefinitely. The sharp observation for this appendix is what that does to the layer above it. An AP2 mandate over an x402 rail is selectively disclosed at the authorization layer and fully public at the settlement layer, and the second dominates — no amount of SD-JWT disclosure hygiene conceals a transfer that anyone can read off the chain. Selective disclosure does not survive contact with a transparent settlement layer, which is the clearest available statement of why ZKM's privacy claims have to be made at settlement (§4.6) and not at the mandate format.

The honest price. ZKM is an authorization layer and does not add a settlement step — the §5 spend-conjoined form carries the ZKA value leg in the same proof — so the per-payment figure a bridged deployment should quote is its settlement rail's. Measured on ZKA/Pay ([ZKA-ECON], W = 4, full fill): $0.2366–$0.3444 marginal per settled payment, against an irreducible amortization floor of roughly $0.02–$0.16 that survives unbounded batching. Against x402's ≈$0.001 that is about 20x–160x at the floor and two-to-three orders of magnitude at today's measured cost. The gap is structural — per-record bookkeeping gas and per-record data availability do not amortize away — so it is a permanent premium, not an interim one.

The conclusion is not that the comparison is unfavourable but that it is the right comparison, and it constrains how ZKM may be described. A bridged deployment is not a cheaper rail and should not be described as one; what it buys is the A.3 column, at roughly two orders of magnitude on fees. The appendix's privacy argument concerns residual verifier linkability after selective disclosure and, for x402, transparent settlement. It was never a cost argument, and the deployed comparator has now settled that it cannot become one.


Informative References

The three DIF reports are unratified works in progress (CC BY 4.0), cited as accessed 2026-08-03 — the closest existing prior-art threat analysis of delegated authority for autonomous agents, not ratified standards. Notably, no threat in [DIF-THREATS] covers the §6 burn hazard: every substrate it models delegates by copying authority, so the hazard class is specific to ZKM's consumptive DELEGATE.


Document History

Rev 11 addendum (2026-09-06) — effect-profile framework published alongside. Informative here; normative in its own document. Nothing in this specification changed except one §1.3 non-goal bullet: no circuit, artifact, public input, verification key, statement identifier, envelope, policy schema or test vector moved, and statement_set_id is byte-identical. docs/zkm_nonfinancial_profiles.md and protocol-family/zkm-effect-profile-registry-v1.json define typed non-financial effect profiles over the mandate semantics specified here, and designate this document's surface zkm:effect:value-transfer:v1. The reason it is a separate surface rather than an extension of §3: a non-financial effect added by rereading asset_id as a resource or remaining_cap as a call count would make one committed field mean two things under one commitment domain, so a single opening would authorize two effects and the difference would live only in the reader's expectation. Non-financial profiles therefore commit under their own commitment and nullifier domains — zkm/mandate_commitment/nf_quota/v1 and siblings — which makes cross-profile reinterpretation unwitnessable rather than merely forbidden, and they are negotiated by an explicit, fail-closed procedure with no default profile and no downgrade path. Every non-financial profile is reserved at that registry's revision 1: identifiers claimed, nothing deployable. The two registries move independently and neither may move the other.

Rev 10 addendum (2026-08-15) — Appendix A refreshed against AP2 v0.2. Informative only: no normative text, circuit, public input, VK, envelope, or test vector changed. Appendix A was written against the AP2 v0.1 line and warned its field names might drift. They did, so the mapping is re-verified against the v0.2 specification text (2026-04-28) rather than secondary coverage, which largely still describes v0.1. Intent Mandate and Cart Mandate are retired; the source artifact for bridging is now the open Payment Mandate, whose typed constraints array holds what the Intent Mandate expressed as fields, and the appendix title, the §1.1 pointer, and every mapping row are updated to v0.2 names. Rows that were inferences are now concrete fields: payment.budget, payment.amount_range (whose min has no ZKM analogue), payment.allowed_payees, payment.execution_date — which supplies the not_before the rev-7 text could only point at as artifact-v2 future work — and payment.agent_recurrence, which a bridge cannot approximate without issuing a more permissive mandate than the user approved. The hard constraint holds and is stronger: mandates are SD-JWT VCs signed ES256 with P-256 cnf keys, a closed mandate carries two such signatures, and v0.2 requires a non-deterministic signature scheme, which rules out the one widely deployed alternative curve by specification rather than by omission. The anti-pattern is located more precisely: v0.2's non-agentic Trusted Surface in the User Credential model is the boundary this appendix argued for, while its Trusted Agent Provider model is the external attestor instantiated — the test being who holds the credential under verification, not first- versus third-party; no such bridge service is publicly announced as of this date, so the warning stays prospective. Two rev-7 claims about AP2 are withdrawn as now false: v0.2 does have selective disclosure (and requires it), and its trust model is not only a curated registry — corrected in place, with the surviving distinction stated as unlinkability rather than disclosure, since a stable issuer signature, cnf key and a network-facing closed Payment Mandate remain linkable. The VC status-list correlator claim is withdrawn too: v0.2 specifies no revocation mechanism at all, so [CRSET] is re-cited conditionally against whatever the FIDO working groups adopt. New A.4 prices the deployed AP2-over-x402 pattern: AP2 v0.2 selectively discloses mandate constraints, while an x402 payment is a public stablecoin transfer at ≈$0.001, and against [ZKA-ECON] §6.5's measured $0.2366–$0.3444 marginal (floor ≈$0.02–$0.16) the premium is roughly 20x–160x at the floor and permanent — with the load-bearing structural point that selective disclosure at the mandate layer does not survive a transparent settlement layer. Comparator figures are cited as public reporting, not measurements from this repository. New informative references [X402] and [ZKA-ECON]; [AP2] updated to v0.2 and FIDO stewardship.

Rev 8 addendum (2026-08-04). At that revision, the composition baseline adopted ZKC v0.4.0; the current baseline is ZKC v0.4.1-draft as §5 states. The stale principal-credential pointer now targets §5's counterparty-referencing-policy direction; and that deferred direction now states the v0.4 presentation-envelope choices and the required same-subject binding that prevents borrowed-proof substitution. The §1.1 reuse summary is also reconciled with rev 5: ZKM reuses ZKC epoch and serialization conventions, not ZKC issuance or accumulator semantics. No circuit, public input, VK, envelope, or test vector changed.

Post-rev-11 non-breaking reconciliation (2026-08-21). req_reconcile_zkm_samesubject_composition_st_b84703a5 aligns the present-tense same-subject composition rules with the implementation delivered by req_specify_samesubject_zkc_composition_for__c4c1f2ef (PR #18). The current baseline is ZKC v0.4.1-draft; the current conforming carriage is direct atomic settlement, and current zka:bundle:v1 has no standardized ZKM member. This reconciliation changes no statement, ABI, VK, toolchain pin, family epoch, statement_set_id, last_breaking_revision, or revision number.

Post-rev-11 editorial consolidation (2026-08-22). The protocol motivation, consumable-cap rationale, self-issued trust boundary, family positioning, and revocation/value distinction now appear in an informative introduction before conformance language. The separate pre-specification scoping note is retired from the published surface, and the stale revision snapshot formerly embedded in the abstract is removed. No statement, ABI, verification key, circuit, toolchain pin, family epoch, statement_set_id, last_breaking_revision, or revision number changed.

Version Date Changes
0.1.0-draft 2026-08-01 Initial skeleton. §4 cap-consumption state model specified as normative direction (mandate note tree; SPEND/SPLIT/RETIRE; conservation, narrowing, nullification-as-serialization; counter model rejected in §4.7). All other sections carry obligations and open items only.
0.1.0-draft (rev 1) 2026-08-01 Circuit-informed amendments from the §4.3 reference implementation: nullifier preimage gains settlement_context (domain-separated nullifiers, §3); zkm/holder_key/v1 added to the KDC registry list (§2); zero reserved as the absent-field sentinel (§3); depth_max realized as a VK-baked constant (§4.3-3); successor distinctness incl. vs. the consumed commitment, and non-zero rebind requirement (§4.3-3); single nullifier set across all ZKM VKs per settlement context (§5); §5 binding list gains epoch label, current_time, settlement context; §5 scaffold paragraph replaced with the two implemented packages.
0.1.0-draft (rev 2) 2026-08-01 Spend-conjoined circuit (§5, noir/zkm_spend_conjoined) and its review hardening: settlement_domain bound into the mandate commitment as the authority domain binding, asserted equal to settlement_context (§3, closes cross-domain authority multiplication — nullifier folding alone only stopped verbatim replay); policy_hash blinded (§3, closes parameter disclosure + lineage correlation); ZKM Standard Policy v1 defined (§5); three-legs-one-holder unification and value conservation (§5); zkm/policy/v1 + mirrored ZKA value-note tags (§2); revocation epoch removed as a public input — the verifier derives it from the recognized root (§4.3-5, §5, §7.2); revocation slot MUST be a canonical field decomposition (§4.3-5, closes a non-canonical-to_be_bits soundness hole that let a revoked mandate prove non-revocation); zkm_mandate_spend_unconjoined superseded by and deleted in favour of zkm_spend_conjoined.
0.1.0-draft (rev 3) 2026-08-03 Prior-art integration from the DIF Trusted AI Agents WG delegated-authority reports (new Informative References: [DIF-PROBLEM], [DIF-THREATS], [DIF-GOV]). §6: delegate key binding specified as normative direction — full rebind MUST prove delegate-key possession (the burn hazard named as the cost of consumptive delegation, absent from copy-based prior art), partial delegation SHOULD (small-cap probe otherwise), fresh delegate-generated keys (impersonation collapse); depth-bound = redelegation-prohibition anti-pattern caution; attenuation threat citation. §7.1: principal-only subtree revocation stated as a deliberate trade against per-hop revocability (lineage disclosure), expiry-lease + partial-delegation mitigations, new §10.7. §7.2: race grounded in the prior art's Lamport-concurrency framing; same-epoch ordering rule extended to require a deterministic, settlement-state-reconstructible recorded outcome; fail-closed rule for verifiers unable to obtain a fresh root. §9: pairwise-accountability, aggregate-authority (composability), and ex-ante-only shadow-delegate anchors. §1.1: positioning vs. the clear-text attenuated-capability family and Governance TOCTOU. §4.3-3 possession-proof pointer; §5 still-unenforced list gains the possession proof.
0.1.0-draft (rev 4) 2026-08-03 Rev-3 follow-ups closed. §7.2 staleness/ordering specified (normative direction): disclosed epoch cadence T + staleness_max root-recency window (SHOULD be 1); derived effectiveness lag (1 + staleness_max) × T MUST be disclosed (the priced §7.2 race); ordering key = the settlement target's canonical transaction order; revocation prospective-never-retroactive with outcomes reconstructible from settlement state; off-settlement verifiers beyond the window make a credit decision, not a freshness claim; §10.1 closed accordingly. §5 Receipts and Verifier Accountability (Safeguard 6): zkm:receipt:v1 transition receipt (re-verifiability rule), per-hop delegation record (identity material + companion possession proof + successor opening), verifier retention + per-nf authenticated-demand standing (no principal-wide query — the pairwise chain is the principal's route). §6 possession-proof form decided and implemented: noir/zkm_delegate_possession companion proof (delegate-generated, matched at settlement on cm_out_1/nf/settlement context, single-use per consumed note, zero added disclosure); §5 companion-matching rule MUST (DELEGATE_FULL) / SHOULD (DELEGATE_PARTIAL delegate leg); possession removed from the §5 still-unenforced list; harness + structural goldens extended (additive — existing VK hashes unchanged).
0.1.0-draft (rev 5) 2026-08-03 Skeleton closed: every rev-4 bracketed item specified or implemented, save two named residuals (authenticated-demand transport, §5; deployment-registry file format at first deployment, §6). §3 issuance corrected (preimage knowledge, not a ZKC signature — self-issued authority has no external attestor) and implemented (noir/zkm_issuance; every accepted leaf provably has a living revocation authority; zkm/issuance_key/v1; principal key lifecycle + retention-is-revocability hazard). §2 encodings/serialization specified; KDC tag ownership resolved per §A.4 (ZKM-owned, no central registration). §4.2/§4.3 gain ROTATE (§6 cross-holder handoff, implemented: same holder, same depth, possession companion REQUIRED). §5: Standard Policy v2 (per-mandate depth_limit; v1 = permit-all, normative) with the delegation gate enforced in-circuit on the mandate-only VK; narrowing registry v1 (parameter-wise; equal-root; v2→v1 forbidden); policy_hash removed from the public vectors (lineage-correlator fix, found writing §4.6); zkm:proof:v1/zkm:receipt:v1 serialization (VK out-of-band). §4.6 unlinkability claims stated (3 claims + residual surfaces). §6: ZKA-Coord citation corrected (no such registry in ZKA — §12.1 scoped form only); ZKM deployment registry defined. §7.1 accumulator polarity corrected (ZKM-owned exclusion tree; ZKC reuse is the epoch pattern only); revocation-authorization proof required (implementation flagged). §8 worked example (AFP §14 introduction-tier composition). §9 full six-safeguard analysis. Four VK re-blesses + zkm_issuance, 12 harness shape classes, 220 tests.
0.1.0-draft (rev 6) 2026-08-03 §10 answered: a position taken on each of the nine original questions (ledger in §10; this row said "discharged … resolved" until rev 7 reframed the ledger — see that row). Implemented: in-circuit §4.3-3 narrowing on the mandate-only VK (assert_policy_narrows; per-successor policy hashes; spend VK deliberately strict) and the §7.1 revocation-authorization proof (noir/zkm_revocation_auth — only the issuance-preimage holder can revoke; blocks observed-root_id griefing). §5 still-unenforced list now EMPTY. Positions taken by design: offline-verifier posture guidance (§7.2); §4.6 formal game sketches (TL-IND, PM-IND, RV-1); counterparty-referencing policies (recursion-gated position, §5); MERGE permanently excluded + replenishment via artifact-v2 not_before (§4.5); per-target tree custody (§4.2); per-target depth_max builds (§6); per-hop revocability via delegation revocation handles as the artifact-v2 direction (§7.1); policy-v3 rotation gate declined (§6 position stands). Remaining surface: artifact v2, full formal proofs, APL interior, two narrow brackets. Six packages, 246 tests, 13 harness shape classes (this row said 14 until rev 7 counted the SHAPES array).
0.1.0-draft (rev 7) 2026-08-04 §10 ledger reframed from resolutions to positions taken: a position is a design decision this draft commits to and argues for, not a tested outcome (column header "Resolution" → "Position"; the rev-6 row's "discharged … resolved" softened in place). Item 9 stays DECLINED and item 8 stays implemented — both accurate; no position was altered. §4.6 gains residual surface (d), the last unpriced disclosure in the document: transition kind and successor arity are public per transition, so an observer learns the counts and timing of each transition kind and of revocation insertions on a settlement target — the [CRSET] "operational information" leak class reproduced at the delegation layer, with ZKM's distinction being that the activity is unattributable, not invisible. Priced as structural, not fixable: kind is load-bearing for the §5 companion-matching rule, arity for successor-slot semantics, and uniform-shape padding is defeated by the zero sentinel and the two proof forms' differing vector lengths. §4.6 claim 1 gains kind/arity as coarse public attributes, and TL-IND is corrected: the swapped transition pair MUST agree on every public element that is not a commitment or nullifier — as previously stated the game was winnable by swapping unequal kinds or amounts, so this is a soundness fix to the claim, not a convenience weakening. New informative reference [CRSET]. §5 Voluntary Disclosure — zkm:disclosure:v1 (the rev-7 headline): a holder-assembled, out-of-band envelope of receipts, delegation records, note openings and control attestations, with a self-verification rule extending the receipt rule (re-verify every proof, open every commitment, check every inclusion reference against settlement state, check chain consistency), three scope kinds, and pairwise composition mirroring §9 — so a subtree disclosure is a chain of consented envelopes and the protocol never creates a party with a global view. The hazard this design turns on: a zkm_revocation_auth proof is the obvious way to prove lineage control, but its public vector IS the revocation-insertion authorization, so an envelope carrying one would enclose a live capability to kill the lineage it discloses. Resolved with a distinct statement zkm:control-attestation:v1 (noir/zkm_control_attestation, additive — public vector lineage_tag, settlement context, disclosure_binding, where lineage_tag = H_{zkm/control_attestation/v1}(root_id, disclosure_binding) as first landed — amended below to include settlement_context in the preimage, which is the normative form). The resolution is structural: the vector carries no root_id, and revocation is root_id insertion, so the VK is incapable of authorizing one — the same species of argument §5 makes for the mandate-only VK carrying no ZKA value leg. Backed at three levels: the absent public input, the §6 one-VK-per-statement registry rule (now spanning six statements), and §7.1's revocation-auth-VK-only acceptance rule. Replay-inertness of every other envelope artifact was checked rather than assumed (transition proofs one-shot via nullifier insertion; issuance re-insert idempotent; possession bound to a consumed nf). Disclosure MUST NOT transfer authority (no issuance-key preimage); SDK rules added to §9 Safeguard 4 (explicit act, narrowest scope, irreversibility warning, and MUST NOT substitute a revocation-auth proof); §9 gains a six-safeguard pass on disclosure, S3 load-bearing — no protocol behavior reads or conditions on disclosure state; §4.6 prices it at zero added observer surface (nothing about an envelope reaches settlement). §5 due-process reference resolved: not a protocol role, deployment-disclosed and out-of-protocol. §10 gains association-set/provenance proofs as explicitly out-of-scope future surface ([PRIVPOOLS]). New tags zkm/control_attestation/v1 + zkm/disclosure_binding/v1 (KAT-guarded, collision-checked against KDC/ZKA/ZKC). Harness: 14th shape class, and a new directed negative case N3 asserting the fixture's root_id appears nowhere in the attestation's bb-emitted public inputs — mutation-tested by republishing root_id in its place, which the public-input-count tripwires do NOT catch (count stays 3) but N3 and two unit tests do. Also corrected: stale "PROVISIONAL pending KDC registration" comments in zkm_lib (settled in rev 5), the harness's stale 15/23 cross-VK figures (re-measured after the amount change: 13, so 16 vs 21), the rev-6 row's shape-class count, and README's rev/circuit list. §4.6/§5 amount made PRIVATE in the spend-conjoined vector (14 -> 13 elements; the one VK re-bless in rev 7). Investigated per the review brief and decided in docs/zkm_amount_commitment_decision.md. Finding: ZKA's own internal shielded spends publish NO cleartext value — zka_transfer, zka_transfer_multi, zka_transfer_binding, zka_pay_transition (not even the fee), zka_pay_batch, and both cross-chain shielded exports — cleartext amounts appearing only in the three POOL-BOUNDARY circuits (deposit/withdrawal/bridge exit) where a contract must move real funds. Publishing amount therefore put ZKM BELOW its settlement layer's own baseline at an internal hop. Decided to DELETE the public input rather than replace it with an amount_cm: zka_cm_recipient (already public, already in the vector) IS a binding commitment to the amount, so a second commitment buys no hiding while costing a domain tag, a fourth uniform-randomness obligation and a new zero-blinding failure mode. No consumer reads the cleartext — none of the five settlement effects touches it, and the payee could never establish payment from it anyway (recipient_address is private), so it verifies against zka_cm_recipient from the note opening it must receive regardless. Range safety MEASURED, not assumed: ACIR opcodes unchanged at 1454 (a lost range constraint would have dropped them), and amount: Field does not compile — assert_policy_permits_spend's ceiling comparison needs Ord, which Field lacks. Consequential corrections: §4.6 claim 1 and residual (a) rewritten (the old "amount publicity is inherent to being paid" was FALSE — ZKA disproves it; the counterparty still learns the amount, observers no longer do); TL-IND no longer names amount; the mandate-only VK's deployment-safety argument restated to rest on carrying no ZKA value leg rather than no amount, which stopped discriminating once neither form published one (same correction applied to both harness scripts' error text); §5 receipts now REQUIRE the ZKA recipient-note opening, since the vector no longer carries the value a receipt must be able to open; §8 unaffected and now says why. asset_id stays public. Appendix A (informative): AP2 Intent Mandate -> ZKM mandate, with a §1.1 pointer. States the hard constraint first — AP2's ECDSA P-256 VCs are NEVER verified in-circuit (~2M BN254 constraints against this spec's ~1.5K-opcode spend circuit), so the credential is verified off-circuit and the boundary MUST be the principal's OWN wallet/credential provider, which then parameterizes a fresh §3 self-issuance. Names the anti-pattern explicitly: a third-party bridge issuing ZKM mandates on users' behalf is an external attestor — the exact role §1.1 exists to remove — and the correct third-party form is the §5 counterparty-referencing-policy direction. Field mapping table with the translation gaps named rather than papered over (AP2 restricts by merchant IDENTITY, ZKM by settlement ADDRESS; no analogue for Cart/Payment Mandates or "Human Not Present" until artifact-v2 not_before). Concludes honestly on what is LOST: AP2's chained non-repudiable audit trail to the network, which unlinkability necessarily destroys — replaced by holder-side receipts and the rev-7 disclosure envelope, which is what a bridged principal shows its own PSP. Post-merge correction (CodeRabbit review of the rev-7 range): settlement_context was a bare public input of zkm_control_attestation — asserted non-zero and otherwise unconstrained, the "§5 hygiene" shape the other statements use — and is now folded into the lineage_tag PREIMAGE. Sound either way (UltraHonk binds every public input, so no forgery was possible), but semantically incomplete where it mattered: rule 1(e) has the recipient recompute the tag, and with the context outside the preimage that recomputation could not detect an attestation presented against the wrong settlement domain — leaving domain-matching a rule the recipient must remember rather than a property its own check enforces. The tag now identifies (lineage, domain, envelope) and one equality covers all three. zkm_control_attestation VK re-blessed (16 -> 19 ACIR opcodes; public-input count UNCHANGED at 3, so the no-root_id safety property is intact); no other VK moved. Two tests added for the domain binding. Seven packages, 261 tests, 14 harness shape classes. Rev 7 moved two VKs in total (zkm_spend_conjoined, zkm_control_attestation); the other four are byte-identical against rev 6.
0.1.0-draft (rev 8) 2026-08-04 Reconciled the composition baseline with ZKC v0.4.0-draft and its presentation-profile ladder. §5 delegation records now use zkc:presentation:scoped:v1 as the normal ZKC identity form, permit zkc:presentation:linkable:v1 only with explicit consent recorded, and reject zkc:presentation:anonymous:v1 as sole identity material while allowing it alongside out-of-band identity evidence. This changes holder-side pairwise-accountability evidence only: no presentation material enters settlement, so §4.6 transition unlinkability, cross-mandate principal unlinkability, TL-IND, and PM-IND are unchanged. ZKA remains the existing v0.7+ floor.
0.1.0-draft (rev 9) 2026-08-04 Specifies and implements same-subject ZKC composition for issuance and spend. Adds exact issuance/spend proof contexts, canonical inner-proof/VK/profile binding, zkm:issuance-zkc:v1 and zkm:spend-zkc-binding:v1, direct-settlement atomic matching, optional equivalent ZKA-bundle/recursive carriage, registry/envelope/receipt rules, and explicit v0.4 profile acceptance: scoped normal, linkable consented, anonymous only in a predicate-specific shared-witness circuit because it exposes no subject handle. Two additive Noir packages prove the KDC entity seed jointly derives the principal issuance key or ZKA recipient identity and the ZKC scoped/linkable handle. Cross-profile positive shapes and negative holder, recipient, issuance-key, context, profile, proof, outer binding and VK cases are covered without changing any existing VK. The review-complete form pins the complete v1/v2 envelope member/type surface, unpadded base64url proof-byte encoding, RFC 8785 digest KAT, self-contained receipt material, issuance inner/outer binding equality and recursive registry disclosure.
0.1.0-draft (rev 10) 2026-08-14 Replaces the provisional depth-128 low-half revocation slot with the version-2 depth-254 full-identifier exclusion tree. Canonical BN254 limb decomposition makes the path injective, leaves bind the full revoked root_id, and low-128 collision regressions prove that revoking one identifier does not co-revoke another. Mandate-only and spend-conjoined statements become v2, add public revocation_scheme = 2, reject legacy scheme values, use 254-sibling witnesses, and receive new VK/artifact goldens. Defines atomic epoch/key cutover and fail-closed recovery, and adds deterministic migration tooling that reconstructs the v2 root from the complete authoritative revoked-id log without registering or stranding active mandates.
0.1.0-draft (rev 11) 2026-08-15 Closes the remaining ways a family consumer can combine ZKM with an incompatible statement surface, and publishes the v1→v2 cutover note the rev-10 break needed. Context: ZKM's version string is 0.1.0-draft for every revision, so the pre-existing epoch could not tell rev 9 from the breaking rev 10; family epoch zka:protocol-family:2026-08-15.2 (ZKA PR #293, vendored here in PR #26) fixed that instance by pinning ZKM as 0.1.0-draft (rev 10) and reconciling ZKA §5.4.1 to the v2 statements and the 14-element vector. Two gaps survive that fix, and this revision closes them. (a) The label is prose: it pins a revision NAME, not a verification key, a public vector, a bytecode digest or a toolchain, so any of those can move within a revision without moving a character the epoch pins. (b) Nothing in the manifest compels a LATER breaking revision to advance the label — the epoch's revision assertion is satisfied by rev 10's changelog row, which is historical and stays true forever, so a future breaking rev 12 would leave every epoch assertion green. Adds protocol-family/zkm-statement-registry-v1.json: a ZKM-owned, digest-pinnable registry of the eight current statement identifiers with their circuit packages, public-input vectors (name + ABI type, in main declaration order), VK hashes, ACIR opcode counts and bytecode digests; the two retired v1 statements with their omission and non-reinterpretation rules; the revocation scheme; the pinned toolchain (VK hashes are toolchain-bound); and a revision_history carrying a statement_set_id per revision. §5 gains the matching normative rule: the registry, not the version string, is ZKM's citable composition identity; a breaking revision MUST bump the revision, append a history entry whose statement_set_id differs from its predecessor's, and move last_breaking_revision; and a breaking revision MUST be followed by the family manifest's revision label, with a conforming release gate failing closed while it is not. scripts/check-zkm-statement-registry.py (cheap tier, no toolchain) re-derives every one of those claims from its real source — the blessed structural goldens, each circuit's own fn main, .noir-version, the specification's §5 enumeration, and the mirrored family manifest — recomputes statement_set_id, rejects a history that classifies a moved surface as non-breaking, and enforces the label invariant in both directions (the family may not pin a revision ZKM has not published, nor one older than last_breaking_revision). --self-test reintroduces 35 real regressions, each in the careless spelling AND in the resealed spelling a diligent-but-wrong maintainer would produce, and requires every one to fail closed; every check is load-bearing under pairwise removal. The registry also records whether the manifest binds its digest, enforced in both directions, so the ZKA-side digest adoption can be neither claimed early nor forgotten late. docs/zkm_v1_to_v2_cutover.md is the consumer-facing normative note: current identifiers and keys, both v2 vectors with the one-slot shift against ZKA §5.4.1's naming, what a post-cutover registry MUST contain and MUST omit, the migration path for a target that verified v1 proofs (no dual-acceptance window; one mandate-nullifier set across the cutover), the gate a consumer should execute, and the artifact-binding stanza that adds the digest half of the pin. No statement, ABI, verification key, revocation scheme or toolchain pin moved: rev 11 carries rev 10's statement_set_id byte-identically, which is exactly what a non-breaking revision is required to look like.
0.1.0-draft (rev 12) 2026-09-08 Upgrades the proving toolchain from nargo 1.0.0-beta.22 and Barretenberg 5.0.0-nightly.20260522 to nargo 1.0.0-beta.25 and Barretenberg 5.0.0. All eight verification keys are re-blessed; mandate-transition and spend-conjoined ACIR/bytecode identities move. Statement identifiers, public-input vectors, and revocation semantics are unchanged. This is a breaking statement-surface revision: consumers must pin its new statement_set_id, and the ZKA-owned family epoch must advance its revision label and statement-set assertion before the combined release is accepted.