Aqua ProtocolAqua Protocol
Aqua Protocol
Aqua ProtocolAqua Protocol, an open protocol for verifiable data provenance and accountability. Developed in the open as a proposed standard, with public specification, documentation, and reference implementations.

Documentation

Welcome to Aqua ProtocolQuick StartCore ConceptsRelease Status & Versioning
Developer Onboarding
aqua-rs-sdk-coreTemplate RegistryAudit Trails for AI Agents
Protocol Reference
Protocol ReferenceData ModelHashing and CanonicalizationTemplatesSignaturesAnchors and LinksSelective DisclosureVerification
Use Cases
Aqua Protocol Use CasesDocument VerificationIdentity AttestationSupply Chain Tracking

Documentation

Welcome to Aqua ProtocolQuick StartCore ConceptsRelease Status & Versioning
Developer Onboarding
aqua-rs-sdk-coreTemplate RegistryAudit Trails for AI Agents
Protocol Reference
Protocol ReferenceData ModelHashing and CanonicalizationTemplatesSignaturesAnchors and LinksSelective DisclosureVerification
Use Cases
Aqua Protocol Use CasesDocument VerificationIdentity AttestationSupply Chain Tracking
Docs
Schema Reference
Selective Disclosure

Selective Disclosure

Field-level redaction and selective trees: revealing a verifiable subset of an Aqua tree

11 min read

Selective disclosure lets a tree's holder reveal a verifiable subset of its content: hidden fields are replaced by salted commitments, and a verifier confirms — with no side channel and no trusted party — that what was disclosed is exactly what the original revision hashes commit to.

Disclosure operates at two levels:

  • Field level — a single tree-method revision is replaced by a redacted revision whose leaves are individually disclosed or sealed.
  • Tree level — a whole tree is exported as a selective tree in which each revision is fully disclosed, field-redacted, or hidden.
Normative source

The protocol specification is authoritative; this page is the readable reference. Selective disclosure is specified in 06-selective-disclosure.md.

Precondition: tree method

Only revisions with method: "tree" support field-level redaction — the per-leaf salted commitments of the hashing model are the disclosure mechanism. A scalar revision hashes as one opaque blob and can only be disclosed in full or hidden; a request to field-redact a scalar revision MUST be rejected.

The disclosure unit is the leaf: one entry of the revision's canonical pointer map, including the container-marker leaves (the root "", every object node as {}, every array node as []). Subtree redaction is expressed by redacting every leaf under the subtree's pointer prefix; whole-revision hiding is a tree-level directive.

The redacted revision

Code
json
1{
2 "revision_hash": "0x1620…",
3 "leaf_count": 19,
4 "leaves": [
5 {
6 "type": "Disclosed",
7 "index": 3,
8 "path": "/payloads/signer_did",
9 "value": "\"did:key:z6Mk…\"",
10 "salt": "0x<64 hex>"
11 },
12 {
13 "type": "Redacted",
14 "index": 5,
15 "path": "/payloads/prompt_text",
16 "value_commit": "0x<64 hex>"
17 }
18 ]
19}

(Example elided: a real artifact carries all 19 leaf entries.)

FieldMeaning
revision_hashThe original revision's full multihash. The hash algorithm is recovered from its multicodec; a redacted revision carries no algorithm field.
leaf_countThe total number of leaves of the original revision. MUST be at least 1.
leavesOne entry per leaf. Every leaf of the original MUST appear, disclosed or redacted.

Leaf entries are discriminated by type:

