Check each evidence claim separately.

Evaluation can issue an Ed25519-signed decision receipt before a tool executes. A proof bundle adds its audit-event binding and Merkle inclusion under a signed checkpoint. These are distinct checks.

Built Receipt signing, proof export, signed checkpoints, and offline verification. Reviewed 19 October 2026.

Concept · illustrative

One call, four separate checks

names the audit_event_id RECEIPT signed decision · Ed25519 01 is a leaf in a batch Merkle tree AUDIT EVENT id · payload hash · sequence 02 ends at a signed root MERKLE INCLUSION path from event to batch root 03 SIGNED CHECKPOINT root · sequence range · count 04

A missing link makes a claim unverifiable. That differs from a failed signature or a mismatched proof.

All IDs, hashes, and outcomes shown here are illustrative. This page calls no backend and performs no cryptographic verification.

What the signed receipt actually contains

The compact decision payload names the tool call, agent, tenant, decision, decision time, and nonce. The current strict-audit path issues a version-2 receipt with its type, signing-key ID, and audit-event ID.

Request hashes, policy versions, rule IDs, delegation context, sequence numbers, and checkpoint roots are not all embedded in this signed payload. They belong to other records or proof components. A valid receipt alone does not authenticate arbitrary neighboring metadata.

With strict audit disabled, the gateway can issue an older unbound receipt. With strict signing enabled, an unsigned non-deny decision fails; a denial may still be returned without a valid receipt when signing fails.

Signed payload and envelope — separate from the wider tool-call record
FieldsMeaning
tool_call_id, agent_id, tenant_idWhich evaluated call and identities the decision names
decision, decided_at, nonceThe verdict, its timestamp, and a random nonce
version, type, kid, audit_event_idCurrent version-2 format, signing-key identifier, and audit binding
payload_b64, signature_b64Canonical payload bytes and their Ed25519 signature, in the envelope

Audit events and asynchronous sequencing

Audit events carry IDs and payload hashes. Sequence numbers are assigned in background batches, not necessarily when evaluate returns. The implemented integrity structure is a Merkle tree over event ID, payload hash, and sequence number; it is not a previous-entry hash chain.

Database privileges restrict ordinary audit mutation, with controlled sequence assignment and retention operations. Retention and database-role configuration remain part of the deployment boundary.

Four illustrative events after sequencing. Hashes are display placeholders.
SeqEventDecisionPayload hash
1041read requestallow9f2c…a1
1042delete requestdenyc418…6b
1043message requestredact51de…f3
1044export requestrequire_approvalaa07…9c

Merkle inclusion under a signed checkpoint

The checkpoint job hashes a batch into a Merkle root and signs its tenant, sequence range, event count, and root. An inclusion proof lets a verifier recompute the path from one event to that root. Optional external anchoring is a separate configuration.

Diagram · illustrative data

Event 1042 under a signed root

SIGNED CHECKPOINT root · seq 1041–1044 H(1041, 1042) on the path H(1043, 1044) sibling hash seq 1041 read 9f2c…a1 seq 1042 delete c418…6b seq 1043 message 51de…f3 seq 1044 export aa07…9c EVENTS, AFTER SEQUENCING
  • Solid path from event 1042 to the root.
  • Dashed sibling hashes the verifier needs to recompute it.

A newly evaluated call may not yet have a checkpoint. Missing or older proof components can make a claim unverifiable; that is different from a failed signature or mismatched proof.

Illustration: alter an event after checkpointing

Changing an event's payload hash changes its Merkle leaf. The original proof can no longer reach the signed root. The buttons illustrate that result; they do not run the real algorithm.

Tamper demonstration

No changes yet. Illustrative event values match the checkpoint example.

Offline verification with ghalta-verify

Export the tool call's proof bundle and obtain the gateway public key independently. The verifier does not trust a key merely because it appears inside the bundle.

  1. Verify the receipt signature against the separately trusted key and compare any independently expected identities or decision.
  2. Check receipt-to-audit-event binding and the event's Merkle inclusion proof.
  3. Authenticate the checkpoint's signed root and sequence range.
  4. Use a maximum age when checkpoint freshness matters. Without that option, no freshness claim is made.
ghalta-verify proof.json --gateway-pubkey gateway-key.json --json
# Optional freshness bound:
ghalta-verify proof.json --gateway-pubkey gateway-key.json --max-age 24h

Commands match the implemented CLI. Files above are example names, not downloadable evidence. Exit codes: 0 verified, 1 failed, 2 unverifiable or usage error.

A receipt and inclusion proof do not prove that an external tool completed an action, that every call was captured, or that all exported request and policy metadata is authentic.

Reports and proof exports

Reports provide review context; a proof export supplies the signed objects and inclusion evidence required by the verifier. An export is not automatically a successful verification. Missing checkpoints and unsupported older formats must remain visible in the result.

  • tamper-proof and verifiable

    In a draft paper, NIST asks the public how AI agents can keep tamper-proof, verifiable logs of their actions.

    How can we ensure that agents log their actions and intent in a tamper-proof and verifiable manner?

    Source: NIST National Cybersecurity Center of Excellence, retrieved