Skip to Content

Examples

These examples use the real exported surface of ahl-core (see API Reference, generated from src/lib.rs and src/receipt.rs). ahl-core is a private, pre-release repository tracking Core Specification v0.3-draft; the surface can still change before publication.

Verify an Evidence Receipt

use ahl_core::receipt::{verify_receipt, TrustPolicy}; use serde_json::Value; use std::fs; fn main() -> Result<(), Box<dyn std::error::Error>> { let receipt_json = fs::read_to_string("decision-d142.ahl")?; let receipt: Value = serde_json::from_str(&receipt_json)?; // TrustPolicy is the verifier's locally configured trust anchor — nothing in it // is taken from the receipt itself (Data Structures §11 design rule 1). let policy = TrustPolicy { genesis_entry_id: "sha256:<published genesis entry id>".to_owned(), genesis_key_ids: ["sha256:<published producer key fingerprint>".to_owned()].into(), ..Default::default() }; match verify_receipt(&receipt, &policy) { Ok(verdict) => { println!("Rendered boundary: {}", verdict.boundary); println!("Claim type: {}", verdict.claim_type); println!("Statement id: {}", verdict.subject_statement_id); println!("Entry index: {}", verdict.subject_entry_index); } Err(e) => println!("Rejected: {e}"), } Ok(()) }

verdict.boundary is rendered only from claim.type and the verified assurance block — never from the receipt’s informative note field, and never a stronger claim than what verification established.

Compute a record commitment

use ahl_core::{commit_keyed, commit_plain, jcs}; use serde_json::json; fn main() -> ahl_core::AhlResult<()> { let record = json!({ "customer_id": "C-1001", "score": 712 }); let bytes = jcs(&record); // `plain` mode: SHA-256(dsid || 0x1F || canonical_bytes). let plain = commit_plain("decisions", &bytes); println!("plain commitment: {plain}"); // `keyed` mode is REQUIRED for personal or sensitive data (Data Structures §7). let dataset_key = [0x11u8; 32]; // held by the producer, never packaged in a receipt let keyed = commit_keyed(&dataset_key, "bureau", &bytes)?; println!("keyed commitment: {keyed}"); Ok(()) }

Build and verify a signed statement envelope

use ahl_core::{envelope, verify_envelope, TestKey}; use serde_json::json; fn main() -> ahl_core::AhlResult<()> { // TestKey builds deterministic Ed25519 keys from committed seeds. The crate is // explicit that this is test-vector machinery, not production key handling. let producer = TestKey::from_seed_hex( "producer-1", "0101010101010101010101010101010101010101010101010101010101010101", )?; let payload = json!({ "ahl_version": "0.3", "type": "ingestion", "producer": "acme-lending", "dataset": "bureau", "record": "hmac-sha256:9f86d081...", }); let env = envelope(payload, &producer); // A minimal resolver: in a real verifier this looks up the key set active at // the statement's entry index under the manifest snapshot rule (Protocol §8.2). let resolve = |key_id: &str| (key_id == producer.key_id()).then(|| producer.pubkey()); let signed = verify_envelope(&env, resolve)?; println!("envelope signature valid: {signed}"); Ok(()) }

Compute a revocation closure

use ahl_core::closure::affected_set; use std::collections::BTreeMap; fn main() -> ahl_core::AhlResult<()> { // `envelopes` is the corpus in entry-index order — the slice index *is* the // entry index, AHL's only ordering primitive (Protocol §3.2). let envelopes: Vec<serde_json::Value> = load_corpus(); let trigger_index = 12; // the entry index of a `retraction` or `correction` let trees = BTreeMap::new(); // committed batch/input-set trees, if any are referenced let closure = affected_set(&envelopes, &trees, trigger_index, envelopes.len())?; println!("seeds: {:?}", closure.seeds); println!("affected (derived records requiring disposition): {:?}", closure.affected); // The seeds themselves are never members of `affected` — they are what the // trigger names directly, not what must be dispositioned (Protocol §6.1). Ok(()) } fn load_corpus() -> Vec<serde_json::Value> { vec![] }

Handle verification errors

use ahl_core::receipt::{verify_receipt, ReceiptError, TrustPolicy}; use serde_json::Value; fn handle(receipt: &Value, policy: &TrustPolicy) { match verify_receipt(receipt, policy) { Ok(verdict) => println!("verified: {}", verdict.boundary), Err(ReceiptError::GenesisAnchorMismatch) => { eprintln!("receipt's genesis anchor does not match locally configured policy"); } Err(ReceiptError::KeyNotBound { key_id, entry_index }) => { eprintln!("key `{key_id}` is not bound to a manifest key object at entry index {entry_index}"); } Err(ReceiptError::CompetingRangeInsufficient { got_from, got_to, tree_size, .. }) => { eprintln!("competing-trigger range [{got_from}, {got_to}) does not cover the required [0, {tree_size})"); } Err(ReceiptError::ClosureMismatch(detail)) => { eprintln!("recomputed closure disagrees with the anchored disposition tree: {detail}"); } Err(other) => eprintln!("rejected: {other}"), } }

ReceiptError is #[non_exhaustive] — each variant names the specific rule that fired (format §5 algorithm), so a test can assert which rule rejected a deliberately malformed receipt rather than that verification “failed somehow”.

Last updated on