Skip to main content
b7n0de, Verified AI Work, pink rabbit mascot over the B7N0DE wordmark

proofbundle

AI eval results need receipts.

Turn an AI evaluation result into one portable, offline-verifiable receipt. It proves who signed these exact bytes and that nothing changed since — not that the number is true. Ed25519 + RFC 6962 Merkle, one file, no server, no network.

CI demo reproducible PyPI Python License: MIT DOI Ruff Mutation tested PyPI attestations SLSA provenance

Reviewing this for adoption? Start with the 30-minute adversarial audit path: docs/REVIEWERS.md.

Contents

60-second try (offline)

pip install "proofbundle[eval]"
proofbundle demo   # honest receipt => OK, six tampers each => FAILED, sample swap caught
# Inspect-native (METR Task Standard / UK-AISI ecosystem, mockllm, no API key):
git clone https://github.com/b7n0de/proofbundle && cd proofbundle
pip install -e ".[eval,inspect]" && make demo   # or `make full-demo` for log -> receipt -> verify

The demo runs entirely in memory: an honest receipt verifies => OK, six independent tampers each verify FAILED, and a swapped sample gets caught. proofbundle demo exits non-zero if any tamper slips through, so it doubles as a self-test. Full walkthrough: docs/DEMO.md.

# verify a real hosted receipt without writing any code:
curl -fsSL https://raw.githubusercontent.com/b7n0de/proofbundle/main/examples/example_bundle.json -o receipt.json
proofbundle verify receipt.json        # CRYPTO: OK  (the verify itself runs fully offline)

# your own receipt, from a signed payload:
proofbundle emit --payload-file result.json --new-key signer.key --out receipt.json
proofbundle verify receipt.json        # exit 0 = crypto OK, 1 = crypto/verification failure, 2 = malformed

# apply YOUR trust decision — verify makes NO trust decision on its own:
proofbundle verify receipt.json --policy trust_policy.json   # POLICY: OK | FAIL (exit 3) | NOT_EVALUATED

# start from a shipped TEMPLATE and pin your own signer — offline, no network:
proofbundle policy instantiate strict-eval-template-v1 \
  --issuer-key org-eval.pub --policy-id org/strict-eval-v1 --output org.json
proofbundle verify receipt.json --json --policy org.json \
  --expected-root <b64> --expected-tree-size <n>   # or: --trusted-checkpoint note.txt --checkpoint-vkey <vkey>
# → root_authenticity.safeForAutomation: true only when the policy pins a trusted signer AND root
#   plus tree size are authenticated ATOMICALLY from one source (a root-bytes-only pin is
#   rootTrustLevel: ROOT_BYTES_ONLY — never automation-safe; automationBlockers names every reason)

Inspect-native? (METR Task Standard, UK-AISI ecosystem)

The receipt layer runs directly on Inspect AI — and the proof is reproducible offline in minutes:

# setup as in the 60-second try above (clone + pip install -e ".[eval,inspect]"), then:
make full-demo   # a genuine inspect_ai eval log (mockllm: offline, no API key, no GPU)
                 # -> signed receipt next to the log -> proofbundle verify => OK

In your own pipeline the end-of-task hook signs every run automatically. Walkthrough: docs/INSPECT_HAPPY_PATH.md · worked example: examples/inspect_receipt.py.

The problem

Every AI eval number you read — a safety benchmark, a capability score, a leaderboard entry — is an unverifiable claim. You trust the lab. There's no portable way to check, offline, that a result was signed by a stated party, hasn't been altered, and covers the samples it claims.

proofbundle is that check. It's a small MIT-licensed Python tool (a compact, auditable trusted core, depends only on cryptography) that turns a result into a signed receipt anyone can verify from a single file — and it's honest about the line it does not cross.

In plain language: a proofbundle receipt is the cash-register receipt of an AI test result. It shows who claimed the number and that nobody quietly changed it afterwards. It does not show the test was good — the way a cash-register receipt does not show the meal was good — but without a receipt there is nothing to check at all.

What a receipt proves, and what it doesn't

✅ It proves ❌ It does not prove
These exact bytes were signed by this key (authorship) That the number is true
Nothing changed since signing (integrity, Ed25519 + RFC 6962) That the issuer is honest
The result is attributable to a stated issuer That the eval was well-designed
A threshold was met while hiding the model/dataset (salted commitments) That there was no cherry-picking — unless pre-registered
Optionally: individual samples, offline-auditable (per-sample Merkle) That the computation was correct — that needs a TEE or independent reproduction

This boundary is the point, not a weakness. A receipt makes a claim attributable, tamper-evident, and — with pre-registration and per-sample auditing — bounded and spot-checkable. Full detail: THREAT_MODEL.md.