TypeFieldsMeaning
Disclosedindex, path, value, saltindex is the leaf's zero-based position in canonical order; path is the RFC 6901 pointer; value is the compact JSON rendering of the flattened value, carried as a JSON string (a string value keeps its quotes, e.g. "\"alice\""; a number is bare, e.g. "42"; container markers are "{}" / "[]"); salt is the 32-byte per-leaf salt, 0x + lowercase hex.
Redactedindex, path, value_commitpath is always cleartext, including on redacted leaves. value_commit = `HASH(0x02

Unknown members of the redacted-revision container MUST be rejected.

What redaction cannot hide

The pointer paths and leaf_count are cleartext by design (the path is authenticated into the leaf hash). A redacted revision therefore necessarily reveals:

  • the complete field-name structure of the revision;
  • array cardinalities (element pointers are visible);
  • which optional fields were populated (absent fields produce no pointer);
  • the total leaf count.

Disclosure policies must treat field presence as public.

Verifying a redacted revision

A verifier MUST, in order:

  1. Count — leaves has exactly leaf_count entries; reject a leaf_count of 0.

  2. Index bijectivity — the index values are exactly 0 … leaf_count − 1: none out of range, none duplicated, none missing.

  3. Algorithm recovery — decode revision_hash under the strict multihash rules; reject any malformation.

  4. Leaf reconstruction — for each entry, in index order:

    Code
    text
    1Disclosed: leaf = HASH(0x00 || HASH(0x03 || path) || HASH(0x02 || salt || value))
    2Redacted: leaf = HASH(0x00 || HASH(0x03 || path) || value_commit)

    For redacted leaves the label is recomputed from the cleartext path; the commitment is taken as supplied.

  5. Root equality — reduce the leaves with the Merkle construction, wrap the root as a multihash, and require byte equality with revision_hash.

Any tampering — a changed value, salt, commitment, path, count, or index — surfaces as a root mismatch or an index/count violation. There is no finer-grained attribution, and none is needed: the guarantee is all-or-nothing per revision.

What redacted verification cannot check

A redacted revision proves hash integrity only. The following checks are impossible over it — they are not silently assumed to pass; they are outside what a selective artifact can prove:

  • template resolution and schema validation of the payload (the payload is partly sealed; under some policies even the naming value is sealed);
  • the published leaves array check of full verification;
  • signature verification of the redacted revision itself (a redacted signature revision seals the signature bytes);
  • compute, anchor resolution, and every other content-dependent stage.
Not full verification

Consumers MUST NOT present a selectively disclosed artifact as having passed full verification. It proves integrity of the disclosed subset against the original hashes, no more and no less.

Selective trees

Code
json
1{
2 "revisions": {
3 "0x1620…a": { "disclosure": "Full", "revision": {} },
4 "0x1620…b": { "disclosure": "Redacted", "redacted": {} },
5 "0x1620…c": { "disclosure": "Hidden" }
6 },
7 "file_index": { "0x1620…a": "name" }
8}

(The nested revision and redacted objects are elided; redacted carries the field-level form above.)

Each revision of the source tree maps to one of three disclosure states:

StateContent
FullThe revision verbatim.
RedactedThe field-level redacted form above.
HiddenA bare placeholder carrying no fields at all, for typed object and template revisions. Hidden signature and anchor revisions are omitted from the map entirely rather than leaving a placeholder.

Production rules

  1. A revision not named by the disclosure policy defaults to Full.
  2. Every field-redacted revision that has a previous_revision MUST disclose the /previous_revision leaf (the exporter adds it if the policy did not) — chain linkage stays verifiable.
  3. file_index entries are retained only for Full revisions; redacted and hidden revisions lose their display names.
  4. Hidden placement. A hidden object or template revision keeps its key in the map (its placeholder), so chain continuity through it remains checkable — the loss is content, not linkage. Hidden signature and anchor revisions are omitted entirely, so a producer MUST NOT hide an anchor or signature that any retained revision chains from: the dangling reference makes the artifact fail chain verification (hiding a genesis anchor is the canonical mistake). Signatures and anchors with no dependents are the intended Hidden targets.
  5. Field-redaction of a signature revision, while mechanically possible for a tree-method signature, destroys its verifiability; disclosure policies MUST keep signature revisions Full (or Hidden as dead ends), and MUST keep anchor revisions Full.

Verifying a selective tree

Per entry:

  • Full — recover the algorithm from the map key, recompute the revision hash, and require byte equality with the key. If the revision has a previous_revision, that link MUST be a key of the selective tree (chain continuity).
  • Redacted — verify by the five-step procedure above. If a disclosed leaf with path exactly /previous_revision is present and its value parses as a revision link, that link MUST be a key of the selective tree. A genesis revision has no /previous_revision leaf at all — absent fields produce no pointer — so genesis-ness manifests as the leaf's absence.
  • Hidden — nothing is verified; the entry attests only that a revision with that hash existed at that position.

A missing parent is a chain-break failure. Signature revisions disclosed as Full SHOULD additionally be verified cryptographically by consumers relying on the attestations; the selective-tree integrity procedure above does not include it.

No completeness guarantee

A selective tree carries no commitment to the revision set: nothing proves that all revisions of the source tree appear. An exporter can omit a revision no disclosed revision references, and a verifier cannot detect the omission. Consumers that need completeness must obtain it out of band — for example, an application-level commitment such as an audit round anchor's leaf list.

Disclosure policies

A disclosure policy maps revision links to directives:

Code
json
1{
2 "revisions": {
3 "0x1620…a": "Full",
4 "0x1620…b": { "FieldRedacted": ["/payloads/signer_did", "/payloads/created_at"] },
5 "0x1620…c": "Hidden"
6 }
7}

FieldRedacted lists the pointers to disclose (an allow-list); everything else in that revision is sealed. A listed pointer that is not a leaf of the revision is an error: the redaction — and with it the export — MUST fail. (The presets and profiles below never produce such pointers: they intersect their fixed path sets with the revision's actual leaves before redacting, so an unset optional field is dropped from the list, not an error.) Policies are exporter inputs; they are never serialized into, or committed by, the artifact.

The full preset

The empty policy: every revision defaults to Full. The artifact is the whole tree, merely in selective-tree form.

The pseudonymous preset

A fixed preset for audit trees: reveal structure, identity, and chronology; seal content. Per revision kind:

  • Signature and anchor revisions: Full (attestations stay verifiable).
  • T1 (audit_user_turn_marker): Full (the marker carries no sensitive content).
  • Audit templates T2–T8, matched by naming value: FieldRedacted with the following allow-lists (all pointers under /payloads/):
TemplateDisclosed paths (beyond forced /previous_revision)
T2 audit_user_promptsigner_did, session_id, turn_id, created_at, plus attached_files/<i>/hash for every present index <i>
T3 audit_agent_thinkingsigner_did, turn_id, seq_in_turn, created_at, model_name
T4 audit_agent_tool_callsigner_did, turn_id, tool_name, risk_level, created_at
T5 audit_api_responsesigner_did, turn_id, method, endpoint, status_code, attested_origin, created_at, request_hash
T6 audit_tool_resultsigner_did, turn_id, tool_name, success, created_at
T7 audit_hitl_approvalsigner_did, turn_id, decision, created_at
T8 audit_agent_responsesigner_did, turn_id, created_at, is_final, model_name

Everything not listed is sealed — including the revision's structural leaves (/nonce, /local_timestamp, /method, /version, /revision_type, the container markers) and the sensitive payload fields (prompt text, thinking text, tool arguments and results, response bodies, HITL prompt text and rationale, attachment names and sizes, token counts).

The T2 attachment rule is dynamic: every actual path matching /payloads/attached_files/<index>/hash — where <index> is one non-empty all-ASCII-digit segment — is disclosed, so attachments stay hash-referenced while their names, sizes, and count-independent details stay sealed.

Any other revision (non-audit content) is Full: the preset takes no position on templates outside the audit family. Holders of mixed trees should use a profile.

Derivation-aware profiles

A disclosure profile generalizes the preset to a closed world with template-derivation awareness: an object revision whose template family is not named by the profile is redacted, not disclosed (disclosing unknown content is the failure mode being designed out). Revisions are classified by walking the template's verified derivation lineage — resolving each ancestor by hash from verified sources, never trusting self-declared ancestry, and sealing the revision fail-closed if the lineage cannot be resolved. Every field-redacted object revision force-discloses /revision_type (it is a hash, not sensitive, and consumers need the type to interpret the artifact). Family rules apply to every descendant template, and a per-revision Full directive cannot override an inherited family redaction rule. See the specification §5.3 for the full rules.

Security rules

  1. The /nonce leaf MUST NOT be disclosed. The per-leaf salts are derived from the revision's 16-byte nonce; disclosing it yields every leaf's salt and makes every sealed value — many of which are low-entropy (booleans, enums, small integers, container markers, the fixed version and method strings) — trivially brute-forceable. Exporters MUST reject a redaction request that lists the /nonce leaf (the SDK's redact_revision refuses with NonceDisclosureForbidden). The artifact format cannot express the prohibition, so the exporter is the enforcement point.
  2. Guessing resistance for sealed values is bounded by the nonce's 128 bits. Disclosing one leaf's salt does not weaken the others: per-leaf salts are derived through a one-way expansion keyed by the pointer path.
  3. Selective-tree inputs are attacker-controlled. Verifiers MUST reject a leaf_count of 0 (an empty leaf list has no defined Merkle root), MUST enforce index bijectivity, and MUST apply the strict multihash decoding rules to revision_hash before hashing anything.

SDK API

The disclosure API of aqua-rs-sdk-core:

APIPurpose
DisclosurePolicy::pseudonymous(&tree)Build the pseudonymous preset for an audit tree.
DisclosurePolicy::full(&tree)The full preset (every revision disclosed).
DisclosurePolicy::with_profile(&tree, &profile, &linked_trees)Build a policy from a derivation-aware closed-world DisclosureProfile.
DisclosureProfile::audit()The built-in profile for the audit template family.
redact_revision(revision, revision_hash, disclosed_paths)Field-redact one tree-method revision into a RedactedRevision.
verify_redacted_revisionThe five-step redacted-revision verification.
export_selective_treeExport a tree under a disclosure policy.
verify_selective_tree(&selective)Verify a selective tree's integrity and chain continuity.

See also

  • Hashing and Canonicalization — the leaf commitments, salts, and Merkle construction that redaction is built on
  • Data Model — revision kinds, methods, and tree structure
  • Verification — full verification and what selective artifacts opt out of
  • aqua-rs-sdk-core — installing and using the disclosure API
  • Audit Trails for AI Agents — the audit template family the pseudonymous preset targets
Edit this pageReport an issue
Previous
Anchors and Links
Next
Verification

Documentation

  • Getting Started
  • Protocol Reference

Community

  • GitHub

Copyright © 2026 inblock.io assets GmbH. All rights reserved.

On this page

Precondition: tree methodThe redacted revisionWhat redaction cannot hideVerifying a redacted revisionWhat redacted verification cannot checkSelective treesProduction rulesVerifying a selective treeNo completeness guaranteeDisclosure policiesThe `full` presetThe `pseudonymous` presetDerivation-aware profilesSecurity rulesSDK APISee also