Data Structures
This page provides detailed technical schemas for the core data structures used in the AHL Protocol. Normative definitions live in The Protocol and its companion Evidence Receipt container format; this page collects the concrete wire shapes.
1. Statement Envelope
Every AHL statement is a signed envelope. payload carries one of the seven statement types (§2–§6); signatures binds it to a producer key.
{
"payload": {
"ahl_version": "0.3",
"type": "derivation",
"producer": "acme-lending",
"manifest": "sha256:1122...",
"valid_time": "2026-08-01T00:00:00Z",
"issued_at": "2026-08-01T00:03:12Z"
},
"signatures": [
{ "key_id": "sha256:4433...", "sig": "base64:SGVsbG8..." }
]
}- Statement id =
SHA-256(JCS(payload))— the reference used inside the statement graph (derivation.inputs[].statement, trigger targets, etc.). - Entry id =
SHA-256(JCS(envelope))— the retrieval key for the anchored log entry, and the reference used where anchoring identity matters (manifest and key statements reference each other by entry id).
2. Ingestion (JSON)
{ "type": "ingestion",
"dataset": "bureau",
"record": "hmac-sha256:9f86d081...",
"origin": "bureau-feed-2026-08" }3. Derivation (JSON)
{ "type": "derivation",
"pipeline": "credit-decision-v3",
"outputs": [
{ "dataset": "decisions", "record": "sha256:7f83b165...", "locator": "s3://decisions/2026/08/d-142.json" }
],
"inputs": [
{ "dataset": "bureau", "record": "hmac-sha256:9f86d081...", "role": "feature" },
{ "dataset": "applications", "record": "sha256:a1b2c3d4...", "role": "reference" }
],
"transform": {
"code": { "digest": "sha256:c0ffee01...", "type": "git_commit" },
"model": { "digest": "sha256:mode1234...", "version": "credit-v4.2" },
"params": { "digest": "sha256:9988aabb..." }
} }Batch form replaces outputs with outputs_root / outputs_count / leaf_format: "ahl-leaf-v2" — see §9.
4. Retraction and Correction (JSON)
{ "type": "retraction",
"dataset": "bureau",
"record": "hmac-sha256:9f86d081...",
"scope": { "effective_from": "2026-09-15T00:00:00Z", "retroactive": true },
"reason_code": "error" }{ "type": "correction",
"dataset": "bureau",
"record": "hmac-sha256:9f86d081...",
"replacement": "hmac-sha256:eeff2233...",
"scope": { "effective_from": "2026-09-15T00:00:00Z", "retroactive": true },
"reason_code": "error" }5. Propagation (JSON)
{ "type": "propagation",
"trigger": "sha256:trigger-statement-id...",
"corpus_checkpoint": { "log_id": "sha256:9988...", "tree_size": 1450, "root_hash": "sha256:7f83..." },
"affected_root": "sha256:disposition-tree-root...",
"affected_count": 12,
"complete_relative_to_manifest": true }Disposition leaf (one per affected record, JCS-canonical):
{ "dataset": "decisions", "record": "sha256:7f83b165...",
"disposition": "recomputed",
"successor_statement": "sha256:successor-derivation-id..." }disposition is one of invalidated, recomputed (REQUIRES successor_statement), or assessed_unaffected (REQUIRES assessment, the SHA-256 of a retained assessment artifact).
6. Manifest and Key (JSON)
The manifest payload IS the corpus manifest object (§8.2 of the protocol) at top level — see §10 below. key statements record key-set transitions:
{ "type": "key", "action": "add",
"key": { "key_id": "sha256:4433...", "pubkey": "base64:MCowBQYD...", "valid_from": "2026-01-01T00:00:00Z" } }7. Commitments
Record identity is a commitment, computed per the dataset’s declared mode:
plain commitment = SHA-256( dsid || 0x1F || canonical_bytes )
keyed commitment = HMAC-SHA-256( k_dataset, dsid || 0x1F || canonical_bytes )dsid is the dataset id — domain separation, so identical bytes in different datasets produce distinct commitments. keyed mode is REQUIRED for personal or sensitive data; k_dataset is held by the producer and disclosed under the manifest’s access rule to authorized verifiers only. One mode and one canonicalization apply to every statement referencing a given dataset.
8. Merkle Tree Rules
All AHL trees (batch outputs, input sets, disposition sets) share one construction:
LeafHash = SHA-256( 0x00 || leaf_bytes )
NodeHash = SHA-256( 0x01 || LeftChildHash || RightChildHash )Leaves are sorted by record — ascending lexicographic comparison of the UTF-8 bytes of the canonical commitment string — duplicates are prohibited. This yields O(log n) membership proofs. The complete leaf set of every committed tree is corpus material: at L3 it MUST be retrievable and enumerable in full, independent of the producer (protocol §4.5).
9. Batching
A batch derivation replaces outputs with three top-level fields:
{ "type": "derivation",
"pipeline": "credit-decision-v3",
"outputs_root": "sha256:aabb1122...",
"outputs_count": 5000,
"leaf_format": "ahl-leaf-v2",
"transform": { ... } }Each batch leaf (ahl-leaf-v2, JCS-canonical) is:
{ "dataset": "decisions", "record": "sha256:7f83b165...",
"inputs": [
{ "dataset": "bureau", "record": "hmac-sha256:9f86d081...", "role": "feature" }
] }For wide input sets, inputs MAY be the compact form { "input_set_root": "sha256:...", "input_set_count": 3 }, itself a tree following the §8 rules — this is the shape a Test Vector or Evidence Receipt uses when it proves membership of a single input without carrying the full input list.
10. Corpus Manifest (JSON)
{ "ahl_version": "0.3",
"type": "manifest",
"producer": "acme-lending",
"predecessor": "sha256:<entry id of prior manifest — absent only for genesis>",
"keys": [ { "key_id": "sha256:4433...", "pubkey": "base64:...", "valid_from_index": 0 } ],
"log": {
"id": "sha256:9988...",
"operator": "acme-lending",
"adaptor": { "id": "ahl-adaptor-atl-v1", "hash": "sha256:profile-hash..." },
"checkpoint_cadence": "PT1H",
"witness_grace_period": "PT6H",
"keys": [ { "key_id": "sha256:log-key...", "pubkey": "base64:...", "valid_from_index": 0 } ]
},
"witnesses": [ { "witness_id": "independent-witness-1", "key_id": "sha256:witness-key...", "pubkey": "base64:...", "valid_from_index": 0 } ],
"datasets": {
"bureau": { "canonicalization": "jcs", "commitment_mode": "keyed", "key_access_rule": "regulator-on-request",
"authority": { "producer": "acme-lending", "key_ids": ["sha256:4433..."] } },
"decisions": { "canonicalization": "jcs", "commitment_mode": "plain" }
},
"pipelines": { "include": ["credit-decision-v3"], "exclude": [] },
"windows": { "anchoring": "PT1H", "propagation": "P30D" },
"retention": { "statements": "P7Y", "artifacts": "P7Y" },
"level": "L3" }The manifest statement’s own statement id is the manifest version id every other statement’s manifest field references. A non-genesis manifest references its predecessor by entry id, not statement id.
11. AHL Evidence Receipt (JSON)
The Evidence Receipt is the portable, offline-verifiable proof artifact. It is a JCS-canonical JSON document (file extension .ahl, provisional media type application/ahl-receipt+json).
claim.record_subject is type-specific, not universal: it is REQUIRED for record-*, trigger-*, and disposition-* claim types, and MUST be absent for statement-anchored and governance-state (which instead target an index via claim_material.target_index). The example below carries record_subject because its claim.type is record-derived; copying it verbatim into a statement-anchored or governance-state receipt would make that receipt invalid.
Structure, condensed:
{
"ahl_receipt_version": "1",
"spec_version": "0.3.0",
"claim": {
"type": "record-derived",
"record_subject": { "dataset": "decisions", "record": "sha256:7f83b165..." },
"assurance": {
"governance": "declared",
"competing_triggers": "not-checked",
"witnessed": true,
"continued_history": false,
"content_binding": "none"
}
},
"subject": {
"statement_id": "sha256:<hex>",
"entry_id": "sha256:<hex>",
"entry_index": 142,
"manifest": "sha256:<manifest version statement id>"
},
"envelope": { "payload": { ... }, "signatures": [ ... ] },
"keys": {
"log": [ { "key_id": "sha256:<hex>", "pubkey": "base64:<...>", "source": "manifest-chain", "binding": { "entry_index": 57 } } ],
"witness": [ { "witness_id": "<id>", "key_id": "sha256:<hex>", "pubkey": "base64:<...>", "source": "manifest-chain", "binding": { "entry_index": 57 } } ],
"producer": [ { "key_id": "sha256:<hex>", "pubkey": "base64:<...>", "source": "manifest-chain", "binding": { "entry_index": 0 } } ]
},
"anchoring": {
"adaptor": { "id": "<profile name>", "hash": "sha256:<hex>" },
"checkpoint": { "log_id": "sha256:<hex>", "tree_size": 1450, "root_hash": "sha256:<hex>",
"checkpoint_time": "<RFC 3339>", "key_id": "sha256:<hex>", "signature": "base64:<...>" },
"inclusion_path": [ "sha256:<hex>", ... ],
"witnesses": [ { "witness_id": "<id>", "key_id": "sha256:<hex>", "cosignature": "base64:<...>", "cosigned_at": "<RFC 3339>" } ]
},
"governance": {
"genesis_entry_id": "sha256:<hex>",
"chain": [ { "envelope": { ... }, "entry_index": 0, "inclusion_path": [ ... ] } ],
"currency": { "mode": "declared", "material": {} }
},
"claim_material": { "output": { "dataset": "decisions", "record": "sha256:7f83b165..." } }
}keys.producer is derived from the governance chain — listed for convenience, but verified against the chain, not trusted from the array itself. All log, witness, and producer keys used in verification MUST be present with source and binding; a key a later manifest version replaced cannot validate material bound to the earlier one.
12. Claim-Type Registry
A receipt declares exactly one claim.type, and proves exactly that claim — never more. Types with suffix -declared carry assurance.governance: "declared" and their rendered verdicts never use the words “effective”, “governs”, or “complete”; -effective / -complete types REQUIRE assurance.governance: "enumerated". The Required claim_material column is not optional detail — a receipt missing it does not prove the claim its claim.type names.
claim.type | Proves | Required claim_material |
|---|---|---|
statement-anchored | The subject envelope is anchored at its entry index under a (witnessed) checkpoint, signed under the declared manifest chain | {} — no additional material |
record-ingested | A record (dataset, commitment) was introduced by the subject ingestion statement | Record bytes + canonicalization id, present iff content_binding != "none" (verification recomputes the commitment) |
record-derived | One output record’s derivation is committed by the subject derivation statement (unbatched or batched, with Merkle path to outputs_root) | The output object; for a batch, the leaf, its Merkle path, and (optionally) proven input members |
trigger-declared | A trigger naming (dataset, record) is anchored and signed under the declared chain by the declared issuer | An embedded record-ingested/record-derived receipt proving the trigger’s own record was introduced — plus, for corrections, an embedded introduction receipt for the replacement |
trigger-effective | As above, plus: the issuer’s authority held under enumerated governance at the trigger index, and this trigger governs at checkpoint C | trigger-declared material, plus a checkpoint_C that MUST match the receipt’s own verified anchoring.checkpoint on {log_id, tree_size, root_hash} (a self-supplied unverified checkpoint is rejected), plus a competing-trigger enumeration whose range MUST be the complete [0, tree_size(C)) — or [introduction_index, tree_size(C)) only where an embedded introduction receipt fixes that lower bound. REQUIRES governance: "enumerated" and competing_triggers: "enumerated" |
disposition-declared | A propagation statement anchored under the declared chain dispositions (dataset, record) | An embedded trigger-declared receipt for the trigger, plus the disposition leaf and its Merkle path |
disposition-effective | As above, with an embedded trigger-effective receipt | Same material, with the embedded trigger receipt REQUIRED to be trigger-effective, not merely trigger-declared |
propagation-complete | The propagation statement’s affected set equals the recomputable closure at the propagation’s declared corpus checkpoint D — never at a later checkpoint | REQUIRES the authenticated declared checkpoint D itself — D’s own signed checkpoint, plus EITHER a consistency proof from D to the receipt’s verified anchoring.checkpoint OR recomputation of D’s prefix root from the enumerated prefix (an untrusted, self-declared D does not ground the claim) — an embedded trigger-effective receipt for the trigger (challenges are never traversed), the full authenticated corpus prefix [0, tree_size(D)), and the complete leaf material for every batch/input-set/disposition tree referenced in that prefix. No compact form exists by construction — an unauthenticated D, a partial prefix, or missing tree material does not prove completeness |
governance-state | The manifest/key state at a target entry index is exactly the presented chain. subject MUST be the manifest statement claimed active at target_index — a key statement is never a valid subject for this claim type, and subject.entry_index MUST be ≤ target_index | The target index, plus enumerated material proving no manifest or key statement exists in (subject.entry_index, target_index]. REQUIRES governance: "enumerated" |
13. Assurance Fields
governance—"declared"proves anchoring and signing from genesis; the active governance state at the subject index is not independently proven."enumerated"additionally proves it via authenticated range enumeration.competing_triggers—"not-checked"makes no claim about other triggers on the same record;"enumerated"carries every competing trigger through checkpoint C, with the governing one established by entry index.witnessed— at least one valid witness cosignature on the inclusion checkpoint.continued_history— a later checkpoint plus a consistency proof are present and valid, claiming continued append-only history past the subject’s checkpoint.content_binding—"none","plain-verified"(recomputes SHA-256 over carried canonical bytes), or"keyed-authorized"(recomputes HMAC over carried canonical bytes with a verifier-held dataset key). Dataset keys are never packaged inside receipts.
A verifier MUST render its verdict from claim.type plus assurance and MUST NOT report a stronger guarantee than these fields establish.
See Reference Implementations for the library that generates and verifies this format, and Test Vectors for the corpus exercising every claim type.