@sanning/proof 0.11.1 → 0.13.0

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.
Files changed (51) hide show
  1. package/README.md +43 -8
  2. package/dist/agent-proof.d.ts +2 -2
  3. package/dist/agent-proof.js +4 -4
  4. package/dist/case-file.d.ts +126 -0
  5. package/dist/case-file.js +349 -0
  6. package/dist/case.d.ts +199 -43
  7. package/dist/case.js +522 -146
  8. package/dist/cli.d.ts +132 -1
  9. package/dist/cli.js +1140 -96
  10. package/dist/crypto.d.ts +1 -1
  11. package/dist/crypto.js +5 -1
  12. package/dist/declaration.d.ts +58 -52
  13. package/dist/declaration.js +256 -33
  14. package/dist/evidence.d.ts +2 -0
  15. package/dist/evidence.js +134 -19
  16. package/dist/exit-codes.d.ts +17 -0
  17. package/dist/exit-codes.js +50 -0
  18. package/dist/grading.d.ts +28 -13
  19. package/dist/grading.js +90 -31
  20. package/dist/handoff.d.ts +16 -1
  21. package/dist/handoff.js +91 -16
  22. package/dist/index.d.ts +10 -7
  23. package/dist/index.js +15 -7
  24. package/dist/message-parts.d.ts +11 -9
  25. package/dist/message-parts.js +11 -9
  26. package/dist/types.d.ts +1 -0
  27. package/dist/verifier.d.ts +9 -2
  28. package/dist/verifier.js +28 -12
  29. package/dist/zip-reader.d.ts +50 -0
  30. package/dist/zip-reader.js +220 -0
  31. package/package.json +2 -2
  32. package/src/agent-proof.ts +4 -4
  33. package/src/case-file.ts +419 -0
  34. package/src/case.ts +651 -156
  35. package/src/cli.ts +1325 -100
  36. package/src/crypto.ts +5 -1
  37. package/src/declaration.ts +248 -52
  38. package/src/evidence.ts +142 -21
  39. package/src/exit-codes.ts +54 -0
  40. package/src/grading.ts +109 -52
  41. package/src/handoff.ts +108 -17
  42. package/src/index.ts +60 -3
  43. package/src/message-parts.ts +11 -9
  44. package/src/types.ts +5 -0
  45. package/src/verifier.ts +35 -13
  46. package/src/zip-reader.ts +229 -0
  47. package/vectors/README.md +56 -3
  48. package/vectors/case-file-index.json +668 -0
  49. package/vectors/case-split.json +1102 -0
  50. package/vectors/pack-grading.json +68 -4
  51. package/vectors/stamp-on-chain.json +3 -3
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "rule": "pack-grading",
3
- "ruleVersion": 5,
4
- "statement": "What a verification surface may CLAIM about a pack it was handed. verified is reachable ONLY when every record's own bytes bound to its own commitment and nothing is missing, unreadable, surplus or omitted. Absent or unreadable evidence is incomplete (UNKNOWN), never failed and never verified. A hard kernel verdict wins: malformed > failed > incomplete > the kernel's own headline. A pack that DEVIATES from its own signed declaration (evidence-bundle.md \u00a75.4) is failed, and every deviation is a cause: a declared record that is absent, a present record nobody declared, or a record the pack declared withheld whose bytes its own SIGNED BODY supplies. A reading that rests instead on the UNSIGNED content handed in beside a pack is not a deviation and never reaches failed (proof#147): each content-dependent finding is its own cause and carries the pack to incomplete, because whoever assembles a case fills that map in, so an accuser holding an intact pack could otherwise reach failed with no key at all. A deviation and a content-dependent finding together are still failed, because the deviation reads off the signed body and a failing check wins. A declaration check that came back partial therefore splits in THREE, and only two of the three turn on the declaration's readability: no declaration member in the body at all moves nothing, because every pack built before \u00a75.4 is that case; a declaration the reader CANNOT READ makes the pack incomplete, because declaration_version is chosen by the party that assembles the pack and has no accept-set, so an unreadable claim must never reach a clean verdict; and a declaration the reader read perfectly well whose disclosure only unsigned bytes could settle makes the pack incomplete as well, named as what the reader could not establish rather than as what the holder did. The grade carries the declaration check verdict in its own output field, so a consumer that keeps only the grade can still tell a declared pack from an undeclared one.",
3
+ "ruleVersion": 6,
4
+ "statement": "What a verification surface may CLAIM about a pack it was handed. verified is reachable ONLY when every record's own bytes bound to its own commitment and nothing is missing, unreadable, surplus or omitted. Absent or unreadable evidence is incomplete (UNKNOWN), never failed and never verified. A hard kernel verdict wins: malformed > failed > incomplete > the kernel's own headline. A pack that DEVIATES from its own signed declaration (evidence-bundle.md \u00a75.4) is failed, and every deviation is a cause: a declared record that is absent, a present record nobody declared, or a record the pack declared withheld whose bytes its own SIGNED BODY supplies. A reading that rests instead on the UNSIGNED content handed in beside a pack is not a deviation and never reaches failed (proof#147): each content-dependent finding is its own cause and carries the pack to incomplete, because whoever assembles a case fills that map in, so an accuser holding an intact pack could otherwise reach failed with no key at all. A deviation and a content-dependent finding together are still failed, because the deviation reads off the signed body and a failing check wins. A declaration check that came back partial therefore splits in THREE, and only two of the three turn on the declaration's readability: no declaration member in the body at all moves nothing, because every pack built before \u00a75.4 is that case; a declaration the reader CANNOT READ makes the pack incomplete, because declaration_version is chosen by the party that assembles the pack and has no accept-set, so an unreadable claim must never reach a clean verdict; and a declaration the reader read perfectly well whose disclosure only unsigned bytes could settle makes the pack incomplete as well, named as what the reader could not establish rather than as what the holder did. The grade carries the declaration check verdict in its own output field, so a consumer that keeps only the grade can still tell a declared pack from an undeclared one. Rule version 6 (evidence-bundle.md \u00a75.4.5 v1.20, A22): a record is WITHHELD BY ITS DECLARATION when a readable declaration lists it in declared_withheld, its ok is true, its contentOk is null and its payloadBindingOk is true; it counts in recordsWithheld and never in recordsVerified, it is taken out of expected and missing before the disclosure causes are computed, and it holds the pack at nothing, so a pack whose every record its declaration withholds grades verified with recordsVerified zero. A record that fails any of the four, a pack with no readable declaration, and an input that does not carry eventId and payloadBindingOk keep rule version 5's reading.",
5
5
  "home": "@sanning/proof — ts/vectors/pack-grading.json. This file is the RULE, not a test fixture of one implementation. The kernel implements it in ts/src/grading.ts and is held to it by ts/test/grading.test.ts.",
