Skip to main content

evidence verify

One command that answers the question your AI audit logs can't: who guarantees this log wasn't altered by the system that produced it?

$ evidence verify receipts.jsonl

entries checked : 5
chain verdict   : CHAIN VALID
independence    : SELF-ATTESTED — would not survive independence challenge.
                  Every hash in this chain was produced and stored by the same
                  system it describes; a compromised producer can re-sign the
                  entire chain. No external anchor found.

Why this exists

The EU AI Act's Article 12 requires automatic event logging for high-risk AI systems (Annex III obligations now apply from December 2, 2027 under the July 2026 Omnibus amendment; retention ≥6 months under Articles 19/26(6)). Most vendors answer with hash-chained logs.

A hash chain proves sequence. It does not prove independence. If the system that writes the log is compromised — or simply motivated — it can rewrite history and re-sign the entire chain. The log is a diary, not an affidavit. The enforcer cannot be the notary.

evidence verify checks three levels:

  1. CHAIN — is every entry linked to the previous one?
  2. TAMPER — does every entry's content still match its hash?
  3. INDEPENDENCE — does the chain reference an anchor outside the producing system? If not, it says so, plainly.

It works on any log that follows the receipt format below — including logs from other gateways and proxies. Point it at your current vendor's export and see what an auditor would see.

Install

Go 1.21+:

go build -o evidence .
./evidence verify sample_selfattested.jsonl

Python reference implementation (identical logic, used for cross-testing):

python3 evidence_verify.py sample_selfattested.jsonl

v0.3: pip install + LangChain

pip install evidence-verify            # core, zero dependencies
pip install "evidence-verify[langchain]"   # + the callback handler

Every LLM and tool call in a LangChain app becomes a chained receipt:

from evidence_verify import EvidenceCallbackHandler

handler = EvidenceCallbackHandler("receipts.jsonl")
llm.invoke(prompt, config={"callbacks": [handler]})

By default only SHA-256 fingerprints of prompts/responses are recorded — the evidence file proves what happened without containing your data (include_text=True opts into full text). Then:

evidence verify receipts.jsonl
evidence anchor receipts.jsonl > anchor.json     # store this OUTSIDE
evidence verify receipts.jsonl --anchor anchor.json

Exit codes

0 = chain valid and anchored (round-trip verified when --anchor is given). 1 = self-attested, mismatch, or broken/tampered. 2 = usage or file error. CI can gate on 0.

Receipt format (v0.1 — spec RFC coming)

One JSON object per line (JSONL):

{
  "seq": 1,
  "ts": "2026-08-09T10:01:00Z",
  "event": { "model": "...", "request_sha256": "...", "response_sha256": "...", "decision": "allow" },
  "prev_hash": "<sha256 of previous entry; 64 zeros for genesis>",
  "entry_hash": "<sha256 of seq|ts|canonical(event)|prev_hash>",
  "anchor": { "type": "external", "provider": "...", "ref": "..." }
}

canonical(event) = JSON with sorted keys, no whitespace (RFC 8785-style). The Go and Python implementations are byte-compatible.

v0.2: anchor round-trip

Anchor a chain (store the output OUTSIDE the producing system — a separate repo, a gist, S3, a timestamping service):

python evidence_verify.py anchor receipts.jsonl > anchor.json

Verify against the anchor (local file or https URL):

python evidence_verify.py verify receipts.jsonl --anchor anchor.json
python evidence_verify.py verify receipts.jsonl --anchor https://example.com/anchor.json

Round-trip verification catches what hash chains alone cannot: full history rewrites (re-signed chains) and truncation. The independence guarantee is exactly as strong as the independence of where the anchor lives.

Samples

  • sample_selfattested.jsonl — valid chain, no anchor (what most vendors ship)
  • sample_anchored.jsonl — valid chain with external anchor references
  • sample_tampered.jsonl — one entry silently edited; the verifier catches it

Regenerate with python3 make_samples.py.

Roadmap

  • Ed25519 signature verification (asymmetric, third-party verifiable)
  • eIDAS-compatible qualified timestamp validation for anchors
  • Adapters: Kong plugin export, LangChain callback, LiteLLM, OpenAI compliance-log export (which retains ~30 days by default — mind the 6-month retention gap)

License

Apache-2.0. Verification stays free forever. That's the point.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evidence_verify-0.3.0.tar.gz (12.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evidence_verify-0.3.0-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file evidence_verify-0.3.0.tar.gz.

File metadata

  • Download URL: evidence_verify-0.3.0.tar.gz
  • Upload date:
  • Size: 12.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for evidence_verify-0.3.0.tar.gz
Algorithm Hash digest
SHA256 37be8be954c4ef3db8733ffd0e6d9b5da17bc85bb92b5977a5f8701b56d85a97
MD5 2819a9251ec48538c134702d022f1831
BLAKE2b-256 8228d8db2c138e3aa13bdeab2e46faa2d8b9d9f0d2fd3f1643198d8cd36e4632

See more details on using hashes here.

File details

Details for the file evidence_verify-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for evidence_verify-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d2f26d2ecfa0200e928743672fc7be480944c842964f0c3b6b72ec205283b02f
MD5 a39e67a6a549e8497354ef8cf0411ce3
BLAKE2b-256 6ce7c6c9741ee43d342632918f6cc5ab823ad0429eacadedaa8976d4e26e9483

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page