proofbundle 1.2.0__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {proofbundle-1.2.0/src/proofbundle.egg-info → proofbundle-1.4.0}/PKG-INFO +103 -22
- {proofbundle-1.2.0 → proofbundle-1.4.0}/README.md +95 -17
- {proofbundle-1.2.0 → proofbundle-1.4.0}/pyproject.toml +12 -7
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/__init__.py +17 -1
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/adapters/__init__.py +3 -1
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/adapters/lm_eval.py +3 -1
- proofbundle-1.4.0/src/proofbundle/adapters/promptfoo.py +131 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/bundle.py +32 -14
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/checkpoint.py +172 -31
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/cli.py +90 -3
- proofbundle-1.4.0/src/proofbundle/hf_evals.py +185 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/kbjwt.py +9 -8
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/sdjwt.py +5 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/sdjwt_issue.py +29 -3
- proofbundle-1.4.0/src/proofbundle/statuslist.py +194 -0
- proofbundle-1.4.0/src/proofbundle/tlogproof.py +175 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0/src/proofbundle.egg-info}/PKG-INFO +103 -22
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle.egg-info/SOURCES.txt +10 -1
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle.egg-info/requires.txt +3 -4
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_cosignature.py +15 -18
- proofbundle-1.4.0/tests/test_cosignature_mldsa.py +153 -0
- proofbundle-1.4.0/tests/test_hf_evals.py +152 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_kbjwt.py +31 -5
- proofbundle-1.4.0/tests/test_promptfoo.py +177 -0
- proofbundle-1.4.0/tests/test_statuslist.py +143 -0
- proofbundle-1.4.0/tests/test_tlogproof.py +163 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/LICENSE +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/setup.cfg +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/_inspect_registry.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/_integration.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/adapters/eee.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/adapters/inspect_ai.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/dsse.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/eee_eval_schema.json +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/emit.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/errors.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/evalclaim.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/inspect_hook.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/intoto.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/merkle.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/py.typed +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/pytest_plugin.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle/signature.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle.egg-info/dependency_links.txt +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle.egg-info/entry_points.txt +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/src/proofbundle.egg-info/top_level.txt +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_adapters.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_adversarial.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_bundle.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_bundle_robustness.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_checkpoint.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_cli.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_cli_eval.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_eee.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_emit.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_eval_claim_schema.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_evalclaim.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_examples.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_inspect_hook.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_intoto.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_intoto_dsse.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_merkle.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_merkle_property.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_pytest_plugin.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_rekor_interop.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_rfc6962_external_vectors.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_schema.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_sdjwt_issue.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_sdjwt_reference.py +0 -0
- {proofbundle-1.2.0 → proofbundle-1.4.0}/tests/test_signature.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofbundle
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Emit and verify portable cryptographic evidence bundles, offline: Ed25519 + RFC 6962 Merkle + optional SD-JWT.
|
|
5
5
|
Author: Konrad Gruszka
|
|
6
6
|
License: MIT
|
|
@@ -14,12 +14,13 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Topic :: Security :: Cryptography
|
|
22
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
License-File: LICENSE
|
|
25
26
|
Requires-Dist: cryptography>=42
|
|
@@ -27,10 +28,12 @@ Provides-Extra: sdjwt
|
|
|
27
28
|
Provides-Extra: eval
|
|
28
29
|
Requires-Dist: rfc8785>=0.1.4; extra == "eval"
|
|
29
30
|
Provides-Extra: adapters
|
|
31
|
+
Provides-Extra: pq
|
|
32
|
+
Requires-Dist: cryptography>=48; extra == "pq"
|
|
30
33
|
Provides-Extra: pytest
|
|
31
34
|
Requires-Dist: pytest>=7; extra == "pytest"
|
|
32
35
|
Provides-Extra: inspect
|
|
33
|
-
Requires-Dist: inspect_ai<0.4,>=0.3.112;
|
|
36
|
+
Requires-Dist: inspect_ai<0.4,>=0.3.112; extra == "inspect"
|
|
34
37
|
Provides-Extra: dev
|
|
35
38
|
Requires-Dist: pytest>=7; extra == "dev"
|
|
36
39
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
@@ -40,7 +43,7 @@ Requires-Dist: build>=1; extra == "dev"
|
|
|
40
43
|
Requires-Dist: hypothesis>=6; extra == "dev"
|
|
41
44
|
Requires-Dist: rfc8785>=0.1.4; extra == "dev"
|
|
42
45
|
Requires-Dist: sd-jwt>=0.10; extra == "dev"
|
|
43
|
-
Requires-Dist: inspect_ai<0.4,>=0.3.112;
|
|
46
|
+
Requires-Dist: inspect_ai<0.4,>=0.3.112; extra == "dev"
|
|
44
47
|
Dynamic: license-file
|
|
45
48
|
|
|
46
49
|
<div align="center">
|
|
@@ -53,9 +56,11 @@ Dynamic: license-file
|
|
|
53
56
|
<h1>proofbundle</h1>
|
|
54
57
|
|
|
55
58
|
**An offline verifier for AI eval receipts. Standards-native: Ed25519 signature,
|
|
56
|
-
RFC 6962 transparency-log Merkle anchoring,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
RFC 6962 transparency-log Merkle anchoring, C2SP witnessed checkpoints and
|
|
60
|
+
`.tlog-proof` transparent signatures (post-quantum-ready witnesses, ML-DSA-44),
|
|
61
|
+
SD-JWT (RFC 9901) selective disclosure with Key Binding, Token-Status-List
|
|
62
|
+
revocation snapshots, aligned to the in-toto test-result predicate. One portable
|
|
63
|
+
file, no server, no network.**
|
|
59
64
|
|
|
60
65
|
[](https://github.com/b7n0de/proofbundle/actions/workflows/ci.yml)
|
|
61
66
|
[](https://pypi.org/project/proofbundle/)
|
|
@@ -65,6 +70,9 @@ no server, no network.**
|
|
|
65
70
|
[](https://github.com/astral-sh/ruff)
|
|
66
71
|
[](https://slsa.dev)
|
|
67
72
|
[-D6248A.svg)](https://pypi.org/project/proofbundle/)
|
|
73
|
+
[](https://github.com/C2SP/C2SP/blob/main/tlog-cosignature.md)
|
|
74
|
+
[](scripts/mutation_check.py)
|
|
75
|
+
[](https://github.com/C2SP/C2SP/blob/main/tlog-proof.md)
|
|
68
76
|
<!-- DOI badge placeholder: enable Zenodo archiving for this repo, then add the Zenodo concept-DOI
|
|
69
77
|
badge here (and the DOI to CITATION.cff) once Zenodo assigns one on the next release. No DOI has
|
|
70
78
|
been assigned yet (no archived record exists at build time) — tracked in the human checklist. -->
|
|
@@ -73,11 +81,12 @@ no server, no network.**
|
|
|
73
81
|
|
|
74
82
|
**At a glance:** `proofbundle emit` signs and anchors a payload; `proofbundle
|
|
75
83
|
verify` checks one self-contained `bundle.json` with three offline cryptographic
|
|
76
|
-
checks → `OK` or `FAILED`. No network, no daemon, no own crypto.
|
|
84
|
+
checks → `OK` or `FAILED`. No network, no daemon, no own crypto. 209 tests + a CI mutation gate.
|
|
77
85
|
|
|
78
86
|
## Contents
|
|
79
87
|
|
|
80
88
|
- [Why](#why)
|
|
89
|
+
- [What a receipt proves](#what-a-receipt-proves-and-what-it-does-not)
|
|
81
90
|
- [What it verifies](#what-it-verifies)
|
|
82
91
|
- [How it fits together](#how-it-fits-together)
|
|
83
92
|
- [Install](#install)
|
|
@@ -88,6 +97,7 @@ checks → `OK` or `FAILED`. No network, no daemon, no own crypto. 148 tests.
|
|
|
88
97
|
- [Bundle format](#bundle-format-proofbundlev01)
|
|
89
98
|
- [Eval receipts](#eval-receipts)
|
|
90
99
|
- [Security notes and scope](#security-notes-and-scope-stated-honestly)
|
|
100
|
+
- [Compliance](#compliance)
|
|
91
101
|
- [Roadmap](#roadmap)
|
|
92
102
|
- [Contributing](#contributing)
|
|
93
103
|
- [License](#license)
|
|
@@ -148,12 +158,19 @@ A bundle is a single JSON document. `proofbundle` checks, offline:
|
|
|
148
158
|
Certificate Transparency)
|
|
149
159
|
3. **sd-jwt** (optional) — an embedded SD-JWT selective-disclosure credential is
|
|
150
160
|
well formed, and if an issuer key is given, correctly issuer-signed
|
|
151
|
-
4. **sd-jwt-key-binding** (optional, v1.2) —
|
|
152
|
-
|
|
153
|
-
`iat`/`aud`/`nonce`/`sd_hash` are
|
|
154
|
-
disclosure set, and the signature verifies under the
|
|
155
|
-
holder key. A present-but-broken KB-JWT fails the bundle
|
|
156
|
-
|
|
161
|
+
4. **sd-jwt-key-binding** (optional, v1.2; hardened v1.3) — proof-of-possession, **fail-closed**.
|
|
162
|
+
The check runs only once the issuer signature itself verified (an unauthenticated SD-JWT can
|
|
163
|
+
never report a valid holder binding). Then: `typ` is `kb+jwt`, `iat`/`aud`/`nonce`/`sd_hash` are
|
|
164
|
+
present, `sd_hash` binds the exact presented disclosure set, and the signature verifies under the
|
|
165
|
+
issuer-bound `cnf.jwk` holder key. A present-but-broken KB-JWT fails the bundle; and if the issuer
|
|
166
|
+
**bound** a `cnf` holder key but the presentation carries **no** KB-JWT, that fails too — stripping
|
|
167
|
+
the binding is a bearer downgrade, not a valid receipt. `verify --aud/--nonce` enforce RFC 9901
|
|
168
|
+
§7.3 audience/replay binding when the relying party supplies them.
|
|
169
|
+
|
|
170
|
+
Beyond the single-file bundle, the library also verifies, all offline: a **witnessed C2SP checkpoint**
|
|
171
|
+
(`verify_witnessed_checkpoint`, Ed25519 + post-quantum ML-DSA-44 cosignatures — the quorum counts distinct
|
|
172
|
+
witness **keys**, not names, so one key under many names can never stuff a threshold), a **C2SP
|
|
173
|
+
`.tlog-proof`** (`proofbundle verify-proof`), and a **Token Status List** revocation snapshot.
|
|
157
174
|
|
|
158
175
|
The verifier treats the payload as opaque bytes. It proves that these exact
|
|
159
176
|
bytes were signed and anchored, not what they mean. That is on purpose: it keeps
|
|
@@ -192,7 +209,7 @@ flowchart LR
|
|
|
192
209
|
pip install proofbundle
|
|
193
210
|
```
|
|
194
211
|
|
|
195
|
-
Requires Python 3.
|
|
212
|
+
Requires Python 3.10+ and [`cryptography`](https://cryptography.io). Signature
|
|
196
213
|
math is delegated to `cryptography`; this project never rolls its own crypto.
|
|
197
214
|
The Merkle and SD-JWT logic is pure standard library.
|
|
198
215
|
|
|
@@ -214,7 +231,7 @@ proofbundle verify examples/example_bundle.json
|
|
|
214
231
|
```
|
|
215
232
|
|
|
216
233
|
<div align="center">
|
|
217
|
-
<img src="assets/demo.svg" alt="proofbundle verify output:
|
|
234
|
+
<img src="assets/demo.svg" alt="proofbundle verify output: PASS checks and OK" width="680">
|
|
218
235
|
</div>
|
|
219
236
|
|
|
220
237
|
Machine-readable output and a non-zero exit code on failure:
|
|
@@ -312,6 +329,41 @@ both log-signed **and** cosigned by a quorum of distinct witnesses, offline,
|
|
|
312
329
|
which rules out a split view by the log operator. This is the same
|
|
313
330
|
witnessed-checkpoint pattern Rekor v2 (GA October 2025) institutionalizes.
|
|
314
331
|
|
|
332
|
+
Since v1.3 the witness layer is complete: **ML-DSA-44 cosignatures** (C2SP type
|
|
333
|
+
0x06, FIPS 204 — post-quantum, the spec's SHOULD for new witness deployments;
|
|
334
|
+
optional `proofbundle[pq]` extra, Ed25519 stays the default) and the
|
|
335
|
+
**[C2SP tlog-proof](https://github.com/C2SP/C2SP/blob/main/tlog-proof.md)** file
|
|
336
|
+
format (`.tlog-proof`) — index + inclusion proof + (co)signed checkpoint in one
|
|
337
|
+
portable, offline-verifiable "transparent signature":
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
proofbundle verify-proof receipt.tlog-proof --payload-file result.json \
|
|
341
|
+
--log-vkey <log-vkey> --witness-vkey <w1> --witness-vkey <w2> --threshold 2
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Revocation joins the offline model too: a receipt SD-JWT can carry a Token
|
|
345
|
+
Status List `status` claim, checked against a signed, **bundled list snapshot**
|
|
346
|
+
(`proofbundle.statuslist`) — staleness is reported explicitly instead of
|
|
347
|
+
assumed away.
|
|
348
|
+
|
|
349
|
+
```mermaid
|
|
350
|
+
flowchart LR
|
|
351
|
+
B["bundle.json<br/>(signed + anchored receipt)"] --> CK["tlog-checkpoint<br/>(signed note over the root)"]
|
|
352
|
+
CK --> W1["witness cosignature<br/>Ed25519 · 0x04"]
|
|
353
|
+
CK --> W2["witness cosignature<br/>ML-DSA-44 · 0x06 · PQ"]
|
|
354
|
+
B -->|"index + inclusion proof"| TP[".tlog-proof<br/>transparent signature"]
|
|
355
|
+
W1 --> TP
|
|
356
|
+
W2 --> TP
|
|
357
|
+
TP --> V{{"proofbundle verify-proof<br/>offline · k-of-n policy"}}
|
|
358
|
+
V --> R1["log signature"]
|
|
359
|
+
V --> R2["witness quorum"]
|
|
360
|
+
V --> R3["merkle inclusion"]
|
|
361
|
+
|
|
362
|
+
style V fill:#D6248A,stroke:#D6248A,color:#fff
|
|
363
|
+
style W2 fill:#1e2327,stroke:#D6248A,color:#fff
|
|
364
|
+
style TP fill:#1e2327,stroke:#D6248A,color:#fff
|
|
365
|
+
```
|
|
366
|
+
|
|
315
367
|
## Bundle format (`proofbundle/v0.1`)
|
|
316
368
|
|
|
317
369
|
The format is specified normatively in [SPEC.md](SPEC.md) (fields, encodings,
|
|
@@ -359,6 +411,11 @@ The scope is deliberately narrow. It does exactly what it says and no more:
|
|
|
359
411
|
- Witness cosignatures (v1.2) prove consistency *observations* by the listed
|
|
360
412
|
witnesses; real split-view resistance additionally requires the witnesses to be
|
|
361
413
|
operationally independent — a deployment property no file format can supply.
|
|
414
|
+
- Post-quantum: witness **cosignatures** may be ML-DSA-44 (v1.3, `[pq]` extra) — the layer where
|
|
415
|
+
the ecosystem (C2SP, Sigstore) is deploying PQ first. Primary signatures stay Ed25519 until the
|
|
416
|
+
ecosystem moves; a configured ML-DSA witness on a build without PQ support fails closed.
|
|
417
|
+
- Revocation (v1.3) is checked against a SIGNED status-list snapshot the relying party supplies;
|
|
418
|
+
its freshness is reported and only judged against the caller's clock — never assumed.
|
|
362
419
|
- The verifier does not fetch anything. Trust anchors (the signer key, the
|
|
363
420
|
expected root, witness keys) are inputs you supply out of band.
|
|
364
421
|
- No custom cryptography. Ed25519 comes from `cryptography`; Merkle hashing is
|
|
@@ -429,7 +486,7 @@ threshold was met without revealing the model or the data. See [INTEROP.md](INTE
|
|
|
429
486
|
[lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) `results_*.json` (the
|
|
430
487
|
genuine `acc,none` filter-suffix format). **`proofbundle.adapters.from_eee_dataset`** (v0.9) reads an
|
|
431
488
|
Every Eval Ever v0.2.2 aggregate JSON and builds a signed receipt — validated against the vendored EEE
|
|
432
|
-
schema, with **no runtime import** of `every_eval_ever` (it needs Python 3.12; proofbundle stays 3.
|
|
489
|
+
schema, with **no runtime import** of `every_eval_ever` (it needs Python 3.12; proofbundle stays 3.10+).
|
|
433
490
|
- **in-toto test-result export, DSSE-signed** (v0.9) — `proofbundle.intoto.export_intoto_dsse(claim,
|
|
434
491
|
signer)` emits the receipt as a DSSE-signed in-toto Statement v1 with the **generic
|
|
435
492
|
`test-result/v0.1` predicate** (result PASSED/FAILED, `configuration` ResourceDescriptors), so a generic
|
|
@@ -455,6 +512,16 @@ bundle-bound views.
|
|
|
455
512
|
Every release ships **PEP 740 attestations** (Trusted Publishing) + an SLSA build-provenance
|
|
456
513
|
attestation — see [SECURITY.md](SECURITY.md).
|
|
457
514
|
|
|
515
|
+
## Compliance
|
|
516
|
+
|
|
517
|
+
The EU AI Act's record-keeping duty for high-risk systems (Article 12 — automatic,
|
|
518
|
+
tamper-evident, independently verifiable logs) applies from **August 2, 2026**;
|
|
519
|
+
the GPAI Code of Practice expects documented, credible model evaluations.
|
|
520
|
+
**[COMPLIANCE.md](COMPLIANCE.md)** maps — honestly, and without claiming more
|
|
521
|
+
than the cryptography supports — what a verified receipt covers under Article 12,
|
|
522
|
+
NIST AI RMF MEASURE, and the logging standards in flight (prEN 18229-1,
|
|
523
|
+
ISO/IEC DIS 24970), including the anti-patterns no one should claim.
|
|
524
|
+
|
|
458
525
|
## Roadmap
|
|
459
526
|
|
|
460
527
|
- **v0.1** — the offline verifier plus a real example bundle.
|
|
@@ -475,13 +542,27 @@ attestation — see [SECURITY.md](SECURITY.md).
|
|
|
475
542
|
eval (end-of-task hook) or a pytest run (pytest11 plugin), plus a composite GitHub Action.
|
|
476
543
|
- **v1.1** — trust hardening: a signed `assurance_level`, a THREAT_MODEL, a self_attested-
|
|
477
544
|
without-prereg warning, model-swap + replay + withheld-field checks, and an adversarial No-Fake-PASS suite.
|
|
478
|
-
- **v1.2
|
|
545
|
+
- **v1.2** — holder binding + witness quorum: **Key Binding JWT** verification
|
|
479
546
|
(RFC 9901 §4.3, fail-closed; closes #1) with `cnf.jwk` issuance and holder presentation, **C2SP
|
|
480
547
|
tlog-cosignature** verification (Ed25519 cosignature/v1, witness quorum, split-view resistance), and
|
|
481
548
|
`verify --verbose` with the recomputed Merkle root (closes #2).
|
|
482
|
-
- **
|
|
483
|
-
|
|
484
|
-
|
|
549
|
+
- **v1.3** — the portable proof: **C2SP tlog-proof** emit/verify (+
|
|
550
|
+
`verify-proof` CLI), **ML-DSA-44** witness cosignatures (post-quantum, `[pq]` extra), **Token
|
|
551
|
+
Status List** snapshot verification (offline revocation), SD-JWT VC markers (`dc+sd-jwt`,
|
|
552
|
+
`vct`, `status`), COMPLIANCE.md (EU AI Act Art. 12 / NIST AI RMF mapping), and the mutation
|
|
553
|
+
suite as a CI gate.
|
|
554
|
+
- **v1.4 (current release)** — distribution: a **promptfoo** adapter (results.json v3 →
|
|
555
|
+
pass_rate receipt), the **Hugging Face Community Evals bridge** (`pb1.` receipt tokens for the
|
|
556
|
+
`.eval_results` `verifyToken` field + a strict YAML entry emitter — proofbundle-verifiable,
|
|
557
|
+
explicitly NOT the HF-internal badge token), `hf-token` CLI, and the **Python 3.10+ floor**
|
|
558
|
+
(3.9 is EOL since 2025-10).
|
|
559
|
+
- **v2.0 (next major, research differentiation)** — **per-sample Merkle receipts**: the payload carries a
|
|
560
|
+
Merkle root over *individual* sample results, so a verifier can force a random-sample re-check instead of
|
|
561
|
+
trusting only the reported aggregate. This is the THREAT_MODEL's one named structural gap ("forced random
|
|
562
|
+
sub-sampling"), and closing it is the road from *authorship* to *sampled correctness*.
|
|
563
|
+
- **Deferred** (explicitly not yet built, stated honestly) — full SD-JWT VC conformance + `vct`
|
|
564
|
+
type-metadata resolution (pre-IESG), an official in-toto eval predicate (proposal path via
|
|
565
|
+
OpenSSF/CoSAI), and a full in-toto client.
|
|
485
566
|
|
|
486
567
|
## Contributing
|
|
487
568
|
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
<h1>proofbundle</h1>
|
|
9
9
|
|
|
10
10
|
**An offline verifier for AI eval receipts. Standards-native: Ed25519 signature,
|
|
11
|
-
RFC 6962 transparency-log Merkle anchoring,
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
RFC 6962 transparency-log Merkle anchoring, C2SP witnessed checkpoints and
|
|
12
|
+
`.tlog-proof` transparent signatures (post-quantum-ready witnesses, ML-DSA-44),
|
|
13
|
+
SD-JWT (RFC 9901) selective disclosure with Key Binding, Token-Status-List
|
|
14
|
+
revocation snapshots, aligned to the in-toto test-result predicate. One portable
|
|
15
|
+
file, no server, no network.**
|
|
14
16
|
|
|
15
17
|
[](https://github.com/b7n0de/proofbundle/actions/workflows/ci.yml)
|
|
16
18
|
[](https://pypi.org/project/proofbundle/)
|
|
@@ -20,6 +22,9 @@ no server, no network.**
|
|
|
20
22
|
[](https://github.com/astral-sh/ruff)
|
|
21
23
|
[](https://slsa.dev)
|
|
22
24
|
[-D6248A.svg)](https://pypi.org/project/proofbundle/)
|
|
25
|
+
[](https://github.com/C2SP/C2SP/blob/main/tlog-cosignature.md)
|
|
26
|
+
[](scripts/mutation_check.py)
|
|
27
|
+
[](https://github.com/C2SP/C2SP/blob/main/tlog-proof.md)
|
|
23
28
|
<!-- DOI badge placeholder: enable Zenodo archiving for this repo, then add the Zenodo concept-DOI
|
|
24
29
|
badge here (and the DOI to CITATION.cff) once Zenodo assigns one on the next release. No DOI has
|
|
25
30
|
been assigned yet (no archived record exists at build time) — tracked in the human checklist. -->
|
|
@@ -28,11 +33,12 @@ no server, no network.**
|
|
|
28
33
|
|
|
29
34
|
**At a glance:** `proofbundle emit` signs and anchors a payload; `proofbundle
|
|
30
35
|
verify` checks one self-contained `bundle.json` with three offline cryptographic
|
|
31
|
-
checks → `OK` or `FAILED`. No network, no daemon, no own crypto.
|
|
36
|
+
checks → `OK` or `FAILED`. No network, no daemon, no own crypto. 209 tests + a CI mutation gate.
|
|
32
37
|
|
|
33
38
|
## Contents
|
|
34
39
|
|
|
35
40
|
- [Why](#why)
|
|
41
|
+
- [What a receipt proves](#what-a-receipt-proves-and-what-it-does-not)
|
|
36
42
|
- [What it verifies](#what-it-verifies)
|
|
37
43
|
- [How it fits together](#how-it-fits-together)
|
|
38
44
|
- [Install](#install)
|
|
@@ -43,6 +49,7 @@ checks → `OK` or `FAILED`. No network, no daemon, no own crypto. 148 tests.
|
|
|
43
49
|
- [Bundle format](#bundle-format-proofbundlev01)
|
|
44
50
|
- [Eval receipts](#eval-receipts)
|
|
45
51
|
- [Security notes and scope](#security-notes-and-scope-stated-honestly)
|
|
52
|
+
- [Compliance](#compliance)
|
|
46
53
|
- [Roadmap](#roadmap)
|
|
47
54
|
- [Contributing](#contributing)
|
|
48
55
|
- [License](#license)
|
|
@@ -103,12 +110,19 @@ A bundle is a single JSON document. `proofbundle` checks, offline:
|
|
|
103
110
|
Certificate Transparency)
|
|
104
111
|
3. **sd-jwt** (optional) — an embedded SD-JWT selective-disclosure credential is
|
|
105
112
|
well formed, and if an issuer key is given, correctly issuer-signed
|
|
106
|
-
4. **sd-jwt-key-binding** (optional, v1.2) —
|
|
107
|
-
|
|
108
|
-
`iat`/`aud`/`nonce`/`sd_hash` are
|
|
109
|
-
disclosure set, and the signature verifies under the
|
|
110
|
-
holder key. A present-but-broken KB-JWT fails the bundle
|
|
111
|
-
|
|
113
|
+
4. **sd-jwt-key-binding** (optional, v1.2; hardened v1.3) — proof-of-possession, **fail-closed**.
|
|
114
|
+
The check runs only once the issuer signature itself verified (an unauthenticated SD-JWT can
|
|
115
|
+
never report a valid holder binding). Then: `typ` is `kb+jwt`, `iat`/`aud`/`nonce`/`sd_hash` are
|
|
116
|
+
present, `sd_hash` binds the exact presented disclosure set, and the signature verifies under the
|
|
117
|
+
issuer-bound `cnf.jwk` holder key. A present-but-broken KB-JWT fails the bundle; and if the issuer
|
|
118
|
+
**bound** a `cnf` holder key but the presentation carries **no** KB-JWT, that fails too — stripping
|
|
119
|
+
the binding is a bearer downgrade, not a valid receipt. `verify --aud/--nonce` enforce RFC 9901
|
|
120
|
+
§7.3 audience/replay binding when the relying party supplies them.
|
|
121
|
+
|
|
122
|
+
Beyond the single-file bundle, the library also verifies, all offline: a **witnessed C2SP checkpoint**
|
|
123
|
+
(`verify_witnessed_checkpoint`, Ed25519 + post-quantum ML-DSA-44 cosignatures — the quorum counts distinct
|
|
124
|
+
witness **keys**, not names, so one key under many names can never stuff a threshold), a **C2SP
|
|
125
|
+
`.tlog-proof`** (`proofbundle verify-proof`), and a **Token Status List** revocation snapshot.
|
|
112
126
|
|
|
113
127
|
The verifier treats the payload as opaque bytes. It proves that these exact
|
|
114
128
|
bytes were signed and anchored, not what they mean. That is on purpose: it keeps
|
|
@@ -147,7 +161,7 @@ flowchart LR
|
|
|
147
161
|
pip install proofbundle
|
|
148
162
|
```
|
|
149
163
|
|
|
150
|
-
Requires Python 3.
|
|
164
|
+
Requires Python 3.10+ and [`cryptography`](https://cryptography.io). Signature
|
|
151
165
|
math is delegated to `cryptography`; this project never rolls its own crypto.
|
|
152
166
|
The Merkle and SD-JWT logic is pure standard library.
|
|
153
167
|
|
|
@@ -169,7 +183,7 @@ proofbundle verify examples/example_bundle.json
|
|
|
169
183
|
```
|
|
170
184
|
|
|
171
185
|
<div align="center">
|
|
172
|
-
<img src="assets/demo.svg" alt="proofbundle verify output:
|
|
186
|
+
<img src="assets/demo.svg" alt="proofbundle verify output: PASS checks and OK" width="680">
|
|
173
187
|
</div>
|
|
174
188
|
|
|
175
189
|
Machine-readable output and a non-zero exit code on failure:
|
|
@@ -267,6 +281,41 @@ both log-signed **and** cosigned by a quorum of distinct witnesses, offline,
|
|
|
267
281
|
which rules out a split view by the log operator. This is the same
|
|
268
282
|
witnessed-checkpoint pattern Rekor v2 (GA October 2025) institutionalizes.
|
|
269
283
|
|
|
284
|
+
Since v1.3 the witness layer is complete: **ML-DSA-44 cosignatures** (C2SP type
|
|
285
|
+
0x06, FIPS 204 — post-quantum, the spec's SHOULD for new witness deployments;
|
|
286
|
+
optional `proofbundle[pq]` extra, Ed25519 stays the default) and the
|
|
287
|
+
**[C2SP tlog-proof](https://github.com/C2SP/C2SP/blob/main/tlog-proof.md)** file
|
|
288
|
+
format (`.tlog-proof`) — index + inclusion proof + (co)signed checkpoint in one
|
|
289
|
+
portable, offline-verifiable "transparent signature":
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
proofbundle verify-proof receipt.tlog-proof --payload-file result.json \
|
|
293
|
+
--log-vkey <log-vkey> --witness-vkey <w1> --witness-vkey <w2> --threshold 2
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Revocation joins the offline model too: a receipt SD-JWT can carry a Token
|
|
297
|
+
Status List `status` claim, checked against a signed, **bundled list snapshot**
|
|
298
|
+
(`proofbundle.statuslist`) — staleness is reported explicitly instead of
|
|
299
|
+
assumed away.
|
|
300
|
+
|
|
301
|
+
```mermaid
|
|
302
|
+
flowchart LR
|
|
303
|
+
B["bundle.json<br/>(signed + anchored receipt)"] --> CK["tlog-checkpoint<br/>(signed note over the root)"]
|
|
304
|
+
CK --> W1["witness cosignature<br/>Ed25519 · 0x04"]
|
|
305
|
+
CK --> W2["witness cosignature<br/>ML-DSA-44 · 0x06 · PQ"]
|
|
306
|
+
B -->|"index + inclusion proof"| TP[".tlog-proof<br/>transparent signature"]
|
|
307
|
+
W1 --> TP
|
|
308
|
+
W2 --> TP
|
|
309
|
+
TP --> V{{"proofbundle verify-proof<br/>offline · k-of-n policy"}}
|
|
310
|
+
V --> R1["log signature"]
|
|
311
|
+
V --> R2["witness quorum"]
|
|
312
|
+
V --> R3["merkle inclusion"]
|
|
313
|
+
|
|
314
|
+
style V fill:#D6248A,stroke:#D6248A,color:#fff
|
|
315
|
+
style W2 fill:#1e2327,stroke:#D6248A,color:#fff
|
|
316
|
+
style TP fill:#1e2327,stroke:#D6248A,color:#fff
|
|
317
|
+
```
|
|
318
|
+
|
|
270
319
|
## Bundle format (`proofbundle/v0.1`)
|
|
271
320
|
|
|
272
321
|
The format is specified normatively in [SPEC.md](SPEC.md) (fields, encodings,
|
|
@@ -314,6 +363,11 @@ The scope is deliberately narrow. It does exactly what it says and no more:
|
|
|
314
363
|
- Witness cosignatures (v1.2) prove consistency *observations* by the listed
|
|
315
364
|
witnesses; real split-view resistance additionally requires the witnesses to be
|
|
316
365
|
operationally independent — a deployment property no file format can supply.
|
|
366
|
+
- Post-quantum: witness **cosignatures** may be ML-DSA-44 (v1.3, `[pq]` extra) — the layer where
|
|
367
|
+
the ecosystem (C2SP, Sigstore) is deploying PQ first. Primary signatures stay Ed25519 until the
|
|
368
|
+
ecosystem moves; a configured ML-DSA witness on a build without PQ support fails closed.
|
|
369
|
+
- Revocation (v1.3) is checked against a SIGNED status-list snapshot the relying party supplies;
|
|
370
|
+
its freshness is reported and only judged against the caller's clock — never assumed.
|
|
317
371
|
- The verifier does not fetch anything. Trust anchors (the signer key, the
|
|
318
372
|
expected root, witness keys) are inputs you supply out of band.
|
|
319
373
|
- No custom cryptography. Ed25519 comes from `cryptography`; Merkle hashing is
|
|
@@ -384,7 +438,7 @@ threshold was met without revealing the model or the data. See [INTEROP.md](INTE
|
|
|
384
438
|
[lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) `results_*.json` (the
|
|
385
439
|
genuine `acc,none` filter-suffix format). **`proofbundle.adapters.from_eee_dataset`** (v0.9) reads an
|
|
386
440
|
Every Eval Ever v0.2.2 aggregate JSON and builds a signed receipt — validated against the vendored EEE
|
|
387
|
-
schema, with **no runtime import** of `every_eval_ever` (it needs Python 3.12; proofbundle stays 3.
|
|
441
|
+
schema, with **no runtime import** of `every_eval_ever` (it needs Python 3.12; proofbundle stays 3.10+).
|
|
388
442
|
- **in-toto test-result export, DSSE-signed** (v0.9) — `proofbundle.intoto.export_intoto_dsse(claim,
|
|
389
443
|
signer)` emits the receipt as a DSSE-signed in-toto Statement v1 with the **generic
|
|
390
444
|
`test-result/v0.1` predicate** (result PASSED/FAILED, `configuration` ResourceDescriptors), so a generic
|
|
@@ -410,6 +464,16 @@ bundle-bound views.
|
|
|
410
464
|
Every release ships **PEP 740 attestations** (Trusted Publishing) + an SLSA build-provenance
|
|
411
465
|
attestation — see [SECURITY.md](SECURITY.md).
|
|
412
466
|
|
|
467
|
+
## Compliance
|
|
468
|
+
|
|
469
|
+
The EU AI Act's record-keeping duty for high-risk systems (Article 12 — automatic,
|
|
470
|
+
tamper-evident, independently verifiable logs) applies from **August 2, 2026**;
|
|
471
|
+
the GPAI Code of Practice expects documented, credible model evaluations.
|
|
472
|
+
**[COMPLIANCE.md](COMPLIANCE.md)** maps — honestly, and without claiming more
|
|
473
|
+
than the cryptography supports — what a verified receipt covers under Article 12,
|
|
474
|
+
NIST AI RMF MEASURE, and the logging standards in flight (prEN 18229-1,
|
|
475
|
+
ISO/IEC DIS 24970), including the anti-patterns no one should claim.
|
|
476
|
+
|
|
413
477
|
## Roadmap
|
|
414
478
|
|
|
415
479
|
- **v0.1** — the offline verifier plus a real example bundle.
|
|
@@ -430,13 +494,27 @@ attestation — see [SECURITY.md](SECURITY.md).
|
|
|
430
494
|
eval (end-of-task hook) or a pytest run (pytest11 plugin), plus a composite GitHub Action.
|
|
431
495
|
- **v1.1** — trust hardening: a signed `assurance_level`, a THREAT_MODEL, a self_attested-
|
|
432
496
|
without-prereg warning, model-swap + replay + withheld-field checks, and an adversarial No-Fake-PASS suite.
|
|
433
|
-
- **v1.2
|
|
497
|
+
- **v1.2** — holder binding + witness quorum: **Key Binding JWT** verification
|
|
434
498
|
(RFC 9901 §4.3, fail-closed; closes #1) with `cnf.jwk` issuance and holder presentation, **C2SP
|
|
435
499
|
tlog-cosignature** verification (Ed25519 cosignature/v1, witness quorum, split-view resistance), and
|
|
436
500
|
`verify --verbose` with the recomputed Merkle root (closes #2).
|
|
437
|
-
- **
|
|
438
|
-
|
|
439
|
-
|
|
501
|
+
- **v1.3** — the portable proof: **C2SP tlog-proof** emit/verify (+
|
|
502
|
+
`verify-proof` CLI), **ML-DSA-44** witness cosignatures (post-quantum, `[pq]` extra), **Token
|
|
503
|
+
Status List** snapshot verification (offline revocation), SD-JWT VC markers (`dc+sd-jwt`,
|
|
504
|
+
`vct`, `status`), COMPLIANCE.md (EU AI Act Art. 12 / NIST AI RMF mapping), and the mutation
|
|
505
|
+
suite as a CI gate.
|
|
506
|
+
- **v1.4 (current release)** — distribution: a **promptfoo** adapter (results.json v3 →
|
|
507
|
+
pass_rate receipt), the **Hugging Face Community Evals bridge** (`pb1.` receipt tokens for the
|
|
508
|
+
`.eval_results` `verifyToken` field + a strict YAML entry emitter — proofbundle-verifiable,
|
|
509
|
+
explicitly NOT the HF-internal badge token), `hf-token` CLI, and the **Python 3.10+ floor**
|
|
510
|
+
(3.9 is EOL since 2025-10).
|
|
511
|
+
- **v2.0 (next major, research differentiation)** — **per-sample Merkle receipts**: the payload carries a
|
|
512
|
+
Merkle root over *individual* sample results, so a verifier can force a random-sample re-check instead of
|
|
513
|
+
trusting only the reported aggregate. This is the THREAT_MODEL's one named structural gap ("forced random
|
|
514
|
+
sub-sampling"), and closing it is the road from *authorship* to *sampled correctness*.
|
|
515
|
+
- **Deferred** (explicitly not yet built, stated honestly) — full SD-JWT VC conformance + `vct`
|
|
516
|
+
type-metadata resolution (pre-IESG), an official in-toto eval predicate (proposal path via
|
|
517
|
+
OpenSSF/CoSAI), and a full in-toto client.
|
|
440
518
|
|
|
441
519
|
## Contributing
|
|
442
520
|
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proofbundle"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
8
8
|
description = "Emit and verify portable cryptographic evidence bundles, offline: Ed25519 + RFC 6962 Merkle + optional SD-JWT."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
license = { text = "MIT" }
|
|
12
12
|
authors = [{ name = "Konrad Gruszka" }]
|
|
13
13
|
keywords = [
|
|
@@ -26,10 +26,11 @@ classifiers = [
|
|
|
26
26
|
"Intended Audience :: Developers",
|
|
27
27
|
"License :: OSI Approved :: MIT License",
|
|
28
28
|
"Programming Language :: Python :: 3",
|
|
29
|
-
"Programming Language :: Python :: 3.9",
|
|
30
29
|
"Programming Language :: Python :: 3.10",
|
|
31
30
|
"Programming Language :: Python :: 3.11",
|
|
32
31
|
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
33
34
|
"Topic :: Security :: Cryptography",
|
|
34
35
|
]
|
|
35
36
|
dependencies = ["cryptography>=42"]
|
|
@@ -45,16 +46,20 @@ sdjwt = []
|
|
|
45
46
|
eval = ["rfc8785>=0.1.4"]
|
|
46
47
|
# The lm-eval adapter reads exported results.json (no import) → pure stdlib.
|
|
47
48
|
adapters = []
|
|
49
|
+
# ML-DSA-44 (FIPS 204) cosignature verification (C2SP tlog-cosignature type 0x06). Needs a
|
|
50
|
+
# cryptography build with OpenSSL 3.5+ (default PyPI wheels since 48.0.0). Verify-only by default;
|
|
51
|
+
# the emit helper needs the same. Ed25519 paths never require this.
|
|
52
|
+
pq = ["cryptography>=48"]
|
|
48
53
|
# The pytest plugin (opt-in test-run receipt via the pytest11 entry-point) — pytest is an optional extra,
|
|
49
54
|
# never a core dependency. Floor 7.0: the hooks/stats API is signature-stable across pytest 7/8/9.
|
|
50
55
|
pytest = ["pytest>=7"]
|
|
51
56
|
# The inspect_ai adapter uses the STABLE read_eval_log API (lazy import). Pinned with an UPPER bound:
|
|
52
57
|
# the .eval format + pydantic schema change between versions (inspect_ai issue 834), and the fixture
|
|
53
|
-
# test is bound to this range.
|
|
54
|
-
#
|
|
55
|
-
inspect = ['inspect_ai>=0.3.112,<0.4
|
|
58
|
+
# test is bound to this range. Since v1.4.0 the project floor is Python 3.10, so no per-extra version
|
|
59
|
+
# marker is needed here (inspect_ai's own >=3.10 requirement is always satisfiable on a supported base).
|
|
60
|
+
inspect = ['inspect_ai>=0.3.112,<0.4']
|
|
56
61
|
dev = ["pytest>=7", "ruff>=0.5", "jsonschema>=4", "mypy>=1.8", "build>=1", "hypothesis>=6",
|
|
57
|
-
"rfc8785>=0.1.4", "sd-jwt>=0.10", 'inspect_ai>=0.3.112,<0.4
|
|
62
|
+
"rfc8785>=0.1.4", "sd-jwt>=0.10", 'inspect_ai>=0.3.112,<0.4']
|
|
58
63
|
|
|
59
64
|
[project.urls]
|
|
60
65
|
Homepage = "https://b7n0de.com"
|
|
@@ -13,7 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
-
__version__ = "1.
|
|
16
|
+
__version__ = "1.4.0"
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"__version__",
|
|
@@ -26,6 +26,12 @@ __all__ = [
|
|
|
26
26
|
"verify_inclusion",
|
|
27
27
|
"verify_consistency",
|
|
28
28
|
"verify_key_binding",
|
|
29
|
+
"verify_tlog_proof",
|
|
30
|
+
"verify_cosignature",
|
|
31
|
+
"verify_witnessed_checkpoint",
|
|
32
|
+
"verify_status_snapshot",
|
|
33
|
+
"receipt_token",
|
|
34
|
+
"verify_receipt_token",
|
|
29
35
|
"VerificationResult",
|
|
30
36
|
"Check",
|
|
31
37
|
"ProofBundleError",
|
|
@@ -39,13 +45,23 @@ _LAZY = {
|
|
|
39
45
|
"Check": ".errors", "ProofBundleError": ".errors", "VerificationResult": ".errors",
|
|
40
46
|
"verify_consistency": ".merkle", "verify_inclusion": ".merkle",
|
|
41
47
|
"verify_key_binding": ".kbjwt",
|
|
48
|
+
"verify_tlog_proof": ".tlogproof",
|
|
49
|
+
"verify_cosignature": ".checkpoint",
|
|
50
|
+
"verify_witnessed_checkpoint": ".checkpoint",
|
|
51
|
+
"verify_status_snapshot": ".statuslist",
|
|
52
|
+
"receipt_token": ".hf_evals",
|
|
53
|
+
"verify_receipt_token": ".hf_evals",
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
if TYPE_CHECKING: # static analysers + IDEs see the real names/types; runtime stays lazy
|
|
45
57
|
from .bundle import SCHEMA, load_bundle, recompute_merkle_root_b64, verify_bundle
|
|
46
58
|
from .emit import emit_bundle, generate_signer
|
|
47
59
|
from .errors import Check, ProofBundleError, VerificationResult
|
|
60
|
+
from .checkpoint import verify_cosignature, verify_witnessed_checkpoint
|
|
48
61
|
from .kbjwt import verify_key_binding
|
|
62
|
+
from .hf_evals import receipt_token, verify_receipt_token
|
|
63
|
+
from .statuslist import verify_status_snapshot
|
|
64
|
+
from .tlogproof import verify_tlog_proof
|
|
49
65
|
from .merkle import verify_consistency, verify_inclusion
|
|
50
66
|
|
|
51
67
|
|
|
@@ -7,5 +7,7 @@ each fixture in tests/fixtures documents its source + version.
|
|
|
7
7
|
from .inspect_ai import from_inspect_ai_log
|
|
8
8
|
from .eee import from_eee_dataset
|
|
9
9
|
from .lm_eval import from_lm_eval_results
|
|
10
|
+
from .promptfoo import from_promptfoo_results
|
|
10
11
|
|
|
11
|
-
__all__ = ["from_lm_eval_results", "from_inspect_ai_log", "from_eee_dataset"
|
|
12
|
+
__all__ = ["from_lm_eval_results", "from_inspect_ai_log", "from_eee_dataset",
|
|
13
|
+
"from_promptfoo_results"]
|
|
@@ -50,7 +50,9 @@ def from_lm_eval_results(path, task: str, metric: str, *, comparator: str, thres
|
|
|
50
50
|
if value is None:
|
|
51
51
|
raise ValueError(f"metric {metric!r} not found in results[{task!r}] "
|
|
52
52
|
f"(available: {sorted(k for k in res if ',' in k)})")
|
|
53
|
-
|
|
53
|
+
# Fixed-point format (never scientific notation, e.g. '1e-05') — build_eval_claim rejects scientific
|
|
54
|
+
# notation, so repr() silently dropped legitimate small scores (release-review fix, mirrors v0.8.1).
|
|
55
|
+
score = value if isinstance(value, str) else (format(float(value), ".12f").rstrip("0").rstrip(".") or "0")
|
|
54
56
|
|
|
55
57
|
n_samples = data.get("n-samples", {}).get(task, {})
|
|
56
58
|
n = int(n_samples.get("effective") or n_samples.get("original") or res.get("sample_len") or 0)
|