6
6
  "consumers": "A surface that grades packs SHOULD call the kernel's gradePack rather than derive the rule again. A surface that cannot yet (an older pinned kernel, another language) vendors this file byte-for-byte, asserts its own derivation against it, and runs a drift check that compares its copy against the copy shipped in the @sanning/proof tarball at vectors/pack-grading.json. A vendored copy that is not byte-identical to this one is a defect, not a variant.",
7
- "howToUse": "Adapt a kernel verification result and a disclosure account into the shapes below, call gradePack, and assert the expectations. `kernel` gives the per-event outcome classes the kernel returns; `logs` gives the account of which records disclosed bytes; `omissions` gives the signed-leaf_count completeness notes. Nothing here depends on how the bytes were transported: a directory read and a browser drop produce the same account and must produce the same verdict. `declaration`, where a case carries one, gives the declaration check's verdict, whether the body carried a declaration member at all, and how many deviations it found; pass a check of that shape as gradePack's optional fourth argument. A case with no `declaration` key calls gradePack with three arguments, which is the pre-§5.4 behavior and must not move, and its `expect.declaration` is absent.",
7
+ "howToUse": "Adapt a kernel verification result and a disclosure account into the shapes below, call gradePack, and assert the expectations. `kernel` gives the per-event outcome classes the kernel returns; `logs` gives the account of which records disclosed bytes; `omissions` gives the signed-leaf_count completeness notes. Nothing here depends on how the bytes were transported: a directory read and a browser drop produce the same account and must produce the same verdict. `declaration`, where a case carries one, gives the declaration check's verdict, whether the body carried a declaration member at all, and how many deviations it found; pass a check of that shape as gradePack's optional fourth argument. A case with no `declaration` key calls gradePack with three arguments, which is the pre-§5.4 behavior and must not move, and its `expect.declaration` is absent. Rule version 6: `kernel.withheldBound` and `kernel.withheldUnbound` events carry eventId withheld-0, withheld-1 and so on, in that order, and payloadBindingOk true and null respectively, unless `kernel.v5Adapter` is true, in which case they carry neither field; `logs.withheld` adds that many of those ids to the account's expected AND missing lists, after the counted ones; `declaration.declaredWithheld` is how many of them, from withheld-0, the check lists in declared_withheld (0 where absent); and `expect.recordsWithheld`, where present, is the grade's count of records withheld by their declaration.",
8
8
  "kernelEventClasses": {
9
9
  "okBound": "{ ok: true, contentOk: true } — the record's own bytes hashed to its own commitment",
10
10
  "okUnknown": "{ ok: true, contentOk: null } — structurally fine, but no bytes were disclosed",
11
11
  "mismatched": "{ ok: false, contentOk: false } — the bytes disagree with the commitment",
12
- "brokenNoContent": "{ ok: false, contentOk: null } — the record itself does not verify"
12
+ "brokenNoContent": "{ ok: false, contentOk: null } — the record itself does not verify",
13
+ "withheldBound": "{ eventId: withheld-<i>, ok: true, contentOk: null, payloadBindingOk: true } — no content bound, the record bytes bound to the envelope (rule version 6)",
14
+ "withheldUnbound": "{ eventId: withheld-<i>, ok: true, contentOk: null, payloadBindingOk: null } — no content bound, and the record bytes did not travel either (rule version 6)"
13
15
  },