How it fits together

(diagram renders on GitHub — view it there; PyPI shows the source)

flowchart LR
    H["eval harness<br/>inspect_ai · lm-eval · promptfoo · pytest"] --> A["adapter → signed claim<br/>salted commitments · provenance · samples root"]
    A --> R["receipt<br/>one portable file"]
    R --> V{{"proofbundle verify — offline"}}
    V --> C["signature · Merkle inclusion · SD-JWT/KB ·<br/>witness quorum · status list · sample openings"]
    C --> OK(["CRYPTO: OK / FAILED"])
    style V fill:#D6248A,stroke:#D6248A,color:#fff
    style OK fill:#D6248A,stroke:#D6248A,color:#fff

Where it sits in the research neighbourhood

proofbundle is a practical, released, offline verifier — complementary to TEE and zero-knowledge approaches, not a replacement for any of them. It is honest about the line each neighbour crosses that a receipt does not. Maturity labels are stated so nothing reads as a settled standard when it is a preprint.

Neighbour What it contributes that a receipt does not Maturity Where proofbundle draws the line
K-Veritas (arXiv 2605.08586) — nonrepudiable experimental results the academic case for tamper-evident, execution-bound experiment reports preprint proofbundle is a released, offline, eval-shaped receipt for exactly this problem, not the only take on it
Attestable Audits (arXiv 2506.23706) — TEE-verified safety audits that the computation actually ran, inside a trusted enclave preprint (research prototype) a receipt proves authorship + integrity, not that the computation was correct — that needs a TEE or independent reproduction
BenchJack (arXiv 2605.12673) — auditing agent benchmarks whether the benchmark itself is gameable (reward-hacking) preprint a receipt over a gameable benchmark is honestly still just a receipt; it says nothing about whether the eval was well designed
Evaluation Cards (arXiv 2606.09809) — reporting / interpretation layer a structured, human-facing account of what a result means preprint a receipt can bind a card's integrity (evaluation_card_sha256, EVAL_CLAIM.md), not its quality
in-toto / Sigstore, SCITT / Rekor v2, OpenSSF Model Signing (stable standards / production) artifact-provenance, public transparency, model-artifact signing see INTEROP.md for the honest tool-by-tool comparison

Cite this work

If proofbundle helped your evaluation pipeline, please cite it. Machine-readable metadata is in CITATION.cff. The archival software record is on Zenodo under concept DOI 10.5281/zenodo.21110642; the Technical Note (design write-up) under concept DOI 10.5281/zenodo.21230466, also linked from b7n0de.com/proofbundle.

Post-quantum posture (honest, two layers)

proofbundle is not "quantum-proof" or "quantum-safe" as a whole. It combines two cryptographic layers with very different quantum exposure, and it is honest about both:

  • Quantum-robust (hash-based) — SHA-256, RFC 6962 / 9162 Merkle inclusion, RFC 8785 canonicalization, and, among the external time anchors, the OpenTimestamps (Bitcoin hash-chain) and chia-datalayer/v1 (Merkle inclusion) types. Grover only halves the effective bit-strength (SHA-256 keeps a ~128-bit quantum margin, which NIST currently treats as adequate), so these stay secure.
  • Quantum-vulnerable (elliptic-curve / classical PKI, Shor) — the Ed25519 receipt signature; for the chia-datalayer anchor, Chia's BLS12-381 wallet layer; and, for the RFC 3161 anchor, the TSA's own classical (RSA/ECDSA) certificate-chain signature. A large enough quantum computer could forge any of these.

The attack that matters is not decryption but back-dated forgery: an attacker with a quantum computer could mint a fake signature on a tampered receipt. The defense — when a receipt carries a hash-based time anchor (optional, the [anchors] beta extra: OpenTimestamps or chia-datalayer) — is that the anchor proves the original receipt existed before that capability, so a forged receipt has no matching anchor. That protects the evidence long-term even if the signature layer later breaks. A plain receipt with no anchor does not carry this property.

On the witness side, C2SP checkpoints already carry post-quantum ML-DSA-44 (FIPS 204) cosignatures (proofbundle[pq]); a post-quantum payload signature — crypto-agility for the receipt itself — is on the roadmap.

