# Zero-Knowledge Agents (ZKA) Protocol Specification **Version:** 0.9.0-draft **Status:** Public Specification (draft revision) **License:** Apache 2.0, with a royalty-free patent non-assertion covenant for conformant implementations (see [License](#license)) **Domain:** ZKA.dev **Date:** 2026-08-15 > This file is the canonical specification; versioned point-in-time snapshots live in `docs/archive/`. --- ## Abstract Zero-Knowledge Agents (ZKA) is a protocol family enabling privacy-preserving interactions between autonomous agents. ZKA defines cryptographic primitives for private payments, verifiable coordination, and anonymous attestation. This document specifies the **protocol layer**: circuit designs, proof formats, nullifier semantics, settlement interfaces, and — new in v0.6.0 — the **normative ZKA/ZKC integration**: how zero-knowledge compliance credentials bind to ZKA notes without linking identities. Implementation details are left to conforming systems. ZKA targets **provable confidentiality**, not unconditional anonymity: a holder can prove specific properties (compliance predicates, balance thresholds, identity binding) to a chosen counterparty while revealing nothing else. The design treats compliance as an **interaction-boundary** concern, never a custody-boundary one — the architectural commitments that enforce this distinction are specified normatively in §1.6 (Freedom Safeguards as Protocol Invariants). **Version 0.9.0-draft Changes:** - **Legacy root-update blob-binding claim corrected.** The existing `zka_root_update` circuit binds only a non-zero, caller-selected public tag into the proof; it does not constrain its private `blobCommitments` to an EIP-4844 blob, and the deployed immutable APLBridge does not execute `BLOBHASH`. A successor claiming blob data availability MUST both compare a same-transaction `BLOBHASH` result on-chain and verify that the blob's canonical contents encode the private commitments folded by the proof. This is a specification/comment correction only; it does not change the legacy circuit, verification key, deployed bytecode, or live vulnerability. - **Fresh note-randomness invariant.** Every active output note now requires fresh, nonzero, uniformly sampled BN254 scalar randomness generated internally by the holder from an operating-system CSPRNG. Production APIs do not accept caller-supplied randomness; batch/process reuse is rejected, deterministic constructors are restricted to non-production test-vector builds, and diagnostics redact openings. Circuits enforce the properties they can observe (nonzero output randomness and no reuse among the transaction's input/output openings) without claiming to prove entropy. - **Machine-checked protocol-family interface epoch.** The canonical cross-specification composition surface is `zka:protocol-family:2026-08-15.2`, published under `protocol-family/`. Its manifest and compatibility matrix pin ZKA 0.9.0-draft, ZKC 0.4.1-draft, AFP 0.3.9-draft, ZKM 0.1.0-draft (rev 10), AFP-KDC 1.0.2, the distinct ZKA/ZKC/ZKM Noir and Barretenberg toolchains, bundle/proof/profile combinations, public-input ordering, domain tags, and all six Freedom Safeguards. Unknown or ambiguous combinations fail closed. AFP, ZKC, and ZKM consume byte-identical, commit-locked snapshots; this authority covers composition only and does not move AFP behavior, ZKC proof semantics, or ZKM authorization semantics into ZKA. ZKM's own version string is identical across all of its revisions, so a bare `0.1.0-draft` pin cannot discriminate one from another: the ZKM tuple entry carries the revision label and the epoch additionally asserts that revision's changelog row and its statement identifiers and ABI arities. A ZKM revision that changes a composed statement therefore requires a new epoch rather than passing a matching version check. - **AFP-KDC v1.0.2 authority package.** AFP-KDC v1.0.2 publishes the normative Poseidon2 parameter serialization, operational-domain registry, and seven-case raw-sponge KAT package. It preserves the v1.0.1 derivations, domain fields, proofs, verification keys, circuit identifiers, and artifact/profile identity strings byte-for-byte; only the current normative authority and conformance coverage move. - **AFP-KDC v1.0.1 correction.** Seed bytes are reduced as one unsigned big-endian BN254.Fr integer; `sk` and `cbk` are sibling derivations from that seed, and `holderCommitment` uses the `zkc/binding` domain. Corrected credential and transfer-binding artifacts, VKs, proofs, and the EVM verifier have explicit `afp-kdc-v1.0.1` identities. The current tree supports already-issued `zka_transfer_binding:legacy-kdc-v1` proofs only through the retained EVM verifier; legacy credential and off-chain transfer-binding identities are hash tombstones, and legacy proving requires the retained pre-correction source/toolchain. Note migration and unconditional exit remain available through their explicit custody paths. - **Poseidon2 construction clarified.** §2 and Appendix B now state the deployed Noir/Aztec/Barretenberg width-4, rate-3 sponge precisely, distinguish the permutation width from the `H_2`/`H_3` message arity, and spell out the existing two-layer note commitment, nested nullifier, and untagged Merkle-parent constructions. The domain-tag text now matches AFP-KDC v1.0.2. This reconciles stale prose with the already-pinned implementation and vectors; it changes no field, hash output, circuit, verification key, or test vector. - **ZKA/Coord v2 normative claim boundary.** Replaces the unenforced legacy §4 prose with a fixed 30-field execution receipt and 33-field final recursive ABI; binds workflow definition/instance/context/topology, verifier-selected profile policy, complete manifest/VK identity, ordered predecessor data flow, assurance class, external facts, and data-availability responsibilities. The complete design, deterministic errors, migration rules, and evidence gate are normative in `docs/sdd/sdd_coord_v2.md`. - **Coord v2 is the pre-launch canonical draft.** ZKA had no launched Coord consumer, so v2 replaces the pre-launch implementation in place without a compatibility path. It remains unaudited and is not production-supported until the signed independent-review gate passes. Public Rust SDK names are unversioned, while every security-sensitive wire, schema, circuit, artifact, policy, and hash identity remains explicitly v2. - **Pre-launch Coord proofs are invalid.** The former five-field bundle object, four-field base circuit, and three-field aggregate have no active decoder, artifact, registry entry, or verifier fallback. Historical behavior remains available only in Git history and archived specifications. The optional `coord_proof` member now carries the canonical 33-field v2 final receipt; this does not change ZKA note validity, custody, nullifiers, transferability, settlement, or exit rights. - **§5.4.1 ZKM direct authorization settlement.** Selects the direct settlement interface for `zkm:spend-conjoined:v2`, fixes its 14-field public vector (including `revocationScheme = 2`) and target/VK/root checks, requires one mandate-nullifier set across all accepted ZKM VKs, and makes the mandate nullifier, ZKA nullifier, mandate successor, and two ZKA outputs one atomic state transition. `zkm:mandate-only:v2` remains structurally unable to move value over its 9-field vector, ordinary ZKA transfers remain ZKM-free, and withdrawals remain unconditional. The pre-cutover 13-/8-field v1 statements are not accepted by a post-cutover registry. No `zka:bundle:v1` member or version change is introduced. - **ZKC v0.4 presentation profiles.** `zka:bundle:v1` now discriminates legacy linkable `zkc:proof:v1` from anonymous/scoped `zkc:proof:v2`. Payments using v2 MUST use a non-zero scoped presentation and `zka_transfer_binding_v2`, which proves note ownership and credential-subject control from one private master seed while exposing only `(presentationPseudonym, scopeId)`. Anonymous v2 remains valid for payment-free, context-bound work attestations. Cross-version/profile replay fails closed; v1 remains accepted unchanged. **Version 0.8.1-draft Changes:** - **ZKM authorization boundary (informative).** ZKM is recorded as the optional principal-issued authorization layer above ZKA settlement. A ZKM proof may constrain whether a particular authorized interaction is accepted; it cannot change ZKA note validity, custody, transferability, or unconditional exit. The current `zka:bundle:v1` has no ZKM carriage member, so a future integration must define an additive bundle profile or a direct atomic settlement interface before claiming bundle-level ZKM support. No circuit, contract, field, hash, envelope, or test vector changes. - **Nullifier-slot canonicity (normative clarification).** A ZKA/Pay sparse nullifier tree indexes a nullifier by the low 128 bits of its **canonical** field representative. Deriving that index from a decomposition of 254 bits or wider does not bind it to the field element: such a decomposition emits per-limb range checks plus a single recomposition equation that holds modulo `p`, so for any `x < 2^254 - p` both `x` and `x + p` satisfy it while yielding different slots — one nullifier, two slots, a double-spend. Conforming implementations MUST reduce to the canonical representative before extracting the index. Stated normatively in SDD `sdd_pay_pool.md` §3.4.1. **This property cannot be demonstrated by test vectors**: an honest prover always emits the canonical decomposition, so sound and unsound implementations reproduce every published vector identically; conformance requires inspecting the constraint system. Clarifies existing intent — no change to nullifier semantics, circuit identifiers, public-input shapes, hashes, `zka:bundle:v1`, or any published test vector. - **Withdrawal recipient bound (circuit-layer).** `zka_pay_withdrawal` now constrains `external_recipient` to be non-zero and less than `2^160`. A wider value has no valid L1 encoding, because a Solidity `address` occupies only the low 20 of the 32 bytes the recipient is right-aligned into; the circuit states the bound itself rather than inheriting it from a settling contract's parameter type. Enforced by the `zka_pay_withdrawal` circuit and stated normatively in SDD §3.4. The `zka_pay_withdrawal` public-input encoder schema is deliberately unchanged: those schemas are frozen, hash-pinned artifacts (the SDK's closed-pool encoder binding compares a sha256 of the raw file), so they carry the wire shape, not conformance prose. This **narrows the accepted witness set** of one circuit; it changes no public-input shape, ordering, field encoding, hash, or envelope, and no published test vector moves. **Version 0.8.0-draft Changes:** - **ZKA/Pay native value engine.** The immutable Base WETH9 single-asset pool profile (§3.4) is the flagship production direction for ADAMAS APL 0.4. AFP/application code prices obligations in USD and signs the exact WETH wei settlement amount; ZKA/Pay performs no repricing and adds no oracle or stablecoin authority. Additional assets require separate immutable pool deployments and explicit asset-risk profiles. A native stable unit is a separate future protocol, not a ZKA/Pay prerequisite. - **§7.4 external-host status clarified.** External shielded pools such as Railgun remain an optional settlement-independence deployment mode, but are not the resolved ADAMAS flagship architecture and do not execute ZKA/Pay circuits. This supersedes only the ADAMAS deployment conclusion recorded in v0.7.1; the optional delegation mechanism remains conforming. - No change to existing circuit identifiers, public-input shapes, hashes, `zka:bundle:v1`, or published test vectors. **Version 0.7.1-draft Changes:** - **§8.6 (new) — Relationship to Aztec Primitives.** Documents, non-normatively, the boundary between what the Aztec-native deployment **delegates** to Aztec's primitives (note hash tree, indexed nullifier tree, kernel-circuit recursion, key hierarchy, and the shared Honk/BN254/Poseidon2 substrate) and what ZKA/ZKC **adds** with no Aztec-native equivalent (ZKC compliance/attestation §5, Freedom Safeguards §1.6, cross-environment note compatibility §8.5, settlement-rail independence). Clarifies that §8.5 cross-environment compatibility is a circuit/field/hash-level contract, not a claim that one proof object verifies in both environments. Descriptive only — no change to notes, bundles, circuits, fields, hashes, the `zka:bundle:v1` envelope, or any test vector. - **ZKC v0.3.0 co-publication harmonization.** Cross-references retargeted from ZKC v0.2.4 to the co-published ZKC v0.3.0 (§5.2–§5.4 integration cites, §12.2 conformance, Appendix C.1); Appendix C.1 reconciliation updated to note bilateral Safeguard-6 alignment (ZKA §1.6 ↔ ZKC §1.4) and the additive APR/Disclosure-Receipt/purpose-bound machinery. No ZKA circuit-layer, field, hash, envelope, or test-vector change. - **§7.4 (new) — Operator-free external shielded-pool settlement (deployment note).** Records that a conforming deployment MAY delegate settlement to an external, immutable, operator-free shielded pool (e.g. Railgun) holding a non-freezable asset (e.g. Liquity BOLD/LUSD) — the §8.6 delegation principle applied to a non-ZKA-native host: ZKA's §3 transfer circuits do not run, the host pool provides the shielded transfer, and ZKA's contribution is the additive ZKA/Coord + ZKC + AFP layer off-chain, with identity bound at the session/context level (§5.4). Reflects the resolved ADAMAS settlement architecture (APL ADR-0011/0012). Deployment-layer guidance; no change to notes, bundles, circuits, fields, hashes, the `zka:bundle:v1` envelope, or any test vector. **Version 0.7.0 Changes:** - **§1.6 Safeguard 6 — Verifier Accountability (new normative invariant).** Added a sixth Freedom Safeguard as a protocol invariant and audit-table row. A verifier that requests a disclosure MUST authenticate the request under a stable verifier identity (an Authenticated Proof Request), the holder MUST be able to retain a self-contained, holder-custodied receipt of what was demanded, for what declared purpose, and what was answered, and a conforming SDK MUST refuse by default to answer an unauthenticated request and MUST NOT require any operator or service to custody receipts. This is the **first safeguard that constrains the counterparty** rather than the protocol or operator; it is orthogonal to operator-blindness (a blind operator and an unaccountable verifier are independent failures), so it is a new number, not an extension of Safeguard 2. It governs the *request*, and makes no claim over the verifier's *decision*, which the protocol cannot reach. The mechanism is specified normatively on the ZKC side (ZKC §2.2 purpose-bound context, §5.5 Authenticated Proof Request, §5.6 Disclosure Receipt, §5.7–§5.8). - No change to notes, bundles, circuits, fields, hashes, the `zka:bundle:v1` envelope, or any test vector. Safeguard 6 binds the ZKC request/receipt layer carried over ZKA; the ZKA settlement and coordination circuits are untouched. **Version 0.6.4-draft Changes:** - **§7.1 Standalone force-exit normatively specified.** The six conforming-bridge invariants of v0.6.3 are expanded into full normative subsections: §7.1.1 enumerates the complete bridge-held state (canonical append log, accepted root set + recency window per §2.4, nullifier set, immutable verifier references, per-asset escrow balances, deployment-binding context); §7.1.2 defines the force-exit path as the ordinary `zka_withdrawal` proof (§3.2) with a fixed verification order and checks-effects-interactions nullifier recording, and states normatively that pure shielded-pool exits have **no challenge period**; §7.1.3 defines the root publication model (append events + root-update events; clients and indexers reconstruct identical state from public L1 history); §7.1.4 defines a machine-readable failure-code taxonomy with retryable/terminal classification (stale root is retryable and MUST NOT invalidate a note); §7.1.5 enumerates the client-retained witness data sufficient to exit with every operator service unavailable; §7.1.6 states that the Aztec Outbox/portal settlement path (ADR-0002, §8) is an explicitly separate mechanism. - **ZKC v0.2.4 alignment.** §5.7 now treats credential rebinding as a ZKC-Core circuit requirement (`zkc:proof:credential_rebind:v1`) rather than an unspecified draft mechanism. Issuer-specific migration tokens are optional integration optimizations and do not replace the ZKC-core circuit; ZKA retains only the settlement/privacy obligations around self-transfer batching and sealed/minimized issuer audit mappings. - **Namada/Penumbra bridge implementation boundary aligned with the completed Rust/SDK work.** §7.2–§7.3 now name the active Noir packages, retain the existing circuit identifiers, and describe the Rust bridge crates plus SDK adapters as implementation boundaries around Noir/UltraHonk/BN254 proof artifacts. The Penumbra Rust crate is explicitly the fresh integration boundary, not the removed legacy Halo2/Pallas implementation. - No ZKA circuit-layer, field, hash, or test-vector change. The `zka_withdrawal` (§3.2), `zka_root_update` (§3.3), identity-binding (§5.2), combined transfer+binding (§5.3), and Namada/Penumbra bridge circuits (§7.2–§7.3) keep their public-input shapes; the new credential rebind circuit is ZKC-owned and referenced by identifier. - **Aleo/Leo target retired (ADR-0009, 2026-06-12).** The Leo conforming-target language (§1.4), the `"aleo"` settlement type and asset-id variant (§2.1), the §4.1 Leo/Aleo coordination carve-out, and the Aleo references in §1.6/§5.7/§12.1 are removed; the Aleo design documents are archived under `docs/archive/`. No Aleo value flow is expected, so the Leo programs, the Aleo finality oracle, and the cross-environment claim anchor were removed from the implementation tree. No change to any Noir circuit, field, hash, or test vector. **Version 0.6.2-draft Changes:** - §2.4 Merkle Tree State Management: defines the canonical append source, deterministic leaf ordering, root-update events, indexer/client reconstruction rules, and deployment-profile stale-root acceptance windows. Balance servers and indexers are mirrors, never Merkle authorities. - §5.7 Note Discovery Protocol: replaces the prior Draft placeholder with a normative append-only note announcement log. Deposits and transfers that create notes MUST publish encrypted note announcements; recipients discover notes by scanning with their incoming viewing key; indexers MAY serve the log but MUST NOT learn recipient linkage. - §1.6 / §5.7 Agent Key Custody: adds a protocol-level non-custodial invariant. A conforming ZKA deployment MUST NOT require an operator, proxy, indexer, prover, or compliance service to receive `masterSeed`, spending keys, or note plaintext. Custody product profiles remain outside ZKA and are owned by deployment specs such as APL. - §5.4 Compliance Bundle: added an **OPTIONAL `coord_proof` member** to `zka:bundle:v1`, carrying a recursive **ZKA/Coord** work-attestation proof (§4) plus its public inputs (`workflowId`, `inputCommitments`, `outputCommitment`, `stepIndex`, `context`) alongside the existing transaction + compliance + binding proofs. The field is optional, so existing payment/compliance bundles are unaffected and the envelope version string stays `zka:bundle:v1` (additive, backward-compatible). - §5.4 Compliance Bundle: the payment **`transaction` member is now OPTIONAL** (with `binding` optional and present IFF `transaction` is). A bundle MUST carry **at least one of `{transaction, coord_proof}`** (an `EMPTY_BUNDLE` is invalid); `compliance` proofs only ever accompany a substantive proof. This admits a **pure work-attestation bundle** (`coord_proof`, no payment) for the non-monetary federation flows generalized in **AFP v0.3.1 §12.7 / ADR-0003** — **PRP reconciliation** (proving a Merkle root) and the non-monetary **VXP `vxp.reciprocal`** (data-for-data) — which carry evidence over `zka:bundle:v1` with no payment leg. The note↔credential binding is N/A when `transaction` is absent; identity is then anchored over whatever proofs are present via the shared bundle `context`, which AFP equates to the envelope's `session_context` (no new crypto). - `verifyComplianceBundle` extended: verify `coord_proof` recursively (per §4.2) when present and bind its `context` to the bundle context; verify each member IFF present; gate the transaction-dependent consistency checks on `transaction` presence; add the substantiveness gate. - This satisfies the ZKA-side follow-up flagged in **AFP v0.3.1 §12.7** (ADR-0003) and **supersedes** VXP's `vxp:coordbundle:v1`: AFP's `afp:envelope:v1` now wraps a single ZKA-owned bundle that already carries the Coord proof and supports payment-free attestation. - No circuit-layer, field, hash, or test-vector change; the ZKA/Coord circuit (§4) and its public-input shape are unchanged — only the bundle envelope is relaxed (optional carriage slot + optional payment leg). **Version 0.6.1 Changes:** - Key derivation (master seed, Poseidon2/BN254 domain separation, key-hierarchy derivation) is no longer defined inline; it is now specified by the **Key Derivation Core (KDC v1.0.0)**, AFP Specification Appendix A, and referenced normatively from §2.1–§2.2. This is a relocation, not a redefinition: KDC v1.0.0 is behaviorally identical to ZKA v0.6.0 §2.2 and the existing `zka-test-vectors` seed→key vectors remain normative and unchanged. - §1.3 records the ZKA→AFP-KDC dependency; Appendix C.1 records the KDC notation reference. - No circuit-layer, field, hash, or test-vector change. The §2.2 Aztec key-mapping table is retained in ZKA. **Version 0.6.0 Changes:** - ZKA/ZKC integration promoted from a deferral stub to a **normative section** (§5), folding in `docs/archive/zka_zkc_integration_v1_0_0.md` ported from Halo 2 to Noir/UltraHonk/BN254 - Added **§1.6 Freedom Safeguards as Protocol Invariants** (Predicate Pluralism, Minimal Disclosure, No Revocability, Open-Source Predicates, Agent Exit Rights) - Added Identity Binding Circuit (full constraint set), Compliance Bundle format and `verifyComplianceBundle`, and the Combined Transfer + Binding circuit - Added **§5.6 Credential Lifecycle** (issuer bootstrapping, revocation, accumulator witness distribution) - Added **§5.7 Structural Requirements** (Note Discovery, Key Rotation / orphaned `cbk` recovery, Delegated Proof Generation) — drawn from the ZKA/ZKC/APL investigation-priorities analysis (tracking file since retired; requirements now normative in §5.7) - Added selective-confidentiality framing to the Abstract and §1.1 - Added post-quantum migration considerations (§11.6) and a CLARITY-Act self-custody alignment statement (§1.6, Safeguard 5) - Reconciled with the authoritative **ZKC Specification v0.2.2** (2026-05-15, targets this revision): bundle envelope scoped to wrap (not redefine) ZKC `zkc:proof:v1` proofs; ZKC conformance taxonomy referenced rather than re-declared; interop parameters (`MAX_EPOCH_LAG`, `claimBinding`, issuer-signature locus) cited. The three ZKC-layer divergences flagged in earlier drafts (canonical proof-type registry, Safeguard 4 mandate, conformance hierarchy) are **resolved in ZKC v0.2.2**; see Appendix C.1 - Circuit layer unchanged from v0.5.0 (Noir / UltraHonk / BN254 / Poseidon2); no test-vector regeneration required for v0.5.0 circuits --- ## 1. Overview ### 1.1 Design Goals 1. **Payment Privacy**: Transfer amounts and party identities are hidden from observers 2. **Double-Spend Prevention**: Nullifier-based consumption tracking prevents replay 3. **Verifiable Coordination**: Recursive proofs attest to multi-agent workflow execution 4. **Settlement Agnosticism**: Protocol works with any settlement layer (L2, L1, centralized) 5. **Selective Disclosure**: Viewing keys and predicate proofs enable audits without compromising privacy 6. **Provable Confidentiality**: A holder proves chosen properties to a chosen counterparty; non-compliance reduces counterparty access, never fund access 7. **Compliance Compatibility**: Normative integration with ZKC for user-controlled, pluralistic compliance 8. **Multi-Chain Deployment**: Aztec native, standalone EVM, and hybrid deployment models 9. **Freedom by Default**: The protocol invariants of §1.6 hold regardless of proving backend, settlement layer, or deployment context ### 1.2 Protocol Family | Protocol | Purpose | Status | |----------|---------|--------| | **ZKA/Pay** | Privacy-preserving payments | Specified | | **ZKA/Coord** | Recursive coordination proofs | Specified | | **ZKA/Attest** | Anonymous credentials / ZKC binding | Specified (§5) | | **ZKA/Reputation** | Privacy-preserving reputation | Draft | | **ZKA/Compute** | Verifiable private computation | Future | ### 1.3 Related Protocols | Protocol | Relationship | |----------|--------------| | **ZKC** | User-controlled compliance layer; shares the key hierarchy with ZKA; integration is normative as of §5 | | **ZKM** | Principal-issued spend-authorization layer. ZKM reuses ZKA note, nullifier, and settlement primitives in its spend-conjoined statement, but remains optional: ordinary ZKA transfers do not require a mandate. ZKA alone owns note validity, custody, settlement, and unconditional exit; revoking or rejecting a ZKM mandate MUST NOT invalidate, freeze, redirect, or seize a ZKA note. | | **AFP** | Agent Federation Protocol; uses ZKA/Coord for cross-org workflow attestation. AFP also **owns the Key Derivation Core** (AFP-KDC v1.0.2, AFP Appendix A) that this specification references for its key hierarchy (§2.1–§2.2). The dependency is acyclic: ZKA depends only on AFP Appendix A, a self-contained module that depends on nothing else in AFP. | ### 1.4 Circuit Implementation ZKA circuits are specified in **Noir** (ACIR IR) and proven using the **Barretenberg** backend (UltraHonk proving system) over the **BN254** scalar field. This enables: - EVM-native Solidity verifiers auto-generated via `nargo codegen-verifier` - Browser-based proving via NoirJS + Barretenberg WASM - Aztec native deployment via `aztec.nr` private functions - Pluggable backend: Barretenberg today, additional backends as Noir's ecosystem matures > **Retired target (2026-06-12).** Earlier drafts named **Leo** (`zka.leo` / `zkc.leo`, Aleo deployment) as a second conforming implementation target with invariant-level (not value- or proof-level) conformance. The Aleo-native execution path was retired per ADR-0009 (no Aleo value flow expected); the design documents are archived under [`docs/archive/`](archive/) (`sdd_leo_pay.md`, `sdd_bridge_aleo.md`, `sdd_coord_aleo.md`). The protocol invariants of §1.6 remain implementation-independent and MUST hold for any future conforming target. ### 1.5 Notation | Symbol | Meaning | |--------|---------| | `H_n(x...)` | Canonical Poseidon2 sponge hash of exactly `n` BN254 field elements; the message length is bound into the sponge IV | | `H(domain, x...)` | `H_n(deriveFieldTag(domain), x...)`, where `n` includes the leading domain field and `deriveFieldTag` is AFP-KDC v1.0.2 §A.3 | | `commit(asset, v, recipient, r)` | `H_2(H_3(asset, v, recipient), r)` note commitment | | `nullifier(n, k)` | Nullifier derived from note `n` and key `k` | | `π` | UltraHonk proof (Barretenberg) | | `vk` | Verification key | | `cbk` | Credential binding key (for ZKC integration) | | `holderCommitment` | `H("zkc/binding", cbk)` — public ZKC identity anchor | | `predicate` | An open-source compliance circuit verifying one property | ### 1.6 Freedom Safeguards as Protocol Invariants These six invariants are **normative**. A conforming implementation MUST satisfy all six; they are not optional features but the architectural commitments that distinguish privacy infrastructure from a compliance control mechanism. Safeguards 3 and 5 are core ZKA invariants; Safeguards 1, 2, and 4 are integration-level invariants binding any ZKC predicate or bundle used with ZKA (§5); Safeguard 6 (added in v0.7.0) is a verifier-accountability invariant that constrains the *counterparty* — the first safeguard that does — and is orthogonal to the operator-blindness of Safeguards 3 and 5. **Safeguard 1 — Predicate Pluralism.** ZKA/ZKC MUST support multiple competing predicate providers simultaneously. No predicate provider may be hardcoded, privileged, or treated as canonical by the protocol, and the protocol MUST NOT include a mechanism for designating an "official" or "default" predicate set. Predicate providers are independent, permissionless services. **Safeguard 2 — Minimal Disclosure by Default.** Predicate circuits MUST be designed for atomic, single-predicate proofs (e.g., "not sanctioned"), never bundled composites. Composite proofs MUST require explicit, separate consent per predicate. SDK and tooling defaults MUST generate minimal proofs; broader disclosure MUST require explicit additional configuration. Proof requests MUST be principal-readable, enumerating exactly what is proved. **Safeguard 3 — No Revocability (core ZKA invariant).** ZKA note validity MUST depend solely on cryptographic properties — valid nullifier, valid commitment, valid Merkle path — never on an external authorization check. No admin, governance, emergency, or regulatory key may exist that can invalidate, freeze, redirect, or force-transfer a note. Smart-contract implementations MUST NOT include pausable, upgradeable, or admin-controlled functions that could affect note validity. The only consequence of non-compliance is counterparty refusal, never fund inaccessibility. **Safeguard 4 — Open-Source Predicate Verification.** All predicate circuits MUST be open-source and publicly auditable, expressed in inspectable Noir code. If an agent cannot inspect the predicate it is asked to satisfy, the SDK MUST refuse to generate the proof by default. Predicate updates MUST be versioned, published, and subject to a public review period before activation. **Safeguard 5 — Agent Exit Rights (core ZKA invariant).** Unshielding/withdrawal from ZKA to a base layer (e.g. Ethereum) MUST be unconditional — no compliance proof may gate exit. The protocol MUST distinguish *custody operations* (always permissionless) from *interaction operations* (may require compliance proofs for specific counterparties). This aligns with and depends on the CLARITY Act self-custody protection: a federal agency may not impair the ability of a covered user to self-custody digital assets using a self-hosted wallet. **Safeguard 6 — Verifier Accountability.** A verifier that requests a disclosure MUST authenticate the request under a stable verifier identity (an Authenticated Proof Request), and the holder MUST be able to retain a self-contained receipt of exactly what was demanded, for what declared purpose, and what was answered. A conforming SDK MUST refuse by default to answer an unauthenticated request, MUST bind declared purpose into the proof context, and MUST NOT require any operator or service to custody receipts. This safeguard governs the *request*; it makes no claim over the verifier's *decision*, which the protocol cannot reach (request accountability is reachable — no signature ⇒ no proof — but a verifier may decline in silence, so decision accountability is structurally unreachable). Safeguard 6 is the first invariant to constrain the *counterparty* rather than the operator; it is orthogonal to operator-blindness and reinforces Safeguard 2 (the signature is over the itemized request) while complementing Safeguard 5 (exit rights say you may leave; the receipt says why you were pushed). The mechanism is specified normatively in **ZKC §2.2, §5.5–§5.8** and is enforced for any ZKC predicate or bundle used with ZKA. A conforming ZKA deployment MUST NOT require an operator, proxy, indexer, prover, predicate provider, compliance service, or settlement administrator to receive `masterSeed`, spending keys, note plaintext, or custody of Disclosure Receipts in order for an agent to hold, receive, spend, or withdraw ZKA notes. Remote services MAY assist with indexing, verification, proving, or signing only when they receive public inputs, encrypted material, viewing-limited material, or delegated authority that is insufficient for unilateral spending. | # | Invariant | Audit Check | |---|-----------|-------------| | 1 | Predicate Pluralism | No hardcoded predicate-provider addresses or privileged roles in contract code | | 2 | Minimal Disclosure | SDK defaults emit single-predicate proofs; composites require explicit opt-in | | 3 | No Revocability | No admin/pausable/force-transfer paths in note-management contracts | | 4 | Open-Source Predicates | Predicate inspection mandatory before proof generation in SDK flow | | 5 | Exit Rights | Unshield/withdraw functions contain no compliance-gating conditional logic; operators and services cannot require custody of `masterSeed`, spending keys, or note plaintext | | 6 | Verifier Accountability | SDK refuses unauthenticated requests by default; declared purpose is a bound public input under the purpose-bound proof context; receipts are holder-custodied and operator retention is absent | --- ## 2. Cryptographic Primitives ### 2.1 Hash Function ZKA uses **Poseidon2** over the BN254 scalar field for all circuit-facing hash operations. The canonical implementation is `noir-lang/poseidon` v0.3.0's `Poseidon2::hash`, backed by Noir/Aztec/Barretenberg's Poseidon2 permutation and optimised for UltraHonk constraints. Its state width is **4**: three rate lanes and one capacity lane. `H_2`, `H_3`, and `H_4` below name the number of message fields supplied to this same width-4 sponge; they do not select width-2, width-3, or width-4 permutations. For a message of `n` field elements, the canonical sponge places `n * 2^64` in the capacity lane, absorbs up to three message fields per permutation, and returns state lane zero. The length-bearing IV makes the message arity part of the hash definition; for example, `H_1(x) != H_2(x, 0)`. Domain separation is construction-specific. A tagged construction prepends ``` deriveFieldTag(tag) = LEInteger(Blake2b-512("ZKA-Domain-" || UTF8(tag))) mod r ``` as specified by **AFP-KDC v1.0.2 §A.3**. Implementations MUST use the pinned field constants and MUST NOT substitute direct UTF-8-to-field encoding, big-endian digest interpretation, another Blake2 variant, or an unprefixed digest. Untagged constructions are explicitly identified below; the mere use of Poseidon2 does not imply a leading domain field. **Key-derivation hashing** — the derivation of the spending, viewing, proof, and credential-binding keys, the address, and the credential commitment, and the domain tags `zka/spending`, `zka/viewing`, `zka/proof`, `zka/address`, `zkc/credential`, `zkc/binding` — is **defined by AFP-KDC v1.0.2** (AFP Appendix A §A.4, §A.6) and is not restated here. See §2.2. **Operational (non-derivation) hashing** remains defined by this specification. ZKA defines one operational domain tag: ``` H_2(H_2(DOMAIN_ZKA_NULLIFIER, commitment), sk) → nullifier ``` `DOMAIN_ZKA_NULLIFIER = deriveFieldTag("zka/nullifier")`. Per the AFP-KDC tag-collision rule (§A.4), it does not collide with a registered KDC or sibling-protocol tag and remains owned by ZKA. The two-layer note commitment and binary Merkle parent in §2.3–§2.4 are deliberately untagged, fixed-arity constructions pinned by the protocol vectors. > **Migration note**: v0.4.0 used Poseidon over the Pallas scalar field; v0.5.0 onward uses Poseidon2 over BN254. Domain-tag strings are unchanged across all versions; only the field and hash variant differ. v0.6.1 introduces no field, hash, or derivation change relative to v0.5.0/v0.6.0 — it only relocates the derivation definition to KDC. ### 2.2 Key Hierarchy ZKA key derivation is defined by the **Key Derivation Core (AFP-KDC v1.0.2)**, specified in **Appendix A of the AFP Specification**. ZKA does not restate the derivation; a conforming ZKA implementation MUST derive keys per AFP-KDC v1.0.2. The 32-byte seed is interpreted as an unsigned **big-endian** integer reduced modulo BN254.Fr before any derivation hash. KDC takes a 256-bit master seed and derives the spending key (`sk`), viewing key (`vk`), proof key (`pk`), address (`addr`), credential binding key (`cbk`), and credential commitment (`holderCommitment`). Two KDC derivation facts are load-bearing for this specification (KDC §A.6): - `cbk = H("zkc/credential", seed)` derives from the **seed**, not from `sk` — it is a sibling of the spending key, not a descendant. This is what allows credential ownership to be proven without exposing spending authority. - The ZKA `address` and the ZKC `holderCommitment` derive along **separate paths** from the same seed and are unlinkable without it. The §5.2 identity-binding circuit proves the link in zero knowledge precisely because the link is otherwise unrecoverable. **Seed provenance.** The 256-bit seed supplied to KDC is, in a standalone ZKA deployment, a master seed generated directly. Under AFP entity identity (AFP §4.3), it is a **per-entity sub-seed** `M'` derived from the tenant master seed via KDC §A.5. KDC key derivation does not distinguish the two cases; ZKA circuits that take a `masterSeed` private input (e.g. §5.2) accept whichever the deployment uses. > Note: the `KeyHierarchy` interface and `deriveKeys` function previously shown here are now KDC §A.6. The ASCII key-hierarchy diagram is now KDC §A.6. The unlinkability paragraph is now KDC §A.6, fact 2. **Aztec mapping** (for Aztec native deployment, §8): | ZKA Key | Aztec Equivalent | |---------|-----------------| | `masterSeed` | App-siloed nullifier secret `nsk_app` | | `spendingKey` | `H("zka/spending", nsk_app)` | | `viewingKey` | `H("zka/viewing", spendingKey)` | | `credentialBindingKey` | `H("zkc/credential", nsk_app)` — sibling of `spendingKey` | ### 2.3 Notes ```typescript interface Note { asset: AssetDescriptor; // Multi-asset support value: u64; // Hidden in proofs recipient: ZKAAddress; randomness: Scalar; // BN254 scalar field element commitment: Commitment; // H_2(H_3(asset_commitment, value, recipient), randomness) nullifier: Nullifier; // Revealed when spent memo: Uint8Array; // Up to 512 bytes, encrypted } interface AssetDescriptor { settlement: SettlementType; assetId: AssetId; decimals: u8; metadata?: { symbol: string; name: string }; } type SettlementType = "ethereum" | "namada" | "penumbra" | "aztec" | "native"; type AssetId = | { type: "ethereum", address: Address } | { type: "namada", denom: string } | { type: "penumbra", assetId: Hash } | { type: "aztec", assetId: Hash } | { type: "native", id: Hash }; function computeCommitment(note: Note): Commitment { const assetCommitment = H_2(DOMAIN_ZKA_ASSET, canonicalAssetIdField(note.asset)); const inner = H_3(assetCommitment, field(note.value), note.recipient); return H_2(inner, note.randomness); } function computeNullifier(note: Note, sk: SpendingKey): Nullifier { return H_2(H_2(DOMAIN_ZKA_NULLIFIER, note.commitment), sk); } ``` `DOMAIN_ZKA_ASSET = deriveFieldTag("zka/asset")`. The settlement-specific canonical asset-id projection is defined by the applicable deployment or bridge profile. The inner and outer note-commitment hashes are untagged; their distinct fixed arities and nesting are normative. #### 2.3.1 Note randomness generation For every active output note, the holder implementation **MUST** generate `randomness` internally from an operating-system cryptographically secure random number generator. Sampling **MUST** cover the complete BN254 scalar field by rejection sampling canonical 32-byte representatives; modulo reduction of a narrow, biased, deterministic, user-supplied, or protocol-derived input is not conforming. Zero is never a valid active-output randomness value. Inactive fixed-width output slots continue to use zero. A production SDK, wallet, bridge adapter, or rotation path **MUST NOT** accept caller-supplied note randomness. It **MUST** reject any reuse it can detect before proof construction, including reuse from an input note and repetition between active outputs in one transaction. A holder implementation **MUST** retain a bounded non-reversible in-process fingerprint window for values it generates so a recent accidental repeated CSPRNG output fails closed without unbounded memory growth. Deterministic constructors MAY exist only in an explicitly non-production test-vector build and **MUST** reject zero. The transfer circuits enforce only witness-visible safety checks: each active output randomness is nonzero and differs from every active input and earlier active output randomness in the same transition. These constraints do **not** prove entropy, unpredictability, CSPRNG use, or uniqueness against notes absent from the witness; those remain generation-boundary obligations. Randomness, note openings, and their reuse fingerprints are private material. Errors, logs, events, debug formatting, and telemetry **MUST NOT** include them. Generation failures use a stable redacted error and fail closed. Preparation and submission APIs that span multiple calls **MUST** retain the exact opening in holder memory (or holder-controlled encrypted storage); they MUST NOT regenerate an opening after publishing its commitment. A post-send transport, receipt, or event-decoding failure **MUST** return recoverable holder state containing that exact opening and any known transaction hash rather than a bare error that discards them. Settlement adapters **MUST** reconcile or resubmit only the exact retained transaction; they MUST NOT rebuild it with fresh note randomness. Confirmation requires a matching canonical settlement event for the retained note commitment (and transaction hash when known). ### 2.4 Merkle Tree ```typescript interface MerkleTree { depth: 32; // Supports 2^32 notes hashFunction: "Poseidon2"; // BN254 field root: Hash; leaves: Commitment[]; nextIndex: u32; } ``` Merkle path verification uses the untagged binary parent `H_2(left, right)` over the depth-32 path (`zka_lib::compute_merkle_root`). Path-index bits are consumed least-significant-bit first across tree levels. The empty leaf is zero and the depth-32 empty root is obtained by 32 applications of `H_2(empty, empty)`. The former `std::merkle::compute_merkle_root` stdlib helper was Pedersen-based and was removed from the Noir stdlib in nargo 1.0.0-beta.4; ZKA computes the root with Poseidon2 for consistency with all other hashing. The canonical append source for a ZKA deployment MUST be the settlement contract, settlement validity predicate, or deployment-defined append log that authorizes note creation. Balance servers, wallets, mirrors, and indexers MAY maintain local Merkle trees, but they MUST be reconstructable from the canonical append source and MUST NOT be authoritative for leaf order or root validity. Every deposit, transfer, bridge import, or settlement action that creates a ZKA note MUST append that note commitment exactly once in deterministic settlement order. The leaf index is the zero-based append position in that canonical order. If multiple commitments are created by one settlement action, their intra-action order MUST be specified by the deployment profile and MUST be stable across all clients. Canonical append events MUST expose enough public or holder-authorized data for clients and indexers to reconstruct the tree: ```typescript interface CommitmentAppendEvent { commitment: Commitment; leafIndex: u32; previousRoot: Hash; newRoot: Hash; context: Hash; // deployment, chain, asset-domain, and circuit-domain binding } ``` A verifier, settlement contract, or validity predicate MUST accept Merkle roots only from a deployment-defined `acceptedRootSet`. The deployment profile MUST define stale-root handling as either: - the latest `N` canonical roots, where `N >= 1`; or - roots no older than a configured block-height, epoch, or wall-clock window. Roots outside the accepted window MUST be rejected with a machine-readable stale-root error. Rejection of a stale root MUST NOT invalidate the note; the holder can regenerate a Merkle path against an accepted root and resubmit. --- ## 3. ZKA/Pay: Private Transfers ### 3.1 Transfer Circuit (`zka_transfer`) **Circuit constraints (in Noir):** 1. Input note Merkle membership 2. Nullifier correctness: derived from commitment + spending key 3. Ownership: input note recipient matches derived address 4. Output commitment correctness 5. Value conservation: `input.value == output.value`, `output.value > 0` 6. Asset conservation: `input.asset == output.asset`, asset commitment matches 7. Context binding: proof bound to epoch/chain ```typescript interface TransferProof { publicInputs: { nullifier: Nullifier; outputCommitment: Commitment; merkleRoot: Hash; context: Hash; // H(epoch, chainId, applicationId) assetCommitment: Hash; // H(asset.assetId) }; privateInputs: { inputNote: Note; inputMerklePath: MerklePath; spendingKey: SpendingKey; outputNote: Note; }; proof: UltraHonkProof; } ``` ### 3.2 Withdrawal Circuit (`zka_withdrawal`) Consumes a note and **reveals** amount, asset, and external recipient. Used for bridge exits (ZKA shielded pool → external chain). Per Safeguard 5 (§1.6), the withdrawal circuit MUST NOT take any compliance predicate, credential, or binding proof as an input or constraint. Exit to self-custody is unconditional. ```typescript interface WithdrawalProof { publicInputs: { nullifier: Nullifier; merkleRoot: Hash; amount: u64; // Revealed asset: AssetDescriptor; // Revealed externalRecipient: Address; // Revealed context: Hash; }; proof: UltraHonkProof; } ``` ### 3.3 Root Update Circuit (`zka_root_update`) Used by deployments that anchor an append-only commitment tree on an external rollup or L1 contract. Proves that a claimed `newRoot` is the correct Poseidon2 incremental insertion of the public deposit list followed by a prover-supplied private blob-commitment list into a prior accepted `prevRoot`. The proof preserves the order of the supplied values, but by itself does not prove that the private list came from published blob data and therefore does not prevent omission or substitution of blob commitments. ```typescript interface RootUpdateProof { publicInputs: { prevRoot: Hash; newRoot: Hash; prevTotal: u64; // commitments folded so far newTotal: u64; // commitments folded after this update blobKzgCommitment: Hash; // legacy-named non-zero proof tag; not a blob binding depositCommitments: Hash[]; // ordered by on-chain event-log position }; privateInputs: { blobCommitments: Hash[]; // prover-supplied; not bound to blobKzgCommitment intermediatePaths: MerklePath[]; // one path per insertion, depth = MERKLE_DEPTH (32) }; proof: UltraHonkProof; } ``` **Circuit constraints (in Noir):** 1. Count consistency: `newTotal - prevTotal == depositCommitments.length + blobCommitments.length` 2. Deterministic interleave order: deposit commitments first (in event-log order), then blob commitments (in blob-payload order) 3. Incremental insertion: fold each commitment in order into the Merkle tree starting from `prevRoot` using `intermediatePaths`; the final fold MUST equal `newRoot` 4. Hash function: Poseidon2 over BN254 per §2.1 5. Tree depth: `MERKLE_DEPTH = 32` per §2.4 6. Legacy blob-tag check: `blobKzgCommitment` is a public input and MUST be non-zero; this binds only the exact tag value into the proof and establishes no KZG opening, blob preimage, or relation to the private `blobCommitments` Despite its historical name, `blobKzgCommitment` is not sufficient to bind the private values folded by this circuit. The immutable Base APLBridge passes a caller-supplied value directly into this public-input slot and never executes `BLOBHASH`; the legacy circuit independently accepts any non-zero value. Consequently, that deployment and circuit establish no blob data-availability or blob-to-leaf binding property. A successor profile claiming EIP-4844 data availability MUST (1) obtain the blob versioned hash with `BLOBHASH` in the same blob-carrying transaction and compare it with a proof-bound public input, and (2) verify a cryptographic relation showing that the ordered commitment projection decoded from the blob's canonical announcement log equals the exact private commitments folded into `newRoot`. A `BLOBHASH` comparison alone binds the transaction to a blob, not the circuit's private witness values to that blob. ### 3.4 Immutable Base WETH9 Pool Profile ZKA/Pay defines an additive, immutable, single-asset settlement profile for Base WETH9. Its four circuits are: | Circuit | Purpose | |---|---| | `zka_pay_deposit` | Binds a queued note commitment to the exact public WETH deposit amount and immutable pool/asset context; the contract stores the exact announcement-hash limbs with the queue entry. | | `zka_pay_transition` | Proves a private payment with up to two inputs and three ordered outputs (recipient, change, fee). | | `zka_pay_batch` | Recursively verifies payments, consumes the mandatory deposit prefix, advances both roots, and binds canonical batch calldata. | | `zka_pay_withdrawal` | Consumes a note, advances the sparse nullifier root, and releases the proof-bound amount to the proof-bound Base recipient. | Each accepted deposit appends the immutable tuple `(queue index, note commitment, amount, full announcement Keccak-256 high/low u128 limbs, depositor proof hash)`. A batch computes `deposit_prefix_digest` from that stored queue range. It separately computes `batch_data_digest` from strict- parsed canonical calldata, recomputes every deposit announcement hash, and requires the calldata commitment, amount, and both hash limbs to match the immutable queue entry before verification or state advancement. The batch circuit constrains the same five deposit fields into both digest streams. The pool context is contract-derived, never a deployer-selected constructor value. It uses the exact UTF-8 tag `zka/pay/pool-context/v1` and the field-count-prefixed BN254 Poseidon2 fold over profile version, Base chain id, pool and asset addresses, asset commitment, full Keccak-256 limbs of all four circuit ids, and every frozen pool bound/depth. The complete ordered 22-field stream is normative in the pool SDD. Changing any field requires a new pool. The complete identifiers, flattened public-input order, fixed bounds, tree depths, canonical `batchData` encoding, digest folds, and deterministic error taxonomy are normative in [the ZKA/Pay pool SDD](sdd/sdd_pay_pool.md). The machine-readable public-input encoders are `schemas/public-inputs/zka_pay_*.json`. This profile supersedes neither the existing bridge circuits (§7) nor the existing ZKA note (§2.3). It uses the existing note-commitment semantics in a separate pool state domain with separate commitment/nullifier trees, escrow, verifiers, and accepted-root history. --- ## 4. ZKA/Coord v2: Profile-Bound Recursive Execution ZKA/Coord v2 uses a fixed, 30-field execution receipt and bounded recursive DAG composition. The normative ABI, domains, profile-manifest contract, workflow-definition encoding, base and recursive cases, trust boundaries, failure codes, migration rules, and graduation gate are defined in [`docs/sdd/sdd_coord_v2.md`](sdd/sdd_coord_v2.md). The machine-readable ABI is [`schemas/public-inputs/zka_coord_v2.json`](../schemas/public-inputs/zka_coord_v2.json). A final proof attests that every accepted step was proven by an execution profile in a verifier-selected policy, every active predecessor proof was cryptographically verified, and ordered predecessor outputs exactly equal the successor inputs. Workflow definition, instance, context, initial state, topology, profile artifacts, verification keys, assurance class, and inactive padding are all bound. The three assurance classes are intentionally distinct: ZK-native execution, proof-producing external execution, and attested execution. The latter proves correct checking of a named trust anchor, not physical-world truth. ZKA defines no global profile allowlist. Because ZKA/Coord had not launched, v2 is the canonical pre-launch draft rather than a compatibility revision. It remains unaudited and not production-supported until the signed independent-review gate in `docs/security/coord_v2_audit_gate.md` passes. The pre-launch four-field base circuit, three-field aggregate, and five-field bundle object are removed from active schemas, artifacts, registries, and SDK decoders. A conforming verifier MUST reject them; there is no legacy alias, width-based interpretation, or fallback verification key. The public Rust API is canonical and unversioned (`CoordWorkflowBuilder`, `CoordProver`, `CoordVerifier`). Version markers remain mandatory in the cryptographic identities that prevent cross-protocol substitution, including the receipt/schema identifiers, circuit and artifact IDs, policy documents, manifest formats, and hash domains. ```typescript interface CoordFinalPublicInputsV2 { receipt: CoordReceiptV2; // exact 30-field order from the normative SDD/schema graphCommitment: Field; proofNodeCount: u32; // 1..=4096 maxDepth: u32; // 1..=32 } interface CoordVerifierPolicyV2 { policyDocument: CoordVerifierPolicyDocumentV2; verifierPolicyRoot: Field; // MUST equal recomputed policyDocument root acceptedAssuranceClasses: Array<1 | 2 | 3>; workflowDefinition: CoordWorkflowDefinitionV2; expectedWorkflowInstanceId?: Field; expectedContext?: Field; acceptedProfileManifestHashes: Field[]; } ``` Serializers flatten `receipt` first and then the three aggregate fields, for a fixed total of 33 public field elements. Proof size is backend-bounded, but proving time and memory depend on active fan-in and recursion depth. Implementations MUST publish measured bounds and MUST NOT claim workflow-length-independent proving cost. An Aztec deployment is conforming only through an explicit, manifest-bound profile whose claim is tested independently. Kernel composition alone does not silently inherit the Coord v2 claim. The reference SDK publishes the off-chain UltraHonk path. An Aztec target additionally requires an independently registered profile and target verifier; no built-in Aztec Coord contract is provided. An EVM request fails with `UnsupportedTarget` until an exact EVM artifact and verifier are published, and MUST NOT fall back to an off-chain VK. --- ## 5. ZKA/Attest: ZKC Credential Integration (Normative) This section folds the ZKA-ZKC Integration Specification (`docs/archive/zka_zkc_integration_v1_0_0.md`, originally Halo 2) into the ZKA protocol layer, ported to Noir / UltraHonk / BN254 / Poseidon2. The constraint logic is field- and proof-system-independent; only the proving backend and performance parameters change relative to the source document. ### 5.1 Identity Binding Model The credential binding key links ZKA and ZKC identities without revealing either. ZKC v0.4 defines three presentation profiles. Legacy `linkable` v1 exposes the stable `holderCommitment`; v2 `scoped` exposes only `H("zkc/presentation-scope:v1", cbk, scopeId)`; v2 `anonymous` exposes neither handle (both presentation public inputs are zero). A scoped pseudonym is unlinkable across distinct scopes unless the principal discloses the link. ``` IDENTITY BINDING ================ ZKA Identity ZKC Identity ------------ ------------ address holderCommitment | | v v H("zka/address", vk) H("zkc/binding", cbk) | | +--------------+---------------+ | v masterSeed (private witness only) ``` ### 5.2 Identity Binding Circuits (`zka_credential`, `zka_transfer_binding_v2`) Proves that a ZKC credential belongs to the same identity as a ZKA note, without revealing the master seed. ``` IDENTITY BINDING CIRCUIT ======================== Public Inputs: - noteCommitment: Commitment - holderCommitment: Hash - merkleRoot: Hash - context: Hash Private Inputs: - masterSeed: Uint8Array[32] - note: Note - merklePath: MerklePath Constraints: 1. KEY DERIVATION (per AFP-KDC v1.0.2 §A.6 — AFP Appendix A) sk = H("zka/spending", masterSeed) vk = H("zka/viewing", sk) addr = H("zka/address", vk) cbk = H("zkc/credential", masterSeed) // masterSeed is the per-entity sub-seed M' under AFP entity identity 2. NOTE OWNERSHIP note.recipient == addr note.commitment == noteCommitment verifyMembership(note.commitment, merklePath, merkleRoot) 3. CREDENTIAL BINDING H("zkc/binding", cbk) == holderCommitment 4. CONTEXT BINDING // context bound into the proof; prevents replay to a different // session/counterparty (context includes a session id) ``` ```typescript interface IdentityBindingProof { publicInputs: { noteCommitment: Commitment; holderCommitment: Hash; merkleRoot: Hash; context: Hash; }; privateInputs: { masterSeed: Uint8Array; // 32 bytes — proves ownership of both note: Note; merklePath: MerklePath; }; proof: UltraHonkProof; } ``` | Parameter | Halo 2 (v1.0 source) | UltraHonk / BN254 (this spec) | |-----------|----------------------|-------------------------------| | Proving system | Halo 2 | UltraHonk (Barretenberg) | | Circuit size | ~2^16 rows | ~2^15 UltraHonk gates | | Proof size | ~5.5 KB | ~2 KB | | Verification | ~12 ms | <50 ms (native), <100 ms (browser) | | Solidity gas | n/a | <300k gas | The four-public-input `zka_credential` circuit above and the five-public-input `zka_transfer_binding` circuit in §5.3 are the frozen **linkable v1** profile. They remain accepted for backward compatibility. For ZKC v0.4 v2 payments, `zka_transfer_binding_v2` is normative. It privately derives `sk = H("zka/spending", masterSeed)` and `cbk = H("zkc/credential", masterSeed)`, proves that `sk` owns the input note, and constrains `presentationPseudonym = H("zkc/presentation-scope:v1", cbk, scopeId)`. Its public inputs are `(nullifier, outputCommitment, merkleRoot, context, presentationPseudonym, scopeId)`. `scopeId` and `presentationPseudonym` MUST both be non-zero. Neither `masterSeed`, `cbk`, nor `holderCommitment` is public. An anonymous v2 presentation cannot bind a payment note to a credential subject and MUST be rejected for a payment bundle; it remains valid for a payment-free bundle whose proofs share the AFP/session context. **Issuer-signature verification is NOT duplicated here**: a ZKA binding proof attests only seed-linkage and note ownership; valid issuance remains in ZKC's per-credential circuits carried by `compliance.proofs[]`. `context` MUST bind the relevant replay-protection context and equals the AFP `session_context` when carried by `afp:envelope:v1`. ### 5.3 Combined Transfer + Binding Circuit For efficiency, a combined circuit proves transfer validity and compliance binding in a single proof. `zka_transfer_binding` is the frozen linkable-v1 circuit: ``` COMBINED TRANSFER + BINDING CIRCUIT =================================== Public Inputs: - nullifier: Nullifier - outputCommitment: Commitment - merkleRoot: Hash - context: Hash - holderCommitment: Hash // for compliance binding Private Inputs: - masterSeed: Uint8Array[32] - inputNote: Note - inputMerklePath: MerklePath - outputNote: Note Constraints: // all transfer constraints from §3.1 + // binding constraint H("zkc/binding", H("zkc/credential", masterSeed)) == holderCommitment Parameters (UltraHonk / BN254): - ~2^16 gates, ~2 KB proof, single proof for transfer + binding ``` `zka_transfer_binding_v2` replaces `holderCommitment` with the ordered public inputs `presentationPseudonym, scopeId` and replaces the private `spendingKey` witness with `masterSeed`. The transfer spending key and ZKC v0.4 credential binding key are derived independently from that same secret in-circuit. This is the required v2 payment proof and prevents a prover from pairing a note owned by one subject with a scoped credential presentation from another. ### 5.4 Compliance Bundle A Compliance Bundle is the unit a verifier checks: it may carry a ZKA transaction proof, zero or more atomic ZKC predicate proofs, the identity binding that links a payment note to a credential, and — optionally — a recursive ZKA/Coord work-attestation proof (§4). The bundle is a **ZKA-side envelope that wraps, but does not redefine,** ZKC compliance proofs: each element of `compliance.proofs[]` is an authoritative ZKC `zkc:proof:v1` (linkable) or `zkc:proof:v2` (anonymous/scoped) object from **ZKC v0.4**. The bundle envelope remains independently versioned as `zka:bundle:v1`; carrying the canonical pre-launch Coord v2 draft proof does not change that envelope version. The carried Coord, ZKC, and presentation-profile identities independently select their exact verifier/VK. Conforming cross-protocol implementations MUST select exactly one combination from the canonical `zka:protocol-family:2026-08-15.2` manifest and compatibility matrix; no match or multiple matches is invalid. **Bundle profiles and the substantiveness rule (v0.6.2-draft).** Both `transaction` and `coord_proof` are OPTIONAL members, but a bundle MUST carry **at least one of `{transaction, coord_proof}`** — a bundle with neither attests no substantive ZKA-side work and is invalid (`EMPTY_BUNDLE`). `compliance.proofs[]` always *accompanies* a substantive proof (carried when the counterparty requires predicates); it is never the sole content of a valid bundle. The admissible profiles are: - **(a) Payment bundle** — `transaction` present (`[+ compliance] [+ coord_proof]`), with `binding` linking the payment note to the credential. This is the v0.6.1 bundle, unchanged. - **(b) Pure work-attestation bundle** — `coord_proof` present, `transaction` ABSENT. This is the bundle for non-monetary federation flows: **PRP reconciliation** (proving a Merkle root; no payment) and the non-monetary **VXP `vxp.reciprocal`** (data-for-data; no payment), both of which carry their evidence via AFP's `afp:envelope:v1` over this bundle (AFP v0.3.1 §12.7, ADR-0003). No payment ⇒ no note ⇒ `binding` is N/A and omitted. The normative reciprocal compute/acknowledgement encodings and assurance boundaries are specified in [`sdd/sdd_coord_reciprocal_data_exchange.md`](sdd/sdd_coord_reciprocal_data_exchange.md). `vxp.reciprocal` MUST travel through the existing `zka:bundle:v1` pure work-attestation shape inside an authenticated AFP session, with the bundle `context` equal to the AFP `session_context`; it introduces no new envelope or session-binding primitive. - **(c) Compliance-accompanied** — either of the above plus `compliance.proofs[]`, carried when the counterparty requires predicate attestations. Compliance proofs ride on a substantive bundle; they do not constitute one on their own. Why `transaction` is now optional: ADR-0003 generalized the AFP federation to non-monetary flows (PRP reconciliation and `vxp.reciprocal`), which carry compliance/work evidence over `zka:bundle:v1` with no payment leg. Requiring a payment `transaction` proof would make those flows unable to form a valid bundle. Relaxing `transaction` to OPTIONAL (with the substantiveness rule above) admits the pure work-attestation profile while leaving every existing payment bundle a valid, unchanged instance. The bundle is also the single ZKA-owned cryptographic payload that AFP's session envelope (`afp:envelope:v1`, AFP v0.3.1 §12.7) carries verbatim across a tenant boundary. Both relaxations here — the OPTIONAL `coord_proof` member and the now-OPTIONAL `transaction` member — are backward-compatible and do **not** bump the envelope version string (`zka:bundle:v1` is unchanged): every existing payment/compliance bundle remains a valid instance, a v0.6.1 verifier never reads `coord_proof`, and a transaction-absent bundle is a *new* instance shape that older payment-only flows simply never emit. (These are pre-release, draft-only relaxations; see the version-string note in the Document History.) **Authenticated verifier context is mandatory.** Verification MUST receive the expected session context and context-profile name from the authenticated outer interaction (under AFP, `afp:envelope:v1.session_context` and the selected AFP profile); it MUST NOT copy either expectation from the untrusted bundle. A scoped v2 payment additionally requires a verifier-selected `scopeId`. The bundle metadata, transaction public input, every ZKC context, and every Coord context MUST match that authenticated context, and a scoped presentation MUST match the selected scope. **Verifier selection is part of verification.** For each ZKC predicate, the parser resolves the exact `(envelopeVersion, presentationProfile, proofType)` tuple and supplies it to the ZKC verifier/VK registry; an unsupported or mismatched tuple fails closed. For the payment binding, the verifier receives the exact selected circuit plus the complete ordered public statement: five fields for `zka_transfer_binding` v1 or six fields for `zka_transfer_binding_v2`. A verifier that has no configured cryptographic implementation MUST reject; structural proof-byte checks are not proof verification. **ZKM carriage boundary.** `zka:bundle:v1` currently defines no `authorization` or ZKM member. An implementation MUST NOT place a ZKM proof in `compliance.proofs[]`, reinterpret `coord_proof` as mandate authorization, or claim recursive ZKM carriage through this envelope. A future ZKA/ZKM integration must select either an additive bundle member/profile or a direct settlement interface and must make the ZKA note/nullifier effects and ZKM mandate transition atomic. Until then, ZKM's separately versioned, spend-conjoined statement remains outside `zka:bundle:v1` and is accepted only through a ZKM-aware settlement integration and ZKM's deployment registry. ```typescript interface ComplianceBundle { transaction?: { // OPTIONAL since v0.6.2-draft — present for PAYMENT type: "transfer" | "deposit" | "withdrawal"; // flows; OMITTED for pure work-attestation proof: UltraHonkProof; // bundles (PRP reconciliation, non-monetary VXP). publicInputs: ZKAPublicInputs; }; compliance: { proofs: (ZKCProofV1 | ZKCProofV2)[]; // Version/profile-discriminated ZKC v0.4 objects; // each proves ONE atomic predicate (Safeguard 2). // MAY be empty when the counterparty requires none; // compliance always ACCOMPANIES a substantive proof, // it is never the sole content of a valid bundle. aggregation?: UltraHonkProof; // optional recursive aggregate (ZKC v0.4 §5.2) }; binding?: ({ // Legacy linkable v1 (version may be omitted). proof: IdentityBindingProof; transactionCommitment: Hash; zkcEnvelopeVersion?: "zkc:proof:v1"; holderCommitment: Hash; } | { // ZKC v0.4 scoped v2; no stable holder handle. proof: TransferBindingV2Proof; transactionCommitment: Hash; zkcEnvelopeVersion: "zkc:proof:v2"; presentationPseudonym: Hash; scopeId: Hash; }); coord_proof?: { // OPTIONAL — ZKA/Coord recursive work-attestation proof: UltraHonkProof; // profile-bound recursive DAG proof (§4) publicInputs: CoordFinalPublicInputsV2; // 30-field final receipt plus // graph commitment/count/depth; // receipt.context MUST equal // metadata.context }; metadata: { version: "zka:bundle:v1"; generatedAt: Timestamp; expiresAt: Timestamp; context: Hash; // Deployment/profile-defined replay context. // Under AFP, equals afp:envelope:v1.session_context. }; } ``` ```typescript async function verifyComplianceBundle( bundle: ComplianceBundle, requirements: ComplianceRequirements, trustPolicy: TrustPolicy, authenticated: { sessionContext: Hash; contextProfile: string; selectedScopeId?: Hash; } ): Promise { const errors: VerificationError[] = []; // Expectations are verifier-supplied, never learned from `bundle`. if (bundle.metadata.context !== authenticated.sessionContext) errors.push({ code: "BUNDLE_INCONSISTENT", details: "unauthenticated bundle context" }); if (bundle.transaction !== undefined && bundle.transaction.publicInputs.context !== authenticated.sessionContext) errors.push({ code: "BUNDLE_INCONSISTENT", details: "transaction context mismatch" }); // 0. Substantiveness (v0.6.2-draft) — a valid bundle MUST carry at least one // substantive proof. `compliance.proofs[]` alone is never sufficient. if (bundle.transaction === undefined && bundle.coord_proof === undefined) errors.push({ code: "EMPTY_BUNDLE", details: "bundle carries neither transaction nor coord_proof" }); // 1. ZKA transaction proof (OPTIONAL) — verify only when present. // A pure work-attestation bundle (profile b) has no transaction leg. if (bundle.transaction !== undefined) { if (!(await verifyUltraHonk(bundle.transaction)).valid) errors.push({ code: "INVALID_ZKA_PROOF" }); } // 2. ZKC predicate proofs — each predicate MUST be open-source and // inspectable per Safeguard 4; trustPolicy enumerates accepted // predicate providers (no canonical provider — Safeguard 1). // Verified over whatever proofs ARE present (the set MAY be empty). if (!(await verifyZKCProofs(bundle.compliance, requirements, trustPolicy, authenticated)).valid) errors.push({ code: "INVALID_ZKC_PROOF" }); // 3. Binding links the payment NOTE <-> ZKC credential. It is present IFF // `transaction` is present (no payment ⇒ no note ⇒ nothing to bind). if (bundle.transaction !== undefined) { if (bundle.binding === undefined) { errors.push({ code: "BUNDLE_INCONSISTENT", details: "transaction present but binding absent" }); } else { const statement = resolveBindingStatement(bundle.transaction, bundle.binding); if (!(await verifyManifestSelectedBindingProof(statement)).valid) errors.push({ code: "INVALID_BINDING" }); // 4. Bundle consistency — note<->credential checks, IFF the relevant // members are present. if (bundle.binding.transactionCommitment !== bundle.transaction.publicInputs.outputCommitment) errors.push({ code: "BUNDLE_INCONSISTENT", details: "tx commitment mismatch" }); for (const envelope of bundle.compliance.proofs) { const target = resolveZkcVerifierTarget( envelope.version, envelope.presentationBinding?.profile, envelope.proofs); if (!target.valid) errors.push({ code: "INVALID_ZKC_PROOF", details: "unsupported version/profile" }); else if (!bindingSubjectMatchesEnvelope(bundle.binding, envelope)) errors.push({ code: "BUNDLE_INCONSISTENT", details: "binding subject/profile mismatch" }); } } } else if (bundle.binding !== undefined) { // A transaction-absent bundle MUST NOT carry a note<->credential binding: // there is no note for it to bind (it would be unanchored/meaningless). errors.push({ code: "BUNDLE_INCONSISTENT", details: "binding present without a transaction" }); } // When `transaction` is absent, the bundle is anchored to a party identity // NOT via a note<->credential binding (N/A) but via the shared bundle context: // every present proof carries `context == metadata.context` (coord_proof step // 6b below; each ZKC proof's claimBinding/context per §5.2), and AFP equates // that context to the authenticated `session_context` of the carrying // `afp:envelope:v1` (AFP §12.7). Identity binding is thus over whatever proofs // ARE present, reusing the existing §5.2 context anchor — no new mechanism. // 5. Freshness — bundle expiry AND ZKC accumulator-epoch recency. // Per ZKC v0.4 §6.2, each ZKC proof's accumulator epoch MUST be // within MAX_EPOCH_LAG = 10 epochs of the verifier's current epoch. if (bundle.metadata.expiresAt < Date.now()) errors.push({ code: "BUNDLE_EXPIRED" }); if (!zkcEpochsWithinLag(bundle.compliance.proofs, /*MAX_EPOCH_LAG*/ 10)) errors.push({ code: "ZKC_EPOCH_STALE" }); // 6. ZKA/Coord work-attestation (OPTIONAL) — verify only when present. // A bundle without coord_proof is unaffected by this step. if (bundle.coord_proof !== undefined) { // 6a. Verify the manifest-bound Coord v2 recursive artifact. This authenticates // profile-adapter and predecessor proofs plus their constrained receipts; // step 6c still applies the verifier's own policy and completion definition. // An Aztec proof is accepted only through an independently conforming // manifest-bound profile (§8.2), never by kernel composition alone. if (!(await verifyManifestBoundUltraHonk({ proof: bundle.coord_proof.proof, publicInputs: bundle.coord_proof.publicInputs, circuit: "zka_coordination_v2_recursive", artifactPolicy: trustPolicy.coord })).valid) errors.push({ code: "INVALID_COORD_PROOF" }); // 6b. Context binding — the coordination proof MUST share the bundle context // (and hence, under AFP, the session_context of the carrying envelope, §12.7). if (bundle.coord_proof.publicInputs.receipt.context !== bundle.metadata.context) errors.push({ code: "BUNDLE_INCONSISTENT", details: "coord context mismatch" }); // 6c. Verifier-selected policy and complete-workflow checks. A prover's // public policy root or workflow root is never authoritative. const coord = trustPolicy.coord; const receipt = bundle.coord_proof.publicInputs.receipt; if (coord === undefined) { errors.push({ code: "COORD_V2_PROFILE_POLICY", details: "no verifier Coord policy" }); } else { const definition = validateAndHashCoordWorkflow(coord.workflowDefinition); const policy = validateAndHashCoordPolicy( coord.policyDocument, coord.acceptedAssuranceClasses, coord.acceptedProfileManifestHashes); if (!definition.valid || receipt.workflowDefinitionHash !== definition.root || receipt.stepId !== definition.completionStepId) errors.push({ code: "COORD_V2_INCOMPLETE_WORKFLOW" }); if (!policy.valid || policy.root !== coord.verifierPolicyRoot || receipt.verifierPolicyRoot !== policy.root) errors.push({ code: "COORD_V2_PROFILE_POLICY" }); if (definition.valid && !definition.steps.every(step => coord.acceptedAssuranceClasses.includes(step.assuranceClass))) errors.push({ code: "COORD_V2_ASSURANCE" }); if (definition.valid && !definition.steps.every(step => coord.acceptedProfileManifestHashes.includes(step.profileManifestHash))) errors.push({ code: "COORD_V2_ARTIFACT" }); if (coord.expectedWorkflowInstanceId !== undefined && receipt.workflowInstanceId !== coord.expectedWorkflowInstanceId) errors.push({ code: "COORD_V2_SCOPE", details: "workflow instance mismatch" }); if (coord.expectedContext !== undefined && receipt.context !== coord.expectedContext) errors.push({ code: "COORD_V2_SCOPE", details: "verifier context mismatch" }); } } return { valid: errors.length === 0, errors }; } ``` **Conditional verification (v0.6.2-draft).** Each member is verified IFF present, and the transaction-dependent consistency checks fire only when a payment leg exists: - `transaction` verified IFF present (step 1). - `binding` is the payment-note↔credential link: it MUST be present when `transaction` is present and MUST be absent when `transaction` is absent. Its transaction commitment MUST match the payment output. Every carried compliance envelope MUST match the binding's version, profile, and subject handle: v1/linkable compares `holderCommitment`; v2/scoped compares both `presentationPseudonym` and `scopeId`. v2/anonymous and mixed/cross-profile sets are invalid for payments. With no payment there is no note, so this binding is N/A. - `compliance.proofs[]` is verified over whatever proofs are present (the set MAY be empty). - `coord_proof` verified IFF present (step 6), with `context == metadata.context` (step 6b) and verifier-selected profile/workflow/artifact policy plus designated completion-step checks (step 6c). **Identity anchor when `transaction` is absent.** A pure work-attestation bundle still binds to a single party identity, but **not** through the note↔credential binding (which is N/A without a note). Instead, identity binding is over whatever proofs ARE present, reusing the **existing** anchor with no new cryptography: every present proof carries the shared bundle `context` (`coord_proof.publicInputs.receipt.context` per step 6b; each ZKC proof's `claimBinding`/context per §5.2), and AFP equates that `context` to the authenticated `session_context` of the carrying `afp:envelope:v1` (AFP v0.3.1 §12.7). The envelope's session authentication is therefore the party-identity anchor for a transaction-absent bundle, exactly as it is the cross-tenant transport anchor for a payment bundle. A bundle verification failure has exactly one permitted consequence: the counterparty declines the interaction. It MUST NOT trigger any note invalidation, freeze, or force-transfer (Safeguard 3). This holds for a `coord_proof` failure as well: a failed (or context-mismatched) coordination proof means the work-attestation is not accepted, never that any note is affected. > **Coord proof carriage supersedes `vxp:coordbundle:v1` (AFP v0.3.1 §12.7, ADR-0003).** The optional `coord_proof` member makes `zka:bundle:v1` the single ZKA-owned wire format that carries the ZKA/Coord recursive work-attestation proof alongside the Pay/compliance/binding proofs — the exact capability VXP v0.2 §11 had introduced `vxp:coordbundle:v1` to provide. Under ADR-0003 the home for that capability is ZKA, not a profile: VXP's `vxp:coordbundle:v1` (and any PRP bundle wrapper) is **retired** in favor of *AFP envelope (`afp:envelope:v1`) over this extended `zka:bundle:v1`*. A profile never authors a bundle wire format; it places a ZKA bundle inside an AFP envelope. The Coord proof's `receipt.context` (above) is what AFP binds to the session: it equals the bundle's `metadata.context`, which an AFP envelope in turn equates to its `session_context` (AFP §12.7). Coord v2 changes the carried public-input shape and verifier policy checks as specified in §4; the pre-launch five-field coordination carriage is invalid. > **AFP §12.7 dependency status:** resolved by this §5.4 revision. AFP's previously flagged requirement that a ZKA-owned bundle carry the ZKA/Coord proof is satisfied by the optional `coord_proof` member of `zka:bundle:v1`; AFP remains responsible only for `afp:envelope:v1` session framing, routing, and `session_context` binding. ### 5.4.1 ZKM Authorization Carriage and Atomic Settlement ZKM spend mandates are an **optional interaction authorization**, not a ZKA note-validity rule. A conforming ZKA deployment MAY expose the interface in this section; an ordinary transfer, deposit, withdrawal, force exit, or bundle that does not opt into ZKM MUST remain valid under exactly the pre-ZKM rules. **Canonical carriage decision — direct settlement.** ZKA selects the ZKM reference path: the settlement target verifies one `zkm:spend-conjoined:v2` UltraHonk proof and applies its ZKA and ZKM effects in one target transaction. The proof already contains the ZKM SPEND transition, the ZKA value-note spend, and the policy predicate over shared wires; a second ZKA transaction proof MUST NOT be accepted as a substitute for that structural conjunction. This revision does **not** add a `zkm_authorization` (or any other ZKM member) to `zka:bundle:v1`. Merely placing unrelated ZKA and ZKM proofs in an envelope is non-conforming. The spend-conjoined public vector is exactly 14 BN254 field elements, in this order (integers are zero-extended to one field element): ```text (kind, mandateNullifier, mandateSuccessorCommitment, mandateSecondSuccessorCommitment, mandateRoot, revocationRoot, revocationScheme, currentTime, settlementContext, assetId, zkaNoteRoot, zkaNullifier, zkaRecipientCommitment, zkaChangeCommitment) ``` `kind` MUST equal ZKM SPEND (`0`), `revocationScheme` MUST equal `2`, and `mandateSecondSuccessorCommitment` MUST be the zero sentinel. The verifier MUST flatten named inputs in this order; it MUST NOT sort fields or accept an alternate encoder. ZKM keeps the private `amount` off this vector. The two ZKA output commitments bind it inside the proof. `revocationScheme` names the ZKM revocation-accumulator profile the proof's non-membership witness was constructed against. Scheme `2` is the depth-254 full-identifier exclusion tree; scheme values `0`, `1`, and `3..255` are invalid and MUST be rejected before verification. The target treats it as a statement discriminator it checks, never as a policy dial it selects: the registry-pinned VK already accepts exactly one scheme, and the explicit equality check makes a scheme mismatch a deterministic rejection rather than a verification failure whose cause is opaque. **Verifier, registry, and binding checks.** Before changing state, a conforming target MUST perform all of the following: 1. Select the one deployment-registry entry for `zkm:spend-conjoined:v2`, including its exact VK hash, verifier artifact, public-input encoder, ZKM version, settlement target, and asset profile. The value-moving entry MUST be immutable or version-pinned for the lifetime of the adapter. A proof verified by `zkm:mandate-only:v2`, a possession, disclosure, revocation, control-attestation, or unknown VK MUST NOT reach a value-moving entry point. 2. Require `settlementContext` to equal the target's own non-zero ZKA deployment context. The fixed spend-conjoined circuit also proves that the consumed mandate commits to this domain. A caller-supplied context is never authoritative. 3. Require `assetId` to equal the target registry's asset identifier. The fixed circuit proves that the consumed ZKA note and both output notes use the corresponding ZKA asset commitment. A settlement adapter MUST document the exact `assetId`↔ZKA asset-commitment mapping it accepts. 4. Recognize `mandateRoot` in its own recent mandate-root history, `zkaNoteRoot` in its own recent ZKA note-root history, and `revocationRoot` in its own ZKM revocation-root history. It derives the revocation epoch from that history and applies ZKM freshness policy. Post-cutover, that history MUST contain only scheme-2 roots published at or after the ZKM activation epoch; a scheme-1 root MUST NOT be recognized, relabelled, or reinterpreted under scheme 2. `currentTime` MUST equal the target clock value substituted for this settlement attempt. `revocationScheme` MUST equal `2`. 5. Verify the proof against the registry-selected spend-conjoined VK and the exact vector above. That circuit is the same-note/same-holder proof: its public `zkaNullifier` consumes the ZKA note proven under `zkaNoteRoot`, while one private holder secret derives the mandate holder key, the consumed ZKA note address, and the ZKA nullifier key. Host code MUST NOT emulate this private equality with caller-supplied holder fields. 6. Reject if `mandateNullifier` is already present in the target's **single mandate-nullifier set for `settlementContext`**, shared by every accepted ZKM VK, or if `zkaNullifier` is already present in ZKA's nullifier state. Per-VK mandate-nullifier sets are forbidden: the same mandate note could otherwise be replayed once per statement/VK. All field elements MUST be canonical BN254 representatives. Required roots, nullifiers, context, asset, and live output commitments MUST be non-zero. Apart from the SPEND kind discriminator, the only zero in a conforming SPEND vector is the unused second mandate-successor slot. **ZKM statement version boundary (fail-closed).** The statements selected here are the post-cutover ZKM ones. `zkm:spend-conjoined:v1` and `zkm:mandate-only:v1` — the 13- and 8-element ABIs of the previous revision, whose witnesses were built against the depth-128 low-half revocation tree and which carry no `revocationScheme` — are valid only under pre-cutover ZKM keys and roots. A post-cutover deployment registry MUST omit both, MUST NOT reinterpret or upgrade a v1 proof, and MUST NOT accept a v1 vector under a v2 VK or a v2 vector under a v1 VK. The two profiles never coexist in one registry: ZKM's cutover is atomic, so before activation only the v1 profile is accepted and at or after activation only scheme 2 and its v2 keys are. A deployment that must remain on pre-cutover ZKM is conforming to the previous revision of this section and MUST document that it is pre-cutover; it is not conforming to this one, and a target MUST NOT present the two as interchangeable. **Atomic state transition.** After every precondition and proof check succeeds, the target applies exactly these five effects as one transaction: 1. check-and-insert `mandateNullifier` into the shared ZKM nullifier set; 2. check-and-insert `zkaNullifier` into ZKA nullifier state; 3. insert `mandateSuccessorCommitment` into the mandate tree; 4. insert `zkaRecipientCommitment` into the ZKA note tree; and 5. insert `zkaChangeCommitment` into the ZKA note tree. Either all five effects and their events become visible, or none do. A target MUST use a transactional state primitive (for example an EVM transaction, a database transaction, or copy-on-write staging) and MUST propagate any late tree, capacity, or callback failure so the two nullifiers roll back with every commitment insertion. It MUST NOT acknowledge, queue, or emit a successful effect before the whole transition commits. All five effects use the target's ordinary ZKA finality semantics. The non-value entry point verifies `zkm:mandate-only:v2` over its distinct 9-element ABI and MAY update only mandate nullifier/commitment state: ```text (kind, mandateNullifier, mandateSuccessorCommitment, mandateSecondSuccessorCommitment, mandateRoot, revocationRoot, revocationScheme, currentTime, settlementContext) ``` `kind` MUST NOT be SPEND (`0`) and `revocationScheme` MUST equal `2`. Its ABI contains no ZKA note root, ZKA nullifier, ZKA output commitment, or asset ID; this absence is a deployment-safety property, not a host-side policy flag. The two vectors are the same nine leading elements plus the spend-conjoined form's five-element ZKA value leg, so an implementation MUST distinguish them by registry-selected VK and vector length, never by content sniffing. Both entry points MUST share the mandate-nullifier set. **Exit and revocation boundary.** The ordinary ZKA withdrawal and force-exit entry points MUST NOT call the ZKM adapter, inspect mandate state, require a ZKM proof, or consult ZKM revocation/expiry. Revoking or exhausting a mandate can prevent a future mandate-authorized interaction; it cannot invalidate, freeze, redirect, or delay any ZKA note or exit. ZKM RETIRE concerns remaining mandate authority and is not the ZKA custody exit. **`zka:bundle:v1` compatibility proof.** The JSON interface in §5.4, its version literal, substantiveness rule, verifier algorithm, error taxonomy, and all existing serialized instances are unchanged. The direct-settlement input is a separate target method and is never parsed by `verifyComplianceBundle`. Therefore every previously valid `zka:bundle:v1` remains byte-for-byte valid, unknown ZKM carriage is not implied, and no envelope version bump is warranted. A future recursive/bundle path requires an explicitly specified member, registry binding, equality checks, atomic target transition, and compatibility decision; until then it is non-conforming. The executable reference boundaries are `contracts/ethereum/ZKMAtomicSettlement.sol` and `src/zka-sdk/src/zkm/mod.rs`. The Solidity contract fixes separate immutable spend-conjoined and mandate-only verifier lanes, authenticates both verifier/VK-hash pairs through the target registry at construction, and uses one cross-lane mandate-nullifier mapping; the SDK model stages a cloned state and publishes it only after every effect succeeds. They define the adapter contract, not a claim that every existing ZKA pool deployment has enabled ZKM. ### 5.5 Predicate Model A **predicate** is an open-source compliance circuit that proves exactly one property (e.g. "holder is not on an OFAC SDN list", "holder is over an age threshold"). Predicates are published by independent, permissionless **predicate providers**. - **Pluralism (Safeguard 1):** the protocol carries no canonical predicate set and no privileged provider. A verifier's `trustPolicy` enumerates which providers/predicates *it* accepts; different counterparties may accept different predicate sets. - **Atomicity (Safeguard 2):** each `ZKCComplianceProof` proves a single predicate. A composite (multi-predicate) request MUST be expressed as separate proofs with separate, principal-readable consent. - **Auditability (Safeguard 4):** predicate circuits MUST be published as inspectable Noir source, versioned, with a public review period before activation. An SDK MUST refuse, by default, to generate a proof against a predicate the agent cannot inspect. (Safeguard 4 is mirrored normatively in **ZKC v0.4 §1.4**, which mandates open-source/auditable predicate and credential-schema circuits, versioned publication with a public review period, and SDK refusal of opaque predicates by default with logged opt-in override.) > **Resolved in ZKC v0.2.2 — proof-type registry is non-exclusive.** Earlier ZKC drafts (v0.2.1 Appendix A) enumerated fixed credential/proof-type identifiers and closed schemas, which tensioned ZKA Safeguard 1. **ZKC v0.2.2 reframes Appendix A as a "Well-Known Identifier Registry (Non-Exclusive)"** and §3.1–3.6 as reference (non-mandatory) schemas, stating normatively that no type/provider is privileged or required, that a verifier's trust policy MAY accept types/predicates outside the registry, and that the protocol defines no mechanism for designating a canonical set. This matches the non-exclusive reading this spec already adopted: ZKC well-known type IDs are permitted but never required, and a verifier's `trustPolicy` MAY accept predicates outside the ZKC registry. No residual conflict with Safeguard 1. ### 5.6 Credential Lifecycle > **Status: normative by reference to ZKC.** The binding, bundle, and predicate model (§5.2–5.5) are normative in this specification. The issuance, revocation, and accumulator-witness mechanics below are owned by ZKC and are now finalized there: the concrete issuer-bootstrapping and accumulator-witness-distribution protocols are specified normatively by the ZKC Specification §4 (Attestation Layer) and the **ZKC Issuer Adapter Protocol v1** (`ZKC/docs/zkc_issuer_adapter_protocol_v1.md`, with `schemas/issuer-metadata.schema.json` and a machine-checked conformance kit). ZKA references that protocol rather than redefining it. **Issuance / Issuer Bootstrapping.** A ZKC credential is issued by a credential issuer (e.g. a KYC provider) binding an attestation to the holder's `cbk` via `holderCommitment = H("zkc/binding", cbk)`. The issuer never learns the holder's ZKA address or spending key. The issuer-bootstrapping protocol (issuer registration, issuer key publication, holder ↔ issuer attestation exchange) is REQUIRED for a conforming ZKC deployment and is specified normatively by ZKC (ZKC Specification §4 and the ZKC Issuer Adapter Protocol v1); ZKA references it rather than redefining it. **Revocation.** Credential validity is tracked by a cryptographic accumulator. A predicate proof MUST demonstrate non-revocation by proving a current accumulator witness for the credential. Revocation MUST NOT be able to affect ZKA note validity (Safeguard 3) — a revoked credential causes counterparty refusal only. **Accumulator Witness Update Distribution.** On any revocation the accumulator changes and all non-revoked holders need an updated witness. A conforming deployment MUST provide a witness-update distribution channel that does not deanonymize holders (e.g. publish epoch accumulator deltas that holders apply locally). The concrete distribution protocol is Draft. ### 5.7 Structural Requirements The following are REQUIRED for a complete deployment. The note discovery, key custody, and key rotation/recovery requirements are normative in this revision; delegated proof generation remains Draft where marked. - **Note Discovery Protocol.** A recipient MUST be able to discover incoming notes using only the incoming viewing key, without requiring a trusted indexer, operator, or sender-side out-of-band channel to reveal which notes belong to the recipient. Every deposit, transfer, bridge import, or settlement action that creates a ZKA note MUST publish an append-only note announcement. The deployment-specific carrier MAY be a smart-contract event, Aztec note log, APL event stream, or another authenticated append-only log, but clients MUST be able to reconstruct the same ordered announcement set from public or holder-authorized data. ```typescript interface NoteAnnouncement { commitment: Commitment; encryptedNoteCiphertext: Uint8Array; ephemeralPubkey: Uint8Array; viewingTag?: Uint8Array; context: Hash; // chain, deployment, asset, and circuit-domain binding } ``` `encryptedNoteCiphertext` MUST encrypt the note plaintext, note randomness, asset/value metadata needed for spending, and any encrypted memo under a key agreement that the sender forms from its ephemeral secret and the recipient's public viewing/address material, and that the recipient can reproduce with its incoming viewing key. The announcement MUST NOT expose the recipient address, `masterSeed`, spending key, note plaintext, or linkable holder identity. Recipients scan announcements with their incoming viewing key and trial-decrypt candidate ciphertexts. Implementations SHOULD include a short Orchard-style `viewingTag` as a coarse filter to reduce scanning cost. The tag MUST be derived so that possession of the tag, announcement log, or indexer state is insufficient to identify the recipient without the incoming viewing key. Indexers MAY mirror, shard, and query the announcement log. They MUST NOT be authoritative for note ownership and MUST NOT learn which announcements a recipient accepts. A recipient MUST be able to recover note discovery from the append-only log plus its own viewing key even if all optional indexers are unavailable. - **Agent Key Custody Model.** ZKA's default and required protocol posture is non-custodial. Agents or their principals own the KDC seed material, including `masterSeed` or the AFP-derived per-entity sub-seed `M'`, and derive spending, viewing, proof, address, and credential-binding keys locally or through a holder-controlled signer. A conforming ZKA deployment MUST NOT require `masterSeed`, spending keys, or unencrypted note plaintext to be held by a platform operator, CredentialProxy, BalanceServer, indexer, prover, predicate provider, or compliance service. Any hosted custody mode is a separate deployment/product profile and MUST NOT be represented as baseline ZKA conformance unless the host lacks unilateral spend authority, such as through holder-controlled remote signing, HSM/KMS policy controlled by the principal, MPC/TSS, or equivalent delegated signing. The ZKA SDK surface SHOULD support local encrypted keystores, OS keychain integrations, HSM/KMS-backed signers, remote signer adapters, viewing-key-only audit export, and explicit capability separation between spending authority and viewing/audit authority. - **Key Rotation / Recovery.** Because `cbk` derives from the master seed, seed compromise orphans the credential binding. A conforming deployment MUST define a rotation/recovery path that re-binds existing credentials to a new seed without revealing the old↔new link on-chain. ZKC v0.4 §4.5 defines the required ZKC-Core credential rebind proof (`zkc:proof:credential_rebind:v1`) for holder-controlled migration from `oldHolderCommitment` to `newHolderCommitment`; issuer-specific migration tokens MAY optimize an integration but MUST NOT replace that core circuit. Routine rotation MUST use ordinary holder-controlled private self-transfers from old notes to an address derived from the replacement seed. The transfer and batch proof formats, encrypted announcements, calldata, events, and public state transitions MUST be indistinguishable in shape from ordinary ZKA/Pay activity: no rotation flag, migration circuit identifier, completion marker, or public old-to-new note mapping is permitted. SDKs MUST provide circuit-bounded batching and randomized local scheduling where latency permits; deployments MAY tune the local policy but MUST document it. Scheduling metadata MUST NOT enter a proof or transaction. For the Base WETH9 immutable-pool deployment profile, production SDK execution MUST use the manifest-bound `zka_pay_transition` / `zka_pay_batch` Noir circuits and that profile's ordinary immutable-pool submission path. Other settlement profiles MUST bind their production rotation circuits and ordinary submission path in the deployment-profile manifest and provide equivalent digest, root, nullifier, and finality validation. Simulated proof execution is not a conforming rotation path. Before proving, the client MUST validate the exact canonical batch-data digest and old commitment/nullifier roots and counters. After submission, status MUST be derived from canonical replay of accepted commitment roots, nullifiers, output commitments, and the deployment's finality policy—not from a local success flag or RPC receipt alone. Rotation state MUST be locally resumable and typed at least as `planned`, `proof_ready`, `submitted`, `retryable_submission`, `confirmed`, `retryable_stale_root`, `already_spent`, `missing_note_plaintext`, and `missing_merkle_path`. `proof_ready` means that a real batch proof was durably recorded before crossing the broadcast boundary. `retryable_submission` requires explicit holder confirmation that the indeterminate transaction and every known canonical replacement were dropped; unresolved or indeterminate broadcasts MUST remain `submitted` and MUST NOT collapse to `planned` or be automatically rebroadcast. A note is `confirmed` only when its nullifier and the batch's output commitments exist at the canonical finalized checkpoint. Before that checkpoint, a reorg MUST remove any noncanonical acceptance observation, but an indeterminate broadcast MUST remain `submitted` until canonical finalization, a finalized conflicting spend, or explicit holder confirmation that the transaction and every known replacement were dropped; only then may canonical replay select a retryable state. Finalized `confirmed` state is immutable under that synchronizer boundary. A stale root is retryable after rebuilding the witness against current state. A nullifier observed without the expected atomic batch outputs is terminal `already_spent`; a note whose plaintext or current membership witness cannot be reconstructed MUST be reported with the corresponding missing-data state rather than silently omitted. Durable diagnostics MUST contain no seed, spending key, note plaintext, recipient address, private path, or old-to-new note mapping. The replacement address and any ZKC v0.4 payment binding MUST derive from the same replacement `masterSeed`: `sk = H("zka/spending", newMasterSeed)`, `address = H("zka/address", H("zka/viewing", sk))`, `cbk = H("zkc/credential", newMasterSeed)`, and `presentationPseudonym = H("zkc/presentation-scope:v1", cbk, scopeId)`. Consequently an old-seed note cannot satisfy a transfer-binding-v2 presentation derived from the new seed. AFP/APL integrations MUST carry the ordinary new-seed payment bundle; a special rotation-completion attestation, if ever required, needs a separate ADR and MUST NOT be inferred from or added to settlement calldata. Credential rebinding is owned by ZKC and affects credential issuance and acceptance only. It MUST NOT affect ZKA note validity, custody, transferability, withdrawal, nullifier semantics, or revocation semantics. ZKA deployments that integrate issuer-assisted credential rebinding MUST require sealed/minimized issuer audit mappings for any retained old-holder-commitment to new-holder-commitment link. Such records MUST be access-controlled, encrypted or equivalently sealed at rest, purpose-limited to migration audit/compliance, retained for the minimum applicable period, and never published to settlement, indexer, note-discovery, compliance-bundle, or public-proof surfaces. - **Delegated Proof Generation.** An agent MAY delegate proof generation to a prover service via encrypted witness transport such that the prover learns neither the witness nor the holder identity. Mechanism: Draft. ### 5.8 Aztec Composability When deployed as an `aztec.nr` contract, an identity binding proof (§5.2) is composable with private DeFi within a single Aztec transaction via the kernel circuit call stack (see §8.4). Credential non-revocation checks run against Aztec public state without revealing which credential is checked. --- ## 6. Selective Disclosure ### 6.1 Balance Proof Circuit (`zka_balance`) Proves `note.value >= threshold` without revealing the exact value. Uses Noir's built-in range constraints. A balance proof is an atomic single-property disclosure and is subject to Safeguard 2. ```typescript interface SelectiveDisclosure { claim: DisclosureClaim; proof: UltraHonkProof; publicInputs: { noteCommitment: Commitment; merkleRoot: Hash; claimBinding: Hash; // H(claim_type, threshold) }; } ``` --- ## 7. Bridge Circuits ### 7.1 Ethereum Bridge Import/export between the ZKA shielded pool and Ethereum. Solidity verifier auto-generated via `nargo codegen-verifier` (`UltraVerifier.sol`). Verifier gas target: <300k gas per verification. Export (withdrawal) is unconditional per Safeguard 5. A conforming ZKA Ethereum bridge contract MUST satisfy the following invariants: 1. **Ownerless and non-upgradeable.** No `owner`, no `pause()`, no `upgradeTo()`, no `setOperator()`, no `setVerifier()`, no `rescue()`, no `migrate()`, no proxy slot, no `selfdestruct`, no constructor `delegatecall`. 2. **Immutable configuration.** Asset address, verifier addresses, operator address (if any), liveness parameters, initial root, and the deployment-binding context MUST be declared `immutable` and set in the constructor. 3. **Append-only mutable state.** Root index, total appended commitments, and last-root-submitted timestamp MUST be strictly monotonic. Consumed nullifiers MUST transition only from `false` to `true`. Accepted roots MUST be removed only by deterministic window pruning. 4. **Root advancement MUST be gated by a root-update proof** verifying that the new root is the correct Poseidon2 incremental fold of the deterministic interleave of on-chain deposit-event commitments and blob-published transfer-announcement commitments into the prior root. The proof system MUST also establish that its private blob-commitment list is the canonical list encoded by the blob identified under invariant 6; the legacy `zka_root_update` circuit (§3.3) does not establish that relation on its own. 5. **Permissionless force-exit path.** When operator silence exceeds a published immutable threshold, any address MUST be able to advance the root and call withdrawal. The contract MUST NOT distinguish operator and non-operator callers for withdrawal. 6. **EIP-4844 blob data availability.** Internal-transfer `NoteAnnouncement`s (§5.7) MUST be carried in EIP-4844 blobs referenced by the same transaction that advances the root. The bridge MUST obtain the blob versioned hash through `BLOBHASH`, bind it to the root-update proof's public inputs, and verify the proof relation required by invariant 4 so the folded leaf list equals the ordered commitment projection decoded from the canonical announcement log. Blob sidecars provide public retrieval only for the network's blob-availability window; a conforming deployment MUST durably archive the canonical payload before expiry, and holders MUST retain the witness data required by §7.1.5. The versioned hash and root-update event alone are not enough to reconstruct expired blob contents. Historical deployment: [APL v0.3.2 §5.4](https://github.com/pyramidal-io/ADAMAS.Network/blob/main/docs/apl/apl_architecture_v0_3_2.md) in [pyramidal-io/ADAMAS.Network](https://github.com/pyramidal-io/ADAMAS.Network) — see also [`contracts/ethereum/APLBridge.sol`](https://github.com/pyramidal-io/ADAMAS.Network/blob/main/contracts/ethereum/APLBridge.sol). The immutable Base APLBridge predates this correction and does not satisfy invariants 4 and 6 because it neither executes `BLOBHASH` nor proves the blob-to-private-leaf relation; it MUST NOT be used as a conforming reference implementation or receive new escrow. #### 7.1.1 Bridge State (normative) A conforming bridge MUST hold, on-chain, all state needed for any holder to exit without any off-chain operator service: | State | Description | Mutability | |-------|-------------|------------| | Canonical append log | Note commitments in deterministic settlement order with zero-based leaf indices (§2.4). Carried as contract storage plus `CommitmentAppendEvent`s. | Append-only | | Accepted root set | Recent Merkle roots per the deployment's accepted-root window (§2.4): latest `N` roots (`N >= 1`), a block-height/epoch/wall-clock window, or both. Window parameters MUST be `immutable` constructor configuration. | Deterministic window pruning only | | Nullifier set | Consumed nullifiers. | `false → true` only | | Verifier references | Addresses of the withdrawal verifier and (where root advancement is SNARK-gated, invariant 4) the root-update verifier. | `immutable`, or version-pinned by immutable configuration | | Escrow balances | Per-asset collateral backing the shielded pool's circulating supply. The bridge MUST maintain `escrow[asset] >= circulatingSupply[asset]` (solvency invariant). | Changed only by deposits and withdrawals | | Deployment context | The deployment-binding context hash (chain, contract, asset-domain, circuit-domain) that withdrawal proofs MUST bind via their `context` public input. | `immutable` | No other party — operator, indexer, balance server, prover — holds authoritative state. Anything they hold MUST be reconstructable from the bridge's events (§7.1.3) and MUST NOT be required for exit. #### 7.1.2 Force-Exit Path (normative) Force-exit is the ordinary withdrawal, exercised permissionlessly. It is an **unconditional withdrawal path, not an operator-mediated rescue path**: there is no separate rescue circuit, no operator co-signature, no rescue court, and no admin intervention point. The submitted proof is the standard `zka_withdrawal` proof (§3.2), whose public inputs are `(nullifier, merkleRoot, amount, asset, externalRecipient, context)`. Per Safeguard 5 (§1.6), no compliance predicate, credential, or binding proof is an input, and the bridge MUST NOT add one. A conforming bridge MUST process a withdrawal in this order: 1. **Root acceptance.** `merkleRoot` MUST be in the accepted root set; otherwise reject with `UNKNOWN_ROOT` or `STALE_ROOT` (§7.1.4). 2. **Nullifier freshness.** `nullifier` MUST NOT be in the nullifier set; otherwise reject with `NULLIFIER_ALREADY_SPENT`. 3. **Asset support and context binding.** `asset` MUST be a configured asset and `context` MUST equal the deployment context; otherwise reject with `UNSUPPORTED_ASSET` / `INVALID_CONTEXT`. 4. **Proof verification.** The proof MUST verify against the immutable withdrawal verifier over exactly the §3.2 public-input vector; otherwise reject with `INVALID_PROOF`. 5. **Escrow sufficiency.** Available escrow for `asset` MUST cover `amount`; otherwise reject with `INSUFFICIENT_ESCROW`. (Under the solvency invariant of §7.1.1 this cannot occur absent a bridge defect; the check is defense-in-depth and MUST still be explicit.) 6. **Effects before interactions.** The bridge MUST record the nullifier and decrement supply/escrow accounting **before** transferring funds to `externalRecipient`. **No challenge period.** For a pure on-chain shielded pool, the accepted root history and the nullifier set are the complete audit surface: a withdrawal is valid iff the proof verifies against an accepted root and the nullifier is fresh. A conforming bridge MUST NOT impose a challenge, delay, or dispute window on shielded-pool exits. Challenge periods exist to police *off-chain* data withholding (state channels, optimistic rollup state) and are out of scope for this path. **Permissionlessness.** The withdrawal entry point MUST be callable by any address, MUST NOT distinguish operator and non-operator callers, and — together with invariant 5 — MUST remain exercisable when the operator is silent: after the published immutable liveness threshold, any address can advance the root with a root-update proof satisfying invariants 4 and 6 and then withdraw. #### 7.1.3 Root Publication Model (normative) Root advancement and commitment-append metadata MUST be observable from settlement-chain execution history. Blob-backed announcement payloads MUST additionally remain retrievable from consensus blob sidecars during the network availability window and from the deployment's durable archive afterward: - Every appended commitment MUST emit a `CommitmentAppendEvent` (§2.4) carrying the commitment, its leaf index, and the pre/post roots. - Every accepted root advancement MUST emit a root-update event carrying at minimum `(previousRoot, newRoot, newTotal)` and, for blob-backed SNARK-gated advancement (invariants 4 and 6), the same-transaction blob versioned hash obtained with `BLOBHASH`. Emitting that hash is necessary for reconstruction but does not replace the proof relation between the blob contents and the folded commitments. - A client or indexer replaying these events together with each referenced canonical blob payload MUST reconstruct the identical tree, leaf order, and accepted root set. The reconstruction source is the consensus blob sidecar while it remains available and the durable archive afterward; holders retain their own exit data per §7.1.5. Indexers and archives remain mirrors, never authorities (§2.4). - Accepted-root pruning is deterministic window pruning only; pruning MUST NOT be discretionary. #### 7.1.4 Failure Codes (normative) Withdrawal rejections MUST be machine-readable (e.g. Solidity custom errors) and distinguish at minimum: | Code | Condition | Class | |------|-----------|-------| | `UNKNOWN_ROOT` | `merkleRoot` never accepted by this bridge | Retryable after re-proving against an accepted root | | `STALE_ROOT` | `merkleRoot` was accepted but has aged out of the window | Retryable: regenerate the Merkle path against a current accepted root and re-prove | | `NULLIFIER_ALREADY_SPENT` | `nullifier` already recorded | Terminal for this note | | `INVALID_PROOF` | Proof fails verification | Retryable after correcting the witness | | `UNSUPPORTED_ASSET` | `asset` not configured at deployment | Terminal for this bridge instance | | `INVALID_CONTEXT` | `context` does not bind this deployment | Retryable with the correct context | | `INSUFFICIENT_ESCROW` | Escrow for `asset` cannot cover `amount` | Retryable; indicates a bridge solvency defect that MUST be observable | A stale or unknown root MUST NOT invalidate the note: the note remains spendable, and the holder retries with a fresh proof. No failure code, and no party, can transition a note itself to an invalid state — only nullifier consumption by the holder's own proof does that (Safeguard 3). #### 7.1.5 Client-Retained Witness Data (normative) Force-exit is only credible if holders can construct the withdrawal witness with **every operator service unavailable**. A conforming client/SDK MUST retain, locally or holder-controlled: - note plaintext: asset, value, randomness, and any memo needed for spending; - owner key material or a holder-controlled signer reference (per the §5.7 custody model — never operator-held); - the note's leaf index, and **either** a cached Merkle path **or** enough append-event data (§7.1.3) to reconstruct one; - root history references sufficient to select an accepted root; - note announcement references (§5.7) for re-discovery and audit. A conforming SDK MUST be able to produce a valid withdrawal proof from this retained data plus public L1 history alone. When witness data is missing, diagnostics MUST be deterministic and machine-readable (missing plaintext, missing/stale path, stale root, spent nullifier), so a holder can distinguish recoverable from unrecoverable states. Retained data MUST NOT expose spending keys to any deployment service. #### 7.1.6 Relationship to the Aztec Portal Path (informative) The Aztec native deployment (§8) settles L2 → L1 withdrawals through the Aztec Outbox and an ownerless per-asset portal contract (ADR-0002). That path consumes Aztec kernel-proven L2→L1 messages; it does not use the §3.2 standalone withdrawal proof, this section's bridge state, or the §3.3 root-update circuit. The two exit paths are deliberately separate mechanisms satisfying the same Safeguard 5 obligation; conformance to one does not imply conformance to the other. ### 7.2 Namada Bridge | Noir package | Circuit ID | Proves | |--------------|------------|--------| | `bridge_namada_import` | `NAMADA_IMPORT_V1` | MASP nullifier valid + ZKA note correctly created | | `bridge_namada_export` | `NAMADA_EXPORT_V1` | ZKA nullifier valid + MASP note correctly created | Namada bridge proofs use the canonical Noir / Barretenberg UltraHonk / BN254 circuit stack. The Rust crate `src/zka-bridge-namada` is the settlement integration boundary: it handles MASP/ZKA translation, IBC request and packet handling, VP state/event access, ordered public inputs, proof requests, and verifier envelopes for the Noir packages above. The SDK adapter `src/zka-sdk/src/bridges/namada.rs` wires this bridge into the common `SettlementBridge` interface. Any retained Groth16 code is non-canonical migration or test support; conforming Namada bridge deployments MUST verify the `NAMADA_IMPORT_V1` and `NAMADA_EXPORT_V1` UltraHonk/BN254 proof artifacts identified in §9.3. ### 7.3 Penumbra Bridge | Noir package | Circuit ID | Proves | |--------------|------------|--------| | `bridge_penumbra_import` | `PENUMBRA_IMPORT_V1` | Penumbra note consumed + ZKA note correctly created | | `bridge_penumbra_export` | `PENUMBRA_EXPORT_V1` | ZKA note consumed + Penumbra note correctly created | Translates between Penumbra value commitments and ZKA note commitments across field boundaries. Penumbra bridge proofs use the canonical Noir / Barretenberg UltraHonk / BN254 circuit stack. The Rust crate `src/zka-bridge-penumbra` is fresh settlement integration code for Penumbra RPC/view/custody boundaries, asset/nullifier mapping, transaction plans, receipt/event parsing, and Noir proof artifact loading. It is not the removed legacy Halo2/Pallas Penumbra bridge crate and MUST NOT depend on `halo2_proofs 0.3` or `zka-circuits`. The SDK adapter `src/zka-sdk/src/bridges/penumbra.rs` wires Penumbra into the common `SettlementBridge` interface. ### 7.4 Optional external shielded-pool settlement (deployment note) Distinct from the ZKA bridge circuits above (§7.1–§7.3), the native ZKA/Pay pool profile (§3.4), and Aztec-native deployment (§8), a conforming deployment MAY delegate settlement to an **external, immutable, operator-free shielded pool** holding a non-freezable asset. ZKA's own transfer circuits (§3) do not run in this mode; the host pool provides the shielded value transfer, and ZKA contributes only the additive ZKA/Coord (§4), ZKC (§5), and AFP federation layer off-chain. Identity therefore binds at the session/context level (§5.4), because host-pool notes are not ZKA notes. This optional delegation mode demonstrates settlement-rail independence; it is **not** the flagship ADAMAS production architecture. ADAMAS APL 0.4 selects the native Base WETH9 ZKA/Pay profile: AFP signs the USD obligation and exact wei settlement quote, while the immutable single-asset pool executes ZKA/Pay proofs. Railgun/BOLD references in the v0.7.1 history describe the superseded APL ADR-0011/0012 decision, not current deployment guidance. --- ## 8. Aztec Native Deployment Aztec native is a first-class deployment target for ZKA notes and transfers. ZKA/Coord v2 conformance is separate: an Aztec implementation MUST publish a manifest-bound execution profile and demonstrate the §4 claim with its own conformance evidence; kernel composition alone is insufficient. ### 8.1 ZKA Transfer as Aztec Private Function ```rust #[private] fn transfer( input_note: ZKANote, output_note: ZKANote, spending_key: SpendingKey, merkle_path: MerklePath ) { // ZKA note -> Aztec note stored in Aztec's note hash tree // Nullifier managed by Aztec's indexed nullifier tree // Kernel circuit handles proof composition } ``` ZKA notes become Aztec notes. Aztec's note hash tree replaces the protocol-level Merkle tree from §2.4. Aztec's indexed nullifier tree handles nullifier management from §2.3. Aztec contract code MUST NOT introduce pausable/upgradeable/admin paths over note validity (Safeguard 3). ### 8.2 ZKA/Coord via Aztec Kernel Circuits An Aztec Coord profile MAY represent a step as a private function call and use kernel proof composition. The profile circuit/adapter MUST still emit the canonical Coord v2 receipt, bind the exact Aztec program and artifact identity, enforce the workflow's per-step predicate/topology, and satisfy the same verifier-selected policy and completion checks. Until that profile passes the Coord v2 conformance gate, it MUST be labelled non-conforming experimental. The active Aztec workspace contains no built-in Coord contract: consumers use the canonical SDK verification flow and may delegate an Aztec proof only through an independently registered profile, exact artifact identity, and target-specific verifier implementation. Off-chain proof/VK material is never an Aztec fallback. ### 8.3 Key Mapping | ZKA Key | Aztec Key | |---------|-----------| | `masterSeed` | App-siloed nullifier secret `nsk_app` | | `spendingKey (sk)` | `H("zka/spending", nsk_app)` | | `viewingKey (vk)` | `H("zka/viewing", spendingKey)` | | `credentialBindingKey (cbk)` | `H("zkc/credential", nsk_app)` — sibling of `spendingKey` | | `address` | `H("zka/address", viewingKey)` | ### 8.4 Composable ZKC Compliance On Aztec, a single transaction can atomically combine a shielded ZKA transfer (§8.1) and an identity binding / predicate proof (§5) via the kernel circuit call stack, without revealing individual claims. Non-revocation checks run against Aztec public state without revealing which credential is checked. ### 8.5 Cross-Environment Interoperability Standalone EVM and Aztec-native deployments can share circuit-level field, hash, note, and receipt semantics where their manifests say so. A proof object is target- and VK-bound: an EVM UltraHonk proof is not assumed to verify in an Aztec kernel, or vice versa. Cross-environment interoperability requires an explicit adapter or independently generated target proof over the same canonical commitments. ### 8.6 Relationship to Aztec Primitives ZKA deliberately shares Aztec's cryptographic substrate — BN254, Poseidon2, UltraHonk/Barretenberg, and Noir — so that the Aztec-native deployment **delegates** to Aztec's primitives rather than reimplementing them. This subsection makes the delegation boundary explicit: what ZKA stops carrying on Aztec, and what it continues to add. It is descriptive, not a new normative requirement. **Delegated to Aztec (native deployment).** In an `aztec.nr` deployment, the host provides the following and ZKA does not carry its own: | ZKA mechanism (spec) | Provided on Aztec by | Notes | |----------------------|----------------------|-------| | Note commitment + Merkle tree (§2.3–§2.4) | Aztec note hash tree | ZKA notes become Aztec notes; the depth-32 Poseidon2 tree is replaced by Aztec's append-only note hash tree | | Nullifier set (§2.3, §11.5) | Aztec indexed nullifier tree | Non-membership and double-spend prevention are Aztec's | | Host proof composition | Aztec kernel circuits | Kernel aggregation is usable only inside an explicit Coord v2 profile; it does not itself establish §4 semantics | | Key hierarchy (§2.2) | Aztec key derivation | app-siloed `nsk_app` is the AFP-KDC seed; `sk` and `cbk` derive as siblings (§8.3) | | Proof system / field / hash (§2.1, §10) | Aztec native (Honk, BN254, Poseidon2) | Identical by construction; ZKA adopted this substrate in v0.5.0 (§2.1 migration note) | On Aztec, the ZKA settlement layer can delegate state primitives to the host. The Coord v2 layer does not reduce to naming conventions: it remains the profile-bound receipt, workflow, policy, and conformance contract defined in §4. ZKA does not claim an improvement over Aztec's host primitives. **Additive — no Aztec-native equivalent.** The following are ZKA/ZKC contributions that Aztec does not provide and that remain ZKA's own in every deployment, including the Aztec-native one: 1. **ZKC compliance / attestation (§5).** The seed-sibling credential binding (`cbk = H("zkc/credential", seed)`, §2.2), the ZKA-note ↔ ZKC-`holderCommitment` identity-binding circuit (§5.2), selective-disclosure predicates (§5.5, §6), the revocation accumulator and credential lifecycle (§5.6), and the `zka:bundle:v1` compliance envelope (§5.4). On Aztec these are implemented as contract logic using Aztec's app-siloed keys (§5.8), but Aztec specifies no equivalent compliance-credential system. 2. **Freedom Safeguards as invariants (§1.6).** Operator-blindness, unconditional exit, no admin/upgrade/pause path over note validity, and verifier accountability. These constrain how Aztec may be *used* rather than duplicating an Aztec feature: §8.1 requires that Aztec contract code introduce no pausable/upgradeable/admin path over note validity (Safeguard 3) — a discipline Aztec's contract model does not impose by default. 3. **Cross-environment compatibility (§8.5).** ZKA pins the note commitment scheme, asset-id encoding (ADR-0003), and domain separators so that the *same circuit semantics* are provable in each environment against that environment's verifier (the standalone Solidity `UltraVerifier` and the Aztec deployment), given a shared BN254 field. This is a compatibility contract at the circuit/field/hash level. It is **not** a claim that a single proof object verifies in both environments, that one note simultaneously occupies an Aztec tree and an external bridge tree, or that either verifier accepts the other's proofs; each environment keeps its own state, and value crosses between them only through the bridge circuits (§7). 4. **Settlement-rail independence.** ZKA's note/nullifier/proof model is specified independently of any settlement rail, so the same model runs Aztec-native, against a standalone EVM bridge (§7.1), or with no chain at all on an operator-run platform. Aztec's primitives are inseparable from the Aztec rollup; ZKA's are not bound to any one host. **Scope summary.** On Aztec specifically, ZKA's value is the additive set above — ZKC compliance, the Freedom Safeguards, and cross-environment note compatibility — layered on delegated Aztec machinery. ZKA's rail-independent primitives (its own tree, nullifier set, recursion plumbing, and the standalone verifier path) are justified by the non-Aztec deployments, not by the Aztec one. Aztec is treated as a first-class — and the most primitive-complete — deployment target; this subsection records that relationship so the boundary between delegation and addition is explicit rather than implied. --- ## 9. Circuit Specifications ### 9.1 Transfer Circuit | Parameter | Value | |-----------|-------| | Proving system | UltraHonk (Barretenberg) | | Curve | BN254 | | Target proving time | <5s (consumer hardware, browser WASM) | | Proof size | ~2 KB | | Solidity verification gas | <300k gas | | Verification time | <50ms (native), <100ms (browser) | ### 9.2 Coordination Circuit | Parameter | Value | |-----------|-------| | Receipt public inputs | 30 fields | | Final recursive public inputs | 33 fields | | Maximum predecessor fan-in | 4 | | Maximum recursive depth | 32 | | Maximum unrolled proof nodes | 4096 | | Proving time / memory / proof size / gas | Unclaimed until the §4 conformance benchmark gate publishes reproducible measurements | ### 9.3 Circuit Identifiers ```typescript const CIRCUIT_IDS = { // Core circuits — Barretenberg/UltraHonk/BN254 TRANSFER_V1: "zka:transfer:ultrahonk:bn254:v1", WITHDRAWAL_V1: "zka:withdrawal:ultrahonk:bn254:v1", ROOT_UPDATE_V1: "zka:root_update:ultrahonk:bn254:v1", COORD_RECEIPT_V2: "zka:coord:receipt:ultrahonk:bn254:v2", COORD_PROFILE_V2: "zka:coord:profile:ultrahonk:bn254:v2", COORD_RECURSIVE_V2: "zka:coord:recursive:ultrahonk:bn254:v2", BALANCE_PROOF_V1: "zka:balance:ultrahonk:bn254:v1", TRANSFER_MULTIASSET_V1: "zka:transfer:multiasset:ultrahonk:bn254:v1", // Immutable Base WETH9 ZKA/Pay pool profile PAY_DEPOSIT_V1: "zka:pay_deposit:ultrahonk:bn254:v1", PAY_TRANSITION_V1: "zka:pay_transition:ultrahonk:bn254:v1", PAY_BATCH_V1: "zka:pay_batch:ultrahonk:bn254:v1", PAY_WITHDRAWAL_V1: "zka:pay_withdrawal:ultrahonk:bn254:v1", // Bridge circuits NAMADA_IMPORT_V1: "zka:bridge:namada:import:ultrahonk:bn254:v1", NAMADA_EXPORT_V1: "zka:bridge:namada:export:ultrahonk:bn254:v1", PENUMBRA_IMPORT_V1: "zka:bridge:penumbra:import:ultrahonk:bn254:v1", PENUMBRA_EXPORT_V1: "zka:bridge:penumbra:export:ultrahonk:bn254:v1", // Credential / ZKC integration circuits CREDENTIAL_BINDING_V1: "zka:zkc:binding:ultrahonk:bn254:v1", TRANSFER_BINDING_V1: "zka:zkc:transfer_binding:ultrahonk:bn254:v1", // Selective disclosure circuits AGE_THRESHOLD_V1: "zka:disclosure:age_threshold:ultrahonk:bn254:v1", RANGE_MEMBERSHIP_V1: "zka:disclosure:range_membership:ultrahonk:bn254:v1", SET_MEMBERSHIP_V1: "zka:disclosure:set_membership:ultrahonk:bn254:v1", }; ``` Predicate circuits are NOT enumerated here: per Safeguard 1 the protocol carries no canonical predicate set. Predicate circuit identifiers are assigned by their independent providers and selected by a verifier's `trustPolicy`. ### 9.4 Noir Project Structure ``` noir/ ├── Nargo.toml # Workspace root (Noir v1.0, pinned) ├── zka_lib/ # Shared primitives (Poseidon2, key derivation, Merkle) ├── zka_transfer/ # ZKA/Pay transfer ├── zka_withdrawal/ # Bridge withdrawal ├── zka_root_update/ # Bridge root advancement (§3.3) ├── zka_balance/ # Balance proof (selective disclosure) ├── zka_coord_v2/ # Shared Coord v2 receipt constraints ├── zka_coordination_v2/ # Coord v2 canonical receipt validator ├── zka_coordination_v2_profile/ # Profile/policy-bound execution adapter ├── zka_coordination_v2_recursive/ # Sound bounded DAG composition ├── zka_coord_v2_sample_application/ # Profile-adapter conformance example ├── zka_credential/ # ZKC identity binding (§5.2) ├── bridge_namada_import/ ├── bridge_namada_export/ ├── bridge_penumbra_import/ └── bridge_penumbra_export/ ``` --- ## 10. Proof Format ### 10.1 UltraHonk Proof (Barretenberg) ```typescript interface UltraHonkProof { proof: Uint8Array; // Serialised UltraHonk proof bytes publicInputs: Field[]; // BN254 scalar field elements verificationKey: Uint8Array; // Serialised verification key } ``` Proofs are generated by Barretenberg (`bb prove`) or NoirJS (`noir_js` + WASM). Verification is performed on-chain by the auto-generated `UltraVerifier.sol` Solidity contract. ### 10.2 Solidity Verifier Interface ```solidity interface IUltraVerifier { function verify( bytes calldata proof, bytes32[] calldata publicInputs ) external view returns (bool); } ``` The verifier is generated per-circuit via `nargo codegen-verifier`. Each circuit has its own deployed `UltraVerifier` instance. Verifier contracts MUST be non-upgradeable and admin-free (Safeguard 3). --- ## 11. Security Considerations ### 11.1 Cryptographic Assumptions | Assumption | Implication if Broken | |------------|----------------------| | Discrete log (BN254) | Spending key recovery | | Poseidon2 collision resistance (BN254 field) | Note forgery / identity binding forgery | | UltraHonk soundness (Barretenberg) | Invalid proofs accepted | | Noir ACIR compilation correctness | Circuit constraints deviate from spec | | Accumulator security | Revoked credentials appear valid | | Master seed secrecy | Full compromise of both ZKA and ZKC identities | ### 11.2 Implementation Requirements | Requirement | Rationale | |-------------|-----------| | Constant-time nullifier queries | Prevent timing attacks | | Secure randomness for note creation | Prevent commitment grinding | | Atomic nullifier recording | Prevent double-spend race conditions | | Merkle root recency checks | Prevent stale state attacks | | Pinned Noir + Barretenberg versions | Prevent breaking changes in proof format | | No admin/pausable/upgrade paths over notes | Enforce Safeguard 3 | | Unconditional unshield/withdraw | Enforce Safeguard 5 | ### 11.3 Privacy Limitations | Limitation | Mitigation | |------------|------------| | Timing correlation | Randomized delays, batching | | Transaction graph (long-term) | Dummy transactions | | Note value distribution | Standardized denominations | | Metadata leakage | Encrypted memos | | Cross-chain correlation | Bridge batching, timing randomization | | Disclosure accumulation over time | Minimal disclosure default (Safeguard 2), per-session fresh binding | ### 11.4 Bridge Security | Risk | Mitigation | |------|------------| | Bridge contract compromise | Multi-sig governance of bridge liquidity only — never of note validity | | Cross-chain replay | Chain-specific context binding | | Asset mismatch | Strict asset ID verification | | Aztec kernel upgrade | Version-pinned circuit IDs | ### 11.5 Nullifier Storage Requirements Nullifier storage MUST provide atomic append with at-least-once delivery guarantees. Event-sourced stores (e.g. KurrentDB/EventStoreDB) satisfy this; the nullifier event log doubles as a compliance audit trail. In Aztec native deployments, Aztec's indexed nullifier tree provides these guarantees natively. ### 11.6 Post-Quantum Considerations (Draft) BN254 discrete-log and the UltraHonk proving system are not post-quantum secure. A future revision will specify a PQ migration path (hash-based commitments are Poseidon2 and remain conjecturally PQ-resistant for collision; key derivation and proof system are the migration surface). This remains open work for a future revision. --- ## 12. Conformance ### 12.1 ZKA Conformance Levels | Level | Requirements | |-------|--------------| | **ZKA-Core** | Transfer proofs, nullifier tracking, canonical Merkle append/root handling, note discovery, non-custodial key ownership, Safeguards 3 & 5 | | **ZKA-Coord** | Core + Coord v2 profile-bound receipt, recursive DAG proof, verifier policy, and completed conformance gate (§4) | | **ZKA-Full** | Coord + viewing keys + selective disclosure | | **ZKA-MultiChain** | Full + Namada/Penumbra/Aztec bridges | | **ZKA-Compliant** | MultiChain + ZKC integration (§5), all six Safeguards | | **ZKA-Aztec** | Full Aztec native deployment plus an independently conforming manifest-bound Coord v2 Aztec profile | The **ZKA-Coord** level requires the exact soundness claim and evidence gate of §4. The reference profile is bounded to fan-in 4, depth 32, and 4096 unrolled proof nodes; no target may claim unbounded depth or infer conformance from host proof composition alone. Another proving mechanism can conform only through an explicit manifest-bound profile with equivalent receipt, workflow, policy, data-flow, completion, and adversarial-verification evidence. **Bridge-deploying conformance.** Any conformance level claimed by a deployment that operates a ZKA Ethereum bridge MUST additionally deploy a `zka_root_update` verifier (§3.3) referenced by the bridge contract as `VERIFIER_ROOTUPDATE`, and the bridge MUST satisfy all six invariants of §7.1. ### 12.2 ZKC Integration Conformance Levels ZKC conformance levels are owned by the **ZKC Specification v0.4 §9.1**. The core ladder is strictly monotonic — `ZKC-Core` → `ZKC-Standard` → `ZKC-Full` (Full is the highest core level) — and `ZKC-Agent` is an orthogonal extension profile, **not** a level above `ZKC-Full`. `ZKC-Core` includes KYC proofs, the credential rebind circuit (`zkc:proof:credential_rebind:v1`), and basic verification. This spec does **not** re-declare a parallel `Integration-*` taxonomy; a deployment integrating ZKC MUST conform to the ZKC v0.4 §9.1 level it claims, and tooling MUST treat the level name as ZKC-namespaced rather than inferring rank from the ZKA taxonomy. The ZKA-side requirements that gate the `ZKA-Compliant` level (§12.1) are: shared key hierarchy (§2.2), identity binding circuit (§5.2), compliance bundle envelope (§5.4), and all six Freedom Safeguards (§1.6). A deployment claiming `ZKA-Compliant` MUST satisfy all six Freedom Safeguards (§1.6); they are not gated by conformance level. ### 12.3 Test Vectors Test vectors are provided in the companion `zka-test-vectors` repository, generated over the **BN254 scalar field** using Poseidon2. v0.5.0 circuit vectors remain valid for v0.6.0 (no field/hash change). New vectors are added for the identity binding and combined transfer+binding circuits. --- ## Appendix A: UltraHonk Proof Format ```typescript interface UltraHonkProof { proof: Uint8Array; // ~2 KB serialised proof publicInputs: Field[]; // Circuit-specific public inputs (BN254 field elements) verificationKey: Uint8Array; // ~3 KB serialised VK } interface RecursiveProofInputs { proof: Field[]; // Proof as field array for std::verify_proof verificationKey: Field[]; // VK as field array publicInputs: Field[]; } ``` --- ## Appendix B: Poseidon2 Parameters (BN254) ``` Poseidon2 Configuration for ZKA ================================ Field: BN254 scalar field (r = 21888242871839275222246405745257275088548364400416034343698204186575808495617) State width: 4 Rate: 3 field elements Capacity: 1 field element Full rounds: 8 Partial rounds: 56 S-box: x^5 Length IV: message_length * 2^64 in state[3] Library: noir-lang/poseidon v0.3.0 Poseidon2::hash, backed by Noir/Aztec/Barretenberg Poseidon2 ``` --- ## Appendix C: ZKA-ZKC Integration Provenance The normative content of §5 is ported from `docs/archive/zka_zkc_integration_v1_0_0.md` (2026-01-19, Halo 2 era). Mapping: | v1.0.0 integration spec | This spec | |-------------------------|-----------| | §2 Shared Key Hierarchy | §2.2 (unchanged derivation) | | §2.3 Binding Proof / §5.1 Identity Binding Circuit | §5.2 (Halo 2 → UltraHonk/BN254) | | §4.2 Compliance Bundle / verifyComplianceBundle | §5.4 (ZKA envelope `zka:bundle:v1` wrapping ZKC `zkc:proof:v1` or `zkc:proof:v2`) | | §5.2 Combined Transfer + Binding | §5.3 | | §7 Conformance Levels | §12.2 (defers to ZKC v0.4 §9.1) | | §8 Roadmap (Phases 1–4) | folded into §5.2–5.7 status markers | Freedom Safeguards (§1.6) incorporate the intent of the historical design memo `docs/archive/zka_zkc_freedom_safeguards_v1_0_0.md` (2026-04-01), which proposed embedding Safeguards 3 & 5 as ZKA invariants and 1, 2, 4 as integration invariants. The memo is retained for provenance only; this specification is the canonical, normative source for the current six-safeguard set. Structural requirements (§5.6–5.7, §11.6) are drawn from the ZKA/ZKC/APL investigation-priorities analysis (2026-03-26; tracking file since retired) and marked Draft where only the requirement (not the mechanism) is settled. The selective-confidentiality framing is from `selective-disclosure-compliance-infrastructure.md` (2026-05-13). ### C.1 Reconciliation with ZKC v0.4 ZKC Specification **v0.4** is the current ZKC integration target. It retains linkable `zkc:proof:v1` and adds `zkc:proof:v2` with anonymous and scoped presentation profiles. ZKA uses the same `cbk = H("zkc/credential", masterSeed)`, scoped domain separator, anonymous zero sentinel, Poseidon2/BN254 field encoding, proof-type/version alignment, replay context, issuance, and revocation rules. ZKA adds only the note-ownership conjunction in `zka_transfer_binding_v2`; it does not redefine ZKC predicate verification. ZKA-side reconciliation: `zka:bundle:v1` wraps both ZKC envelope families and dispatches verification by the exact `(envelopeVersion, presentationProfile, proofType)` tuple. Payment bindings accept v1/linkable or v2/scoped only; payment-free work attestations may carry v2/anonymous because identity remains session/context-bound. The legacy v1 circuit and wire shape remain accepted unchanged. **Precedence:** Per ZKC v0.4 §1.4, where ZKC and ZKA §1.6 differ in wording on a Freedom Safeguard, the safeguard *intent as stated in ZKA §1.6 governs*. ZKA §1.6 is the authoritative source for safeguard intent; ZKC mirrors it. **Previously-flagged ZKC-layer divergences — all resolved in ZKC v0.2.2:** 1. **Canonical proof-type registry (§5.5).** ✅ Resolved. ZKC v0.2.2 reframes Appendix A as a "Well-Known Identifier Registry (Non-Exclusive)" and §3.1–3.6 as reference (non-mandatory) schemas, with normative text that no type/provider is privileged or required and that the protocol defines no mechanism for a canonical set. Consistent with Safeguard 1. 2. **Open-Source Predicate mandate (Safeguard 4).** ✅ Resolved. ZKC v0.2.2 §1.4 adds Safeguard 4 normatively (open-source/auditable circuits, versioned publication with public review period, SDK refusal of opaque predicates by default with logged opt-in). ZKC v0.2.2 §1.4 also adds Safeguard 2 (Minimal Disclosure). 3. **Conformance hierarchy semantics.** ✅ Resolved. ZKC v0.2.2 §9.1 is now a strictly monotonic ladder (`ZKC-Core` → `ZKC-Standard` → `ZKC-Full`, Full highest) with `ZKC-Agent` as an orthogonal extension profile, not a level above Full. **Notation reference (resolved by KDC).** Both specifications reference AFP-KDC for hashing, domain separation, and seed-to-key derivation rather than a ZKA-versioned derivation. **AFP-KDC v1.0.2 §A.3** resolves the former domain-tag prose ambiguity in favor of the deployed Blake2b-512 little-endian field-tag derivation; it is byte-for-byte compatible with the existing tag constants and vectors. The derivation facts cited above (`cbk = H("zkc/credential", masterSeed)`, `H("zkc/binding", cbk)`, Poseidon2/BN254) are defined by AFP-KDC §A.6 and are cited here, not redefined. **Credential rebind reference (added by ZKC v0.2.4, carried in ZKC v0.4).** ZKC v0.4 §4.5 defines `zkc:proof:credential_rebind:v1` as a ZKC-Core circuit that proves holder-controlled migration from `oldHolderCommitment` to `newHolderCommitment` without revealing either seed, either `cbk`, any ZKA address, any spending key, or payment activity. ZKA does not duplicate that circuit. ZKA's obligations are to keep note migration private via self-transfer batching (§5.7), keep any issuer-retained old→new holder-commitment mappings sealed and minimized (§5.7), and ensure rebind proofs never affect ZKA note validity, custody, transferability, withdrawal, nullifier semantics, or revocation semantics. --- ## Document History | Version | Date | Changes | |---------|------|---------| | 0.9.0-draft | 2026-08-17 | Corrected the legacy `zka_root_update` blob-binding claim: its non-zero `blobKzgCommitment` field is only a proof-bound tag, private `blobCommitments` are not constrained to blob contents, and the immutable Base APLBridge does not execute `BLOBHASH`. Successor blob-backed settlement requires both same-transaction `BLOBHASH` verification and a proof relation from canonical blob contents to the folded leaf list. Specification/comment correction only; no legacy circuit, verification key, deployed bytecode, or test-vector change. | | 0.9.0-draft | 2026-08-15 | **Family epoch pins ZKM's cryptographic surface, not its revision label.** Epoch `zka:protocol-family:2026-08-15.2` adds four ZKM projection assertions over `protocol-family/zkm-statement-registry-v1.json`: its `statement_set_id` (a SHA-256 over ZKM's projected statement/retired-statement/revocation/toolchain surface, with prose fields excluded by construction), its `last_breaking_revision`, its `family_epoch` back-reference, and the registry's existence. The `2026-08-15.1` revision label discriminated one ZKM revision from another but pinned no verification key, public-input vector, bytecode digest, or toolchain; the digest does. The binding is bidirectional — ZKM's own gate requires the family's pinned label to be at least its last breaking revision. The family pins the registry's `statement_set_id` FIELD and deliberately not the file's digest, since the file carries prose that is not protocol surface. No ZKA circuit, hash, bundle, statement, ABI, or test vector changes. | | 0.9.0-draft | 2026-08-15 | **§5.4.1 reconciled with ZKM rev 10.** ZKM replaced its depth-128 low-half revocation tree with the depth-254 full-identifier exclusion tree and re-versioned both composed statements, so §5.4.1 now selects `zkm:spend-conjoined:v2` over a 14-element vector (adding `revocationScheme`, which MUST equal `2`, between `revocationRoot` and `currentTime`) and `zkm:mandate-only:v2` over its 9-element vector. Adds the fail-closed statement-version boundary: a post-cutover registry MUST omit the 13-/8-field v1 statements and MUST NOT reinterpret, upgrade, or cross-verify their proofs, and only scheme-2 revocation roots at or after the ZKM activation epoch may be recognized. Reference boundaries `contracts/ethereum/ZKMAtomicSettlement.sol` and `src/zka-sdk/src/zkm/mod.rs` updated with the new element and a deterministic scheme-mismatch rejection. Published protocol-family epoch `zka:protocol-family:2026-08-15.2`, whose ZKM pin carries the revision label because ZKM's version string does not change between revisions. No ZKA circuit, hash, bundle, or test vector changes. | | 0.9.0-draft | 2026-08-14 | Adopted AFP-KDC v1.0.2 as the current normative authority for the byte-identical domain registry, Poseidon2 parameters, and seven-case raw-sponge KAT package. No derivation, domain field, circuit, proof, verification key, or `afp-kdc-v1.0.1` artifact/profile identity changed. Published protocol-family epoch `zka:protocol-family:2026-08-14.1`. | | 0.9.0-draft | 2026-08-12 | Corrected all canonical derivation paths to AFP-KDC v1.0.1: unsigned big-endian seed reduction, seed-sibling `cbk`, and `holderCommitment` under `zkc/binding`. Corrected circuits, VKs, proofs, and verifier artifacts have explicit profile identities; legacy identities remain hash-pinned for custody migration. | | 0.1.0 | 2025-12-04 | Initial specification | | 0.2.0 | 2025-12-23 | Orchard-native design | | 0.3.0 | 2026-01-13 | Settlement-agnostic redesign, L2 bridge reference | | 0.4.0 | 2026-01-19 | Abstract settlement interface, credential binding key, Namada/Penumbra bridges, ZKC integration (stub) | | 0.5.0 | 2026-03-06 | Noir/UltraHonk circuit layer (replaces Halo 2), BN254 field (replaces Pallas), Poseidon2, Aztec native deployment, updated circuit IDs, test vectors regenerated | | 0.6.0 | 2026-05-15 | Normative ZKA/ZKC integration (§5: identity binding circuit, compliance bundle, combined circuit, predicate model, credential lifecycle, structural requirements); Freedom Safeguards as protocol invariants (§1.6); selective-confidentiality framing; post-quantum considerations. No circuit-layer or test-vector change vs v0.5.0. Reconciled with ZKC v0.2.2 (all three prior ZKC-layer divergences resolved; see Appendix C.1). | | 0.6.0 (clarif) | 2026-05-26 | **Additive clarifications — no invariant, circuit-layer, or test-vector change.** §1.4: Leo conformance is invariant-level, not value/proof-level. §4.1: Leo/Aleo deployment carve-out (no in-circuit recursion → Varuna call-graph composition; scoped Coord; see `docs/sdd/sdd_coord_aleo.md`). §12.1: ZKA-Coord is mechanism/environment-dependent — new tooling `MUST NOT` re: §9.2 unbounded recursive depth on backends lacking in-circuit recursion. Whether to bump to v0.6.1 is a separate maintainer decision (see tracking req `req_zka_spec_ratify_leoaleo_conf_d6406b19`). | | 0.6.1 | 2026-06-04 | Key derivation relocated to the Key Derivation Core (KDC v1.0.0, AFP Specification Appendix A) and referenced normatively from §2.1–§2.2; §5.2 in-circuit derivation annotated as KDC-defined; §1.3 records the ZKA→AFP-KDC dependency; Appendix C.1 records the KDC notation reference. Relocation only — KDC v1.0.0 is behaviorally identical to v0.6.0 §2.2; no circuit, field, hash, or test-vector change; existing `zka-test-vectors` seed→key vectors remain normative. | | 0.6.2-draft | 2026-06-05 | **§5.4: optional `coord_proof` member added to `zka:bundle:v1`** — carries a recursive ZKA/Coord work-attestation proof (§4) + its public inputs (`workflowId`, `inputCommitments`, `outputCommitment`, `stepIndex`, `context`) alongside the existing transaction/compliance/binding proofs; `verifyComplianceBundle` gains an optional step 6 (recursive verification + `context == metadata.context` binding). Backward-compatible and additive: the member is OPTIONAL, existing bundles are unaffected, and the envelope version string stays `zka:bundle:v1` (no envelope-version bump). Satisfies the ZKA-side follow-up flagged in **AFP v0.3.1 §12.7** (ADR-0003) and supersedes VXP's `vxp:coordbundle:v1`. No circuit-layer, field, hash, or test-vector change — the ZKA/Coord circuit (§4) is unchanged; only the bundle envelope gains an optional carriage slot. | | 0.6.2-draft | 2026-06-05 | **§5.4: payment `transaction` member made OPTIONAL** (and `binding` optional, present IFF `transaction` is) — a valid bundle MUST carry at least one of `{transaction, coord_proof}` (an `EMPTY_BUNDLE` is invalid); `compliance` proofs only accompany a substantive proof. Adds the **pure work-attestation bundle** profile (`coord_proof`, no payment) for the non-monetary federation flows of **AFP §12.7 / ADR-0003** — PRP reconciliation and non-monetary VXP `vxp.reciprocal` — which carry evidence over `zka:bundle:v1` with no payment leg. `verifyComplianceBundle` verifies each member IFF present and gates the note↔credential consistency checks on `transaction` presence; when `transaction` is absent the note↔credential binding is N/A and identity is anchored over the present proofs via the shared `context` (which AFP equates to the envelope `session_context`) — no new crypto. **Version-string decision:** the envelope string stays `zka:bundle:v1` — this is a relaxation of a still-draft, pre-release envelope (no released consumer pins a required `transaction`), existing payment bundles remain valid byte-for-byte, and a transaction-absent bundle is simply a new instance shape; a `v2` bump would falsely signal a breaking wire change. Backward-compatible; no circuit-layer, field, hash, or test-vector change. | | 0.6.2-draft | 2026-06-06 | **§5.7: Note Discovery Protocol finalized from Draft placeholder** using an append-only encrypted note announcement log with optional viewing tags; recipients discover notes with incoming viewing keys and indexers remain non-authoritative. **§1.6 / §5.7: Agent Key Custody Model added as a non-custodial protocol invariant**: ZKA conformance cannot require platform custody of `masterSeed`, spending keys, or note plaintext; hosted custody belongs to separate deployment/product profiles. No circuit-layer, field, hash, or test-vector change. | | 0.6.2-draft | 2026-06-06 | **§2.4: Merkle Tree State Management finalized** with canonical append source, deterministic leaf ordering, commitment append events, accepted root windows, stale-root errors, and non-authoritative indexer/client reconstruction. No circuit-layer, field, hash, or test-vector change. | | 0.6.2-draft | 2026-06-06 | **§5.4: context binding comment generalized** from the payment-verifier shape to a deployment/profile-defined replay context; under AFP it equals `afp:envelope:v1.session_context`. Adds explicit note that the AFP §12.7 ZKA-side dependency is resolved by the optional `coord_proof` member. No circuit-layer, field, hash, or test-vector change. | | 0.6.2-draft | 2026-06-06 | **§5.7: Key Rotation / Recovery requirements refined**: SDKs must provide bucketed rotation batching policies, and ZKA deployments using issuer-assisted credential rebinding must require sealed/minimized issuer audit mappings for old→new holder-commitment links. No circuit-layer, field, hash, or test-vector change. | | 0.6.3-draft | 2026-06-08 | **§3.3 (NEW): Root Update Circuit (`zka_root_update`)** — public/private inputs, six normative constraints; **§7.1 promoted to a normative subsection** with six conforming-bridge invariants (ownerless, immutable config, append-only state, root-update SNARK, permissionless force-exit, EIP-4844 blob DA); **§9.3** adds `ROOT_UPDATE_V1` circuit identifier; **§9.4** adds `zka_root_update/` package; **§12.1** adds bridge-deploying conformance clause. No change to existing circuits, field, hash, or test vectors. | | 0.6.4-draft | 2026-06-10 | **§7.1 standalone force-exit normatively specified** (closes the bridge-state/proof-semantics gap of investigation item 3): §7.1.1 bridge-held state enumeration (append log, accepted root set + immutable window config, nullifier set, immutable/version-pinned verifier references, per-asset escrow with solvency invariant, deployment context); §7.1.2 force-exit = ordinary `zka_withdrawal` proof with fixed verification order, effects-before-interactions nullifier recording, **no challenge period** for pure shielded-pool exits, permissionless caller model; §7.1.3 root publication model (append + root-update events; full reconstruction from L1 history); §7.1.4 machine-readable failure-code taxonomy with retryable/terminal classes (stale root retryable, never invalidates a note); §7.1.5 client-retained witness data sufficient to exit with all operator services down; §7.1.6 Aztec Outbox/portal path explicitly separate (ADR-0002). **ZKC v0.2.4 alignment:** §5.7 now references the ZKC-Core credential rebind circuit (`zkc:proof:credential_rebind:v1`), treats issuer migration tokens as optional optimizations, and preserves sealed/minimized issuer audit mappings as the ZKA-side privacy requirement. No ZKA circuit-layer, field, hash, or test-vector change. | | 0.6.4-draft | 2026-06-12 | **Aleo/Leo target retired (ADR-0009).** Removed the §1.4 Leo conforming-target language, the `"aleo"` `SettlementType`/`AssetId` variant (§2.1), the §4.1 Leo/Aleo coordination carve-out, and Aleo mentions in §1.6 Safeguards 4–5, §5.4, §5.7, §12.1. Aleo design docs (`sdd_leo_pay.md`, `sdd_bridge_aleo.md`, `sdd_coord_aleo.md`) archived under `docs/archive/`. Implementation tree drops the `leo/` programs, the Aleo finality oracle + EVM claim anchor, the `[aleo]` settlement-ops chain kind, and the aleo registry/certification entries. No circuit-layer, field, hash, public-input, or test-vector change. | | 0.6.4-draft | 2026-06-10 | **Namada/Penumbra bridge implementation boundary aligned with completed Rust/SDK work**: §7.2–§7.3 identify the active Noir package names (`bridge_namada_*`, `bridge_penumbra_*`) while preserving the existing circuit IDs; §7.2 documents `src/zka-bridge-namada` plus `src/zka-sdk/src/bridges/namada.rs` as the Namada integration boundary around UltraHonk/BN254 artifacts; §7.3 documents the fresh `src/zka-bridge-penumbra` crate plus SDK adapter and explicitly excludes the removed legacy Halo2/Pallas crate path. §9.4 updates the Noir project tree. No circuit-layer, field, hash, public-input, circuit-ID, or test-vector change. | | 0.7.0 | 2026-06-13 | Added **Safeguard 6 (Verifier Accountability)** to §1.6 as a normative invariant and audit-table row — the first safeguard constraining the counterparty rather than the operator, orthogonal to operator-blindness. The mechanism is specified normatively on the ZKC side (ZKC §2.2 purpose-bound proof context, §5.5 Authenticated Proof Request, §5.6 holder-custodied Disclosure Receipt, §5.7–§5.8); ZKA records the invariant and extends the non-custody clause to receipts. No change to notes, bundles, circuits, fields, hashes, the `zka:bundle:v1` envelope, or any test vector. | | 0.7.1-draft | 2026-06-15 | **§8.6 (NEW): Relationship to Aztec Primitives** — documents the delegation boundary (Aztec note hash tree, indexed nullifier tree, kernel-circuit recursion, key hierarchy, and the shared Honk/BN254/Poseidon2 substrate provide ZKA's §2–§4 machinery in the native deployment) versus the additive layer with no Aztec-native equivalent (ZKC compliance §5, Freedom Safeguards §1.6, cross-environment compatibility §8.5, settlement-rail independence). Clarifies §8.5 as a circuit/field/hash-level compatibility contract, not a one-proof-verifies-in-both claim. **§7.4 (NEW): Operator-free external shielded-pool settlement (deployment note)** — a deployment MAY delegate settlement to an external immutable operator-free shielded pool (e.g. Railgun) holding a non-freezable asset (e.g. Liquity BOLD/LUSD); ZKA's §3 circuits do not run, ZKA contributes the additive Coord/ZKC/AFP layer off-chain, identity binds at the session/context level (§5.4). Reflects the resolved ADAMAS settlement architecture (APL ADR-0011/0012); the §8.6 delegation principle applied to a non-ZKA-native host. Descriptive / deployment-layer; no circuit-layer, field, hash, envelope, or test-vector change. Also retargets ZKC cross-references from v0.2.4 to the co-published v0.3.0 (incl. Appendix C.1 reconciliation, bilateral Safeguard-6 alignment). | | 0.8.0-draft | 2026-07-11 | Makes the immutable Base WETH9 ZKA/Pay single-asset pool (§3.4) the flagship ADAMAS APL 0.4 production direction. AFP owns USD obligation pricing and signs the exact wei quote; ZKA/Pay adds no repricing oracle or stablecoin authority. Additional assets require separate immutable pools and explicit risk profiles; a native stable unit remains independent future work. §7.4 is retained as an optional external-host mode but no longer describes the resolved ADAMAS architecture. No existing circuit identifier, public-input shape, hash, bundle, or published test vector changes. | | 0.8.1-draft | 2026-08-04 | Adds the informative ZKM authorization/settlement boundary and states explicitly that current `zka:bundle:v1` has no ZKM carriage member. ZKM remains optional and cannot affect ZKA note validity, custody, transferability, or unconditional exit. Future bundle carriage and atomic settlement remain separately tracked integration work. No circuit, contract, field, hash, envelope, or test vector changes. | | 0.9.0-draft | 2026-08-05 | Makes ZKA/Coord v2 the canonical pre-launch coordination draft, explicitly unaudited until its signed independent-review gate passes; defines its fixed execution receipt, verifier-selected profile policy, manifest/VK and assurance binding, sound predecessor data-flow/DAG semantics, final recursive ABI, deterministic errors, and evidence gate; removes the pre-launch artifacts, registry/API surface, and five-field bundle object; keeps public Rust SDK names unversioned and security identities explicitly v2; leaves `zka:bundle:v1` independently versioned. | | 0.9.0-draft | 2026-08-05 | **§5.4.1: ZKM direct authorization settlement.** Selects `zkm:spend-conjoined:v1` direct settlement rather than extending `zka:bundle:v1`; fixes the 13-field ABI and context/asset/root/clock/VK checks; requires one mandate-nullifier set across spend-conjoined and mandate-only VKs; atomically commits both nullifiers, one mandate successor, and two ZKA outputs; keeps mandate-only VKs structurally unable to move value and keeps withdrawal/force-exit independent of mandate revocation. Adds Solidity and Rust reference boundaries with success, rollback, mismatch, cross-VK replay, mandate-only, and unconditional-exit tests. No existing bundle, ZKA circuit, hash, or test vector changes. | | 0.9.0-draft | 2026-08-05 | Adopts ZKC v0.4 presentation profiles in `zka:bundle:v1`: preserves linkable v1, adds anonymous/scoped v2 carriage and deterministic verifier selection, requires scoped v2 for payment bindings, and adds `zka_transfer_binding_v2` so note ownership and credential control share a private master seed without exposing `holderCommitment`. | --- ## License This specification is released under the Apache 2.0 License. ### Patent Non-Assertion Covenant The Apache 2.0 patent grant (License §3) covers patent claims necessarily infringed by a contribution alone or by its combination with the Work it was submitted to. An independent implementation of this specification — one written from the specification text without deriving from any reference code — falls outside that scope, and so is not clearly covered by that grant. The following covenant, modeled on the Open Web Foundation Agreement (OWFa) 1.0 non-assert and the patent commitment of the Community Specification License 1.0, closes that gap. By submitting a contribution to this specification (for example, a pull request modifying this document), each contributor accepts and makes this covenant for every version of this specification that incorporates its contribution. Each contributor to this specification irrevocably covenants, on behalf of itself and its successors and assigns, not to assert any Essential Claims against any party for making, having made, using, selling, offering for sale, importing, or distributing a Conformant Implementation of this specification. **Essential Claims** are patent claims owned or controlled by the contributor — now or in the future, including claims later acquired — that are necessarily infringed by implementing the required portions (those designated MUST, REQUIRED, or SHALL) of a version of this specification to which the contributor contributed. "Necessarily infringed" follows the W3C Patent Policy definition of Essential Claims: it applies only where no commercially reasonable non-infringing alternative exists, and it excludes claims covering implementation choices this specification leaves open and technologies this specification merely references rather than describes in detail. A **Conformant Implementation** is any implementation that conforms to those required portions — whether or not it derives from any contributor's code or specification text; where an implementation forms part of a larger work, this covenant extends only to the portions that so conform. This covenant is royalty-free and worldwide, and supplements — never limits — the rights granted under the Apache 2.0 License. This covenant runs with the Essential Claims: it is intended to bind any future owner, assignee, or exclusive licensee that acquires the right to enforce them, and a contributor that transfers a patent containing Essential Claims satisfies this obligation by notifying the transferee of this covenant. The covenant is suspended with respect to any party that asserts a patent infringement claim alleging that a Conformant Implementation of this specification infringes that party's patents (excluding claims brought defensively in response to a prior such assertion by a contributor or any successor or assignee bound by this covenant), for as long as that assertion is maintained. --- *ZKA: Provable confidentiality for autonomous agents.*