14
16
  "cases": [
15
17
  {
@@ -259,6 +261,68 @@
259
261
  "omissions": [],
260
262
  "declaration": { "verdict": "partial", "present": true, "deviations": 0 },
261
263
  "expect": { "verdict": "incomplete", "recordsVerified": 26, "recordsFailed": 0, "recordsUndetermined": 0, "causeCount": 0, "declaration": "partial" }
264
+ },
265
+ {
266
+ "id": "withheld-by-declaration",
267
+ "why": "rule version 6, D5. One record the pack's readable declaration lists as content_withheld, whose record bytes bound to its envelope and whose content did not travel. Withholding it is the holder's signed act, so it holds the pack at nothing: verified, counted apart from the two records whose content bound. `logs.withheld` names the records that are both in the account's expected list and in its missing list under the withheld records' own ids",
268
+ "kernel": { "status": "verified", "okBound": 2, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 1, "withheldUnbound": 0 },
269
+ "logs": { "expected": 3, "present": 2, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
270
+ "omissions": [],
271
+ "declaration": { "verdict": "verified", "present": true, "deviations": 0, "declaredWithheld": 1 },
272
+ "expect": { "verdict": "verified", "recordsVerified": 2, "recordsFailed": 0, "recordsUndetermined": 0, "recordsWithheld": 1, "causeCount": 0, "declaration": "verified" }
273
+ },
274
+ {
275
+ "id": "every-record-withheld-by-declaration",
276
+ "why": "the cost of D5, stated rather than left to be found: every record withheld by the declaration grades verified with recordsVerified ZERO and no content byte hashed. It is safe to print only because the count travels, and a surface MUST NOT count these as verified records",
277
+ "kernel": { "status": "verified", "okBound": 0, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 26, "withheldUnbound": 0 },
278
+ "logs": { "expected": 26, "present": 0, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 26 },
279
+ "omissions": [],
280
+ "declaration": { "verdict": "verified", "present": true, "deviations": 0, "declaredWithheld": 26 },
281
+ "expect": { "verdict": "verified", "recordsVerified": 0, "recordsFailed": 0, "recordsUndetermined": 0, "recordsWithheld": 26, "causeCount": 0, "declaration": "verified" }
282
+ },
283
+ {
284
+ "id": "withheld-record-bytes-too",
285
+ "why": "the declaration withholds the record's content, and the pack withholds its record bytes as well, so its payload binding is undetermined: what is withheld is not only its content, and the fourth condition fails. Rule version 5's reading: incomplete, no_disclosure",
286
+ "kernel": { "status": "verified", "okBound": 0, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 0, "withheldUnbound": 1 },
287
+ "logs": { "expected": 1, "present": 0, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
288
+ "omissions": [],
289
+ "declaration": { "verdict": "verified", "present": true, "deviations": 0, "declaredWithheld": 1 },
290
+ "expect": { "verdict": "incomplete", "recordsVerified": 0, "recordsFailed": 0, "recordsUndetermined": 1, "recordsWithheld": 0, "causeCount": 1, "declaration": "verified" }
291
+ },
292
+ {
293
+ "id": "withheld-no-declaration",
294
+ "why": "the records of withheld-by-declaration with NO declaration. Only a declaration makes withholding a signed act; without one, absent content is indistinguishable from content lost in transit, so the record is undetermined and the pack is incomplete (missing_disclosure), exactly as rule version 5 graded it",
295
+ "kernel": { "status": "verified", "okBound": 2, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 1, "withheldUnbound": 0 },
296
+ "logs": { "expected": 3, "present": 2, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
297
+ "omissions": [],
298
+ "expect": { "verdict": "incomplete", "recordsVerified": 2, "recordsFailed": 0, "recordsUndetermined": 1, "recordsWithheld": 0, "causeCount": 1 }
299
+ },
300
+ {
301
+ "id": "withheld-unreadable-declaration",
302
+ "why": "the records of withheld-by-declaration beside a declaration that is present and that this reader cannot read. Its declared_withheld is empty, so nothing is withheld by it, and the pack is incomplete for the absent content and for the unreadable claim both",
303
+ "kernel": { "status": "verified", "okBound": 2, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 1, "withheldUnbound": 0 },
304
+ "logs": { "expected": 3, "present": 2, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
305
+ "omissions": [],
306
+ "declaration": { "verdict": "partial", "present": true, "deviations": 0, "declaredWithheld": 0 },
307
+ "expect": { "verdict": "incomplete", "recordsVerified": 2, "recordsFailed": 0, "recordsUndetermined": 1, "recordsWithheld": 0, "causeCount": 1, "declaration": "partial" }
308
+ },
309
+ {
310
+ "id": "withheld-and-side-bytes",
311
+ "why": "a record withheld by its declaration, and a content-dependent finding on the pack: bytes nobody signed that do not line up with the claim. The record is still withheld by the declaration and counted so, and the finding is its own cause, so the pack is incomplete and never failed",
312
+ "kernel": { "status": "verified", "okBound": 2, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 1, "withheldUnbound": 0 },
313
+ "logs": { "expected": 3, "present": 2, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
314
+ "omissions": [],
315
+ "declaration": { "verdict": "partial", "present": true, "deviations": 0, "contentFindings": 1, "declaredWithheld": 1 },
316
+ "expect": { "verdict": "incomplete", "recordsVerified": 2, "recordsFailed": 0, "recordsUndetermined": 0, "recordsWithheld": 1, "causeCount": 1, "declaration": "partial" }
317
+ },
318
+ {
319
+ "id": "withheld-v5-adapter",
320
+ "why": "withheld-by-declaration graded through an adapter written for rule version 5, whose events carry neither eventId nor payloadBindingOk (`kernel.v5Adapter`). Such an input is never withheld by its declaration, so the adapter loses the exemption and keeps the stricter reading: incomplete, missing_disclosure",
321
+ "kernel": { "status": "verified", "okBound": 2, "okUnknown": 0, "mismatched": 0, "brokenNoContent": 0, "withheldBound": 1, "withheldUnbound": 0, "v5Adapter": true },
322
+ "logs": { "expected": 3, "present": 2, "missing": 0, "unreadable": 0, "extra": 0, "withheld": 1 },
323
+ "omissions": [],
324
+ "declaration": { "verdict": "verified", "present": true, "deviations": 0, "declaredWithheld": 1 },
325
+ "expect": { "verdict": "incomplete", "recordsVerified": 2, "recordsFailed": 0, "recordsUndetermined": 1, "recordsWithheld": 0, "causeCount": 1, "declaration": "verified" }
262
326
  }
263
327
  ]
264
328
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "rule": "stamp-on-chain",
3
- "ruleVersion": 1,
3
+ "ruleVersion": 2,
4
4
  "statement": "What re-fetching a stamp's witness from a gateway DOES and DOES NOT settle. A stamp body carries no producer signature and no inclusion proof of its own, so the witness is the entire independent part of it (evidence-bundle.md §5.2.4): confirming that a gateway holds the exact bytes named by anchor_ref is the only check that turns Sanning's own assertion into evidence. It is an ONLINE act and always optional — an offline verdict is unchanged by it and never claims it happened. Four states, never three: not-checked (no gateway was asked, or the stamp names no witness), confirm (a gateway returned the witnessed bytes), mismatch (a gateway returned DIFFERENT bytes — the only outcome that fails anything), unreachable (we asked and nobody answered — an availability gap, never an accusation). The expected bytes are stamps[].stamp_bytes VERBATIM, because §5.2.2 defines that field as the exact bytes handed to the witness; re-canonicalizing them would compare the chain against bytes nobody signed.",
5
5
  "home": "@sanning/proof — ts/vectors/stamp-on-chain.json. This file is the RULE, not a test fixture of one implementation. The TypeScript kernel implements it in ts/src/evidence.ts (refetchStamps / refetchStamp / probeGateway / rollupOnChain) and is held to it by ts/test/stamp-on-chain-vectors.test.ts; the Python kernel implements it in src/sanning_proof/evidence.py (_refetch_stamps / _refetch_stamp / _probe_gateway / _rollup_on_chain) and is held to the SAME file by tests/test_stamp_on_chain_vectors.py. Neither kernel can pass by agreeing with the other.",
6
6
  "consumers": "A surface that re-fetches a stamp witness SHOULD call the kernel's verifyEvidenceBundle with `gateways` rather than derive the rule again. A surface that cannot yet (an older pinned kernel, another language) vendors this file byte-for-byte, asserts its own derivation against it, and runs a drift check against the copy shipped in the @sanning/proof tarball at vectors/stamp-on-chain.json — failing when it cannot find a copy to compare against. A vendored copy that is not byte-identical to this one is a defect, not a variant. In particular: a surface MUST NOT collapse the four states into a boolean. A reader that cannot distinguish not-checked from unreachable will report that no gateway could be reached when none was tried, which is a false accusation manufactured by a missing state, and is the defect this table was written after.",
@@ -150,13 +150,13 @@
150
150
  },
151
151
  {
152
152
  "id": "unwitnessed-stamp-is-not-checked-never-unreachable",
153
- "why": "A stamp with no anchor_ref names no witness, so there is nothing to ask any gateway for. onChain stays null — not-checked — even though gateways were supplied and every one of them is reachable. Reporting all-unreachable here would invent a network event that did not occur, and would move this pack's exit code from 0 to 3 purely because the caller passed a gateway list. The bundle is already `partial` for the right reason, said in the right words, by §5.2.4.",
153
+ "why": "A stamp with no anchor_ref names no witness, so there is nothing to ask any gateway for. onChain stays null — not-checked — even though gateways were supplied and every one of them is reachable. Reporting all-unreachable here would invent a network event that did not occur. The bundle is already `partial` for the right reason, said in the right words, by §5.2.4, and exits 3 for that reason with or without a gateway list (0.13.0: `partial` exits 3 in every verb).",
154
154
  "gateways": ["https://g1.example"],
155
155
  "stamps": [{ "witnessed": false, "returns": ["witnessed"] }],
156
156
  "expect": {
157
157
  "onChainChecked": true,
158
158
  "status": "partial",
159
- "exit": 0,
159
+ "exit": 3,
160
160
  "stamps": [{ "rollup": null, "onChainOk": null, "ok": true }],
161
161
  "bundleErrorsContain": ["sealed by Sanning, witnessed by nobody"]
162
162
  }