What's in the box

  • Core — Ed25519 signature + RFC 6962 / 9162 Merkle inclusion, verified fully offline. Checks a real Sigstore Rekor proof, so correctness isn't self-referential.
  • Eval receipts — a signed claim (metric ⋈ threshold, n, salted model/dataset commitments, assurance level, provenance) from your run. See EVAL_CLAIM.md.
  • Selective disclosure — SD-JWT (RFC 9901) with Key Binding: prove a threshold while withholding the exact score. Secure-by-default in 3.0.0 (breaking): an unsigned SD-JWT, or one whose disclosures do not bind this bundle, now fails verification (was warn-only).
  • Transparency-log interop — C2SP tlog-checkpoint / cosignature / .tlog-proof, with post-quantum ML-DSA-44 witness cosignatures. Optional Token-Status-List revocation snapshots.
  • Per-sample audit — commit to every sample; an auditor challenges random indices (with a fresh nonce or a public randomness beacon, v1.9) and openings must bind to the signed root. With such an auditor-supplied or beacon-bound challenge, 300 samples catch 1% sample-doctoring with 95% confidence, regardless of run size — a challenge the issuer chose itself does not give this guarantee.
  • Pre-registrationproofbundle prereg <plan> commits to the protocol before the run, so best-of-many publishing becomes visible.
  • Integrations — opt-in inspect_ai end-of-task hook and pytest plugin (emit only when PROOFBUNDLE_EMIT=1 / --proofbundle), plus a Hugging Face Community Evals bridge. See INTEGRATIONS.md, or the end-to-end walkthrough docs/INSPECT_HAPPY_PATH.md — run an eval, get a receipt, verify it offline.
  • External time anchors (v2.0 beta, the [anchors] extra) — an optional anchors[] layer that attaches external evidence of when a commitment or receipt existed, from a party the producer does not control. Two built-in types verify offline: RFC 3161 TSA tokens (against a relying-party-supplied TSA root, see the 3.0.0 trust note below) and OpenTimestamps Bitcoin proofs (honest pending → confirmed lifecycle). A register_anchor_type extension interface lets a third party ship its own fail-closed type; two worked examples ship — a first-party chia-datalayer/v1 (offline Merkle inclusion of a canonical root under a published Chia DataLayer root) and a third-party markovian-provenance/v1 (a wallet-attributable, Bitcoin-anchored stamp). Since 2.1: a verify --require-anchor relying-party gate (optionally narrowed by --anchor-type) turns "no verifying anchor of that type" into a failure layered over the crypto result (exit 3, like --policy); a pending anchor does not satisfy it unless --allow-pending. Plus RFC 3161 hardening — the frozen cert chain is validated at the token's own gen_time, with optional policyOid pinning. Breaking in 3.0.0: an anchor's TRUST now comes only from the relying party — supply a TSA root (--trusted-tsa-root) or a Bitcoin block header (--bitcoin-header), or the equivalent anchors policy keys; the bundle's producer-controlled frozen block is evidence, never a trust source, so --require-anchor without relying-party trust material is unmet (exit 3). An anchor stays detached from the content root, and the statement target is RESERVED for decision receipts. See docs/ANCHORS.md.
  • Universal content root (2.1, jcs-sha256-v1, ADR 0002) — one shared primitive now underlies both the decision-receipt path and the in-toto eval-result / test-result / SVR exports: SHA-256 over the RFC 8785 (JCS) canonical bytes of the full pre-signature Statement, so a content root survives counter-signing and key rotation. The algorithm is a versioned id signed inside the payload (contentRootAlg, default jcs-sha256-v1); a verifier re-serializes with exactly the declared algorithm, never falls back, and an unknown algorithm fails closed. Migration is a compatible evolution, not a cutover: absent contentRootAlg ⇒ the historic legacy-sortkeys-json-v0 mode, so every already-signed 2.0.0 receipt keeps verifying byte-for-byte. This is not a completed universal migration — a CLI flag to select the algorithm is still deferred. Independent cross-implementation (MarkovianProtocol) interop is now proven for RFC 8785 canonicalization + content-root binding (see conformance/decision/crossimpl/); the same corpus additionally verifies a confirmed Bitcoin anchor (block 957504) offline. The external fixture currently reports 12 findings against the enforced decision-receipt/v0.1 validator — recorded as an expected-fail, not hidden — so full schema conformance awaits a further schema-conformant regeneration.
  • Decision Receipts (2.1, vendored decision-receipt/v0.1 predicate) — a separate predicate for agent decisions (not eval metrics): who decided, the proposed action, the policy boundary, digest-bound evidence, the verdict (ALLOW/DENY/REFUSE/ESCALATE/DEFER/OBSERVE), and explicitly what was not checked. proofbundle decision emit|verify with a v0.2 trust policy (trusted_decision_makers, accepted_predicate_ types). An eval receipt says the number is authored and intact; a Decision Receipt says this decision was made by this gate over this evidence — never that the decision was correct. actionOutcome=executed without a signed outcome is self-assertion. See docs/predicates/decision-receipt.md and ADR 0001. That signed outcome is the action-outcome/v0.1 predicate (EXPERIMENTAL, 3.2.0): an executor signs what was actually done, bound by content root to the Decision Receipt, with role separation (executor ≠ decision maker) and execution_proven only when an effect digest backs an executed status. See docs/predicates/action-outcome.md.

Docs

For… Read
Skeptics (why not SHA-256 / Sigstore / trust the issuer) docs/FAQ.md
New to this? plain-terms glossary docs/GLOSSARY.md
Reviewers (30-minute adversarial audit path) docs/REVIEWERS.md
Where every trust anchor comes from docs/TRUST_ANCHORS.md
The demos, tier by tier docs/DEMO.md
The normative format + verification order SPEC.md
Honest comparison to Rekor / in-toto / OMS / ValiChord INTEROP.md
Regulatory mapping (and what to never claim) COMPLIANCE.md
Funders / role fit docs/PROJECT_BRIEF.md
External time anchors + the bring-your-own-type extension interface (v2.0 beta) docs/ANCHORS.md
Preview: TEE-attestation bridge (v2.0 beta) docs/EXPERIMENTAL_ENCLAVE.md

Install

pip install proofbundle                 # core: offline verify + plain emit (dependency-free)
pip install "proofbundle[eval]"          # + eval receipts, prereg, and the demo (adds an RFC 8785 JCS canonicalizer)
pip install "proofbundle[inspect]"      # inspect_ai adapter + hook
pip install "proofbundle[pq]"           # verify ML-DSA-44 (post-quantum) witness cosignatures

Requires Python 3.10+. The verify path never rolls its own crypto — Ed25519 comes from cryptography; Merkle hashing is RFC 6962.

Status, scope and roadmap

Beta, SemVer-committed, with a CI test suite behind a mutation gate + property-based parser fuzzing. Correctness is anchored to external RFC 6962 vectors and a real Rekor proof, not just its own bundles. It is not a log service, a full in-toto client, a TEE, a consensus network, or a compliance product by itself — it is the small, offline, standards-native receipt layer between them. Security policy: SECURITY.md.

Roadmap (stated honestly, not yet built):

  • A post-quantum payload signature (crypto-agility for the receipt itself) is on the roadmap; today the post-quantum coverage is witness-side ML-DSA-44 cosignatures only.
  • A CLI flag to select the content-root algorithm is still deferred (jcs-sha256-v1 is the signed default).

Already shipped, preview/experimental maturity (not on the roadmap above — built, tested, install extra [experimental], but API/wire-format may still change without deprecation):

  • A TEE-attestation bridge (RATS/EAT, RFC 9334 + RFC 9711) that makes assurance_level = enclave_attested independently verifiable — see docs/EXPERIMENTAL_ENCLAVE.md.

Contributing

See CONTRIBUTING.md and the Code of Conduct. Good first issues are labeled good-first-issue; security findings go through SECURITY.md. The verifier core aims to stay small, dependency-light, and correct.

License

MIT — see LICENSE.


proofbundle is part of b7n0de, Verified AI Work · b7n0de.com

Download files

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

Source Distribution

proofbundle-3.6.0.tar.gz (612.5 kB view details)

Uploaded Source

Built Distribution

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

proofbundle-3.6.0-py3-none-any.whl (357.2 kB view details)

Uploaded Python 3

File details

Details for the file proofbundle-3.6.0.tar.gz.

File metadata

  • Download URL: proofbundle-3.6.0.tar.gz
  • Upload date:
  • Size: 612.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for proofbundle-3.6.0.tar.gz
Algorithm Hash digest
SHA256 e4d8586fc5e1205dca589a86291f20ceacb24b0fb1e6a9709b829be8c24a1554
MD5 99f6599a8eb3164bc3f2627b815bf785
BLAKE2b-256 437a1d87b34f5211bd6398796e3dbc70a9d6aa9446ee0cfb74e283357f7ee748

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofbundle-3.6.0.tar.gz:

Publisher: release.yml on b7n0de/proofbundle

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proofbundle-3.6.0-py3-none-any.whl.

File metadata

  • Download URL: proofbundle-3.6.0-py3-none-any.whl
  • Upload date:
  • Size: 357.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for proofbundle-3.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c65a0bf254b5cf65ecd245669691cbd4dd8f08b1a65695a28ba51859f5f7544c
MD5 a998147f3cf00c2cc1578978b25c05ea
BLAKE2b-256 36c306c01fd27697774616e1c6438196957cf0193749e83f59c819aa99c7421b

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofbundle-3.6.0-py3-none-any.whl:

Publisher: release.yml on b7n0de/proofbundle

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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