Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
afa6712
docs(profile): das Umschlagprofil als Profil ZU RFC 9943 und dem SCIT…
Aug 30, 2026
e08e5de
feat(schema): Abdeckung und typisierte Digest-Referenz additiv — die …
Aug 30, 2026
63f1686
feat(conformance): elf Vektoren fuer das Umschlagprofil — und die dri…
Aug 30, 2026
9138cec
docs(record): die drei gepinnten Messungen in INTEROP und PRIORITY_RE…
Aug 30, 2026
861f212
fix(conformance): R1 versprach drei Gegenproben und lieferte eine — z…
Aug 30, 2026
e6061a1
fix(evalclaim): drei Aufrufstellen pruefen dieselbe Funktion in zwei …
Aug 30, 2026
67c0a9f
docs(profile): zwei Ungenauigkeiten im eigenen Text, beim Volllesen g…
Aug 30, 2026
853c9c2
fix(conformance): der Handler war nur in EINE Richtung fail-closed, u…
Aug 30, 2026
94160b2
test(classify): die never-raise-Zusage unter pathologischer Verschach…
Aug 30, 2026
f486069
fix(docs): zwei Zahlen in meinen eigenen Dokumenten stimmten nicht — …
Aug 30, 2026
c734fab
docs(mapping): die Abschnittsnummern des Entwurfs waren aus zweiter H…
Aug 30, 2026
26910c2
docs: RFC 9943 an der Quelle geprueft — die Angabe traegt, und die As…
Aug 30, 2026
05dcb88
revert(coverage): die drei Abdeckungsfelder wieder entfernt — ein zeh…
Aug 30, 2026
42d5102
fix(mutation): beide Luecken lagen im PRUEFER, nicht im Code — plus d…
Aug 30, 2026
b49c41e
fix(mutation-fresh): die Vorpruefung stellte nur die halbe Frage — me…
Aug 30, 2026
bdb3c1c
docs(gate): warum C12.1 auf einem Arbeitszweig rot ist, und warum das…
Aug 30, 2026
92cb031
docs(scitt): G1 und G2 nach Gegenlesung — Geltungsbereich vor Behaupt…
Aug 30, 2026
c45a035
docs(scitt): G2 aus dem Originaltext berichtigt — 7.1 erreicht unsere…
Aug 30, 2026
46bd00c
docs(scitt): alle sechs Entwurfs-Aussagen am Original geprueft, eine …
Aug 30, 2026
3e9c554
fix(conftest): die Hermetik-Ableitung erkennt eine Wurzel an ihrer HE…
Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,28 @@ contradict each other in either direction.
The corpus carries one vector per status value and one per rejection class under
`conformance/provenance/`. A rule with no rejection vector is not conformance-testable: an
implementation could satisfy every positive case and still accept anything.

## Receipt-envelope profile (5.1)

`conformance/envelope_profile/` holds ten vectors for the profile in
`docs/RECEIPT_ENVELOPE_PROFILE.md`: at least one counter-proof and one positive control for each of
**R1 to R4**. R1 carries three, because two of its three divergence axes cannot arise in a format
that refuses floats — for those the refusal itself is the counter-proof.

**R5 carries no vector, and that is recorded rather than quietly left out.** Its field form was
withdrawn on 2026-08-30 after `draft-hillier-coverage-attestation-00` (CAP-1, 20 Aug 2026) was
measured to rule out the shape this profile had proposed. A counter-proof against a withdrawn shape
would test nothing; the R5 probe follows once CAP-1 has been read.
They run through the library's own emit and verify path — a vector family checked by a purpose-built
mock would prove something about the mock.

The positive controls are not decoration. Without them a verifier that rejected every input would
score perfectly on the counter-proofs, and the corpus would call that conformance.

R6 (every rule ships its counter-proof) deliberately has NO vector: a case asserting that the cases
exist is the tautology the rule warns about. R6 is satisfied by the family existing and by its
detection rate being measured — the planted defects that remain applicable after the R5 withdrawal
are counted in the profile, together with the two that escaped on the first attempt and the two
attempts that were ineffective rather than escaped (they changed a message, not a property). A third gap came from reading the profile against the
corpus rather than from the meta-test: a meta-test measures whether a shipped check can fail, it
cannot notice a check that was never shipped.
69 changes: 69 additions & 0 deletions INTEROP.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,75 @@ for different threats (computation-correctness vs. artifact authenticity/integri

[ValiChord](https://github.com/ValiChord/ValiChord) is a real neighbour: its `valichord_attestation` (Apache-2.0) also attests eval runs and, like proofbundle, canonicalizes with RFC 8785 JCS. Named fairly, the v1 library differs in exactly the standards proofbundle leads with: its format v1 carries **no digital signature** (`signatures` is reserved for v2), uses a **simple SHA-256 Merkle tree** (no RFC 6962 domain separation), and has **no SD-JWT, no in-toto, and no Every Eval Ever converter**; blind peer consensus and an attested log live in its Holochain layer (v2 scope). proofbundle is complementary — the portable, standards-native, transparency-log-anchored receipt layer — not a rival network.

## CSOAI inspect-receipts — measured, three pinned commits

[`CSOAI-ORG/inspect-receipts`](https://github.com/CSOAI-ORG/inspect-receipts) (package
`inspect-signed-receipt`, schema `csoai.inspect-receipt/0.2`) signs Inspect AI eval runs. It is the
nearest neighbour to this project's own shape, and the only one here measured **independently**
rather than read from its documentation.

**This section is a measurement record.** Every line is pinned to a commit hash and a date. It
contains no assessment of the project, its authors or their intentions, and nothing about work that
may exist outside the commits named. A measurement against a pinned commit says what that tree does;
it says nothing about a branch nobody published.

### What was measured, and when

| Commit | Date | Measured |
|---|---|---|
| `ca3fd060` | 2026-08-19 | 2 commits, no CI, no LICENSE file, no did:web resolution path |
| `8e33f160` | 2026-08-20 | 7 commits, publish-only workflow, 6 tests (green when run by hand), did:web resolution present and working |
| `397ae3ad` | 2026-08-29 | canonicalisation re-measured with the real `jcs 0.2.1` encoder installed |

### Envelope interoperability — both directions, both fail before signature verification

Measured 2026-08-20 against `8e33f160`, with `proofbundle==4.0.0` from PyPI, both verifiers in the
same throwaway environment.

- **Their receipt through our verifier:** `ERROR: unsupported schema 'csoai.inspect-receipt/0.2',
expected 'proofbundle/v0.1'` — `proofbundle/bundle.py:298`, `UnsupportedError`.
- **Our `conformance/bundle/valid-minimal` through their verifier:**
`INVALID — content_id mismatch` — their `receipt.py:295-301`, which runs *before* their signature
check. Our bundle carries no `content_id` field at all; its top-level fields are `merkle`,
`payload_b64`, `schema`, `sd_jwt_vc`, `signature`.

Neither direction reaches the cryptography. **This is the subject of
[issue #147](https://github.com/b7n0de/proofbundle/issues/147)** and the reason a common envelope is
worth defining: two implementations that both sign correctly still cannot read each other.

A control was run first in both directions — each verifier against its own fixture — because a
failure at the wrapper looks exactly like a failure at the substance. In the first pass ours was
invoked as `python -m proofbundle`, which the package does not provide; both legs then failed on the
invocation rather than on the question, and the control leg was the one that mattered. Repeated with
the real entry point.

### Canonicalisation — the divergence is string escaping alone

Measured 2026-08-29 against `397ae3ad`, with `jcs 0.2.1` and `rfc8785 0.1.4` both really installed.

| | `jcs.canonicalize` | `rfc8785` | their `canonical.canonical_bytes` |
|---|---|---|---|
| UTF-16 key order | raw | same | same order, **escaped** |
| number `1e-7` | `1e-7` | same | same |
| integer `2` | `2` | same | same |

**Key ordering agrees everywhere** — that was vector 1's actual question, and it is answered. The
bytes differ because the encoder is parametrised as
`JSONEncoder(sort_keys=True, ensure_ascii=True, separators=(",",":"))`, and `ensure_ascii=True`
writes every non-ASCII character as `\uXXXX`, while RFC 8785 emits raw UTF-8. Since
`content_id = sha256(canonical_bytes(body))`, a body containing one non-ASCII character yields a
different `content_id` than a conformant serializer computes. **ASCII-only receipts are byte-identical**,
which is why a corpus of ASCII vectors does not surface it — an earlier pass of ours reported "no
finding" on exactly that basis and was
[corrected in the thread on 28 August](https://github.com/b7n0de/proofbundle/issues/147).

### Not measured

Whether the escaping is intended as RFC 8785 conformant (their docstring names the `\uXXXX`
escaping as part of the implementation, but says nothing about the intent). RFC 8785 conformance
beyond the three vectors. Any state of the code outside the three pinned commits. How the two
projects should reconcile — that is a conversation in the issue, not a measurement.

## Comparison tables (fair, at-a-glance)

### vs Sigstore Rekor / Rekor v2
Expand Down
Loading
Loading