@sanning/proof 0.10.0 → 0.11.1

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.
package/README.md CHANGED
@@ -61,7 +61,7 @@ npx @sanning/proof verify trace-bundle.json
61
61
  npx @sanning/proof verify trace-bundle.json https://arweave.net,https://permagate.io
62
62
  ```
63
63
 
64
- It prints a per-event + rollup verdict and exits on a pinned code: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` malformed bundle · `3` gateway-unavailable when an on-chain re-fetch was requested. Only `arweave` witnesses are re-fetched: a checkpoint or stamp whose `anchor_ref.kind` is anything else is not checked, and with gateways passed the run answers `partial`, exit `3`. The producer's asserted `verdict` is shown but **never trusted**, the displayed verdict is always recomputed from the body. The same CLI also verifies the agent's `sanning.agent.proof/v1` inclusion bundle (it sniffs `spec_version`), so one command covers anchor + agent.
64
+ It prints a per-event verdict, then the PACK verdict, and exits on a pinned code: `0` verified · `1` a real failure (bad signature / tamper / broken inclusion) · `2` malformed bundle · `3` undetermined, which is either a gateway that could not be reached on an on-chain re-fetch or a pack the grading rule calls `incomplete`. The headline is the pack verdict, the same one `gradePack` gives the browser verify page: a record nobody hashed leaves the pack incomplete even when the signed bundle itself is `verified`, and both verdicts are printed when they differ. Only `arweave` witnesses are re-fetched: a checkpoint or stamp whose `anchor_ref.kind` is anything else is not checked, and with gateways passed the run answers `partial`, exit `3`. The producer's asserted `verdict` is shown but **never trusted**, the displayed verdict is always recomputed from the body. The same CLI also verifies the agent's `sanning.agent.proof/v1` inclusion bundle (it sniffs `spec_version`), so one command covers anchor + agent.
65
65
 
66
66
  > The `verify` CLI is live in `@sanning/proof` ≥ 0.2.2, `npx @sanning/proof verify <bundle> [gateways]`. `verifyEvidenceBundle` is the programmatic API.
67
67
 
@@ -115,13 +115,13 @@ The directory form's edge cases, **none of which is a verification failure**, an
115
115
  | Situation | What happens |
116
116
  |---|---|
117
117
  | A record with no file | That event's content is **undetermined** (`~`) and reported as undisclosed. Exit code unchanged. |
118
- | An empty directory | Valid. Discloses nothing, says so, exit code unchanged. |
118
+ | An empty directory | Valid. Discloses nothing and says so. The exit code is whatever the pack reads with no `--logs` at all, which for a pack holding any undisclosed record is **3**, not 0. |
119
119
  | A file whose name is no event id | Read, binds to nothing, and is **named** in a note: a typo'd filename is otherwise indistinguishable from minimal disclosure. |
120
120
  | A subdirectory | Skipped, never recursed: the layout is flat. Noted. |
121
121
  | Anything not named `<event_id>.json`, or not a regular file | Skipped, noted. |
122
122
  | A file we selected but cannot read | **Exit 2.** We were pointed at it, so a permissions or broken-link problem must not read as minimal disclosure. |
123
123
 
124
- Either way each event gains a `contentOk`: **`true`** match · **`false`** mismatch (the event *and* the rollup fail, exit `1`) · **`null`** undetermined, nothing disclosed, or no committed `content_hash` (a withheld record); never a failure, mirroring the payload-binding rule. The CLI prints a `logs ✓/✗/~` mark per event and a `logs: N/M disclosed verified` line.
124
+ Either way each event gains a `contentOk`: **`true`** match · **`false`** mismatch (the event *and* the rollup fail, exit `1`) · **`null`** undetermined, nothing disclosed, or no committed `content_hash` (a withheld record); never a failure, mirroring the payload-binding rule. The CLI prints a `logs ✓/✗/~` mark per event and a `logs: N/M record(s) content-verified` line, where M is the records in the PACK and not the records that happened to disclose bytes: a pack disclosing two of three reads `2/3`, rather than the full score `2/2` a partial hand-over used to print.
125
125
 
126
126
  Programmatically the same side input is `content`, keyed by `event_id`: a `Uint8Array` is used as-is, a `string` is hex. **Prefer a `Map`:**
127
127
 
@@ -203,7 +203,7 @@ The primary signature covers `JCS(envelope minus signature minus co_signatures)`
203
203
 
204
204
  ## Why you can trust it (without trusting us)
205
205
 
206
- Three **independent implementations** of this kernel exist: this package, the Python [`sanning-proof`](https://pypi.org/project/sanning-proof/) (PyPI), and and the MIT-licensed Go reference. The TypeScript and Python kernels are conformance-gated **byte-for-byte** in CI against one shared corpus, pinned per file by SHA-256 at cut tag `test-vectors-v4.0`, and a cross-kernel agreement gate re-runs the corpus plus adversarial negatives through both, asserting identical verdicts on identical bytes. The Go reference runs as a third leg of that same gate, built at the commit its vendoring pins.
206
+ Three **independent implementations** of this kernel exist: this package, the Python [`sanning-proof`](https://pypi.org/project/sanning-proof/) (PyPI), and and the MIT-licensed Go reference. The TypeScript and Python kernels are conformance-gated **byte-for-byte** in CI against one shared corpus, pinned per file by SHA-256 at cut tag `test-vectors-v4.4`, and a cross-kernel agreement gate re-runs the corpus plus adversarial negatives through both, asserting identical verdicts on identical bytes. The Go reference runs as a third leg of that same gate, built at the commit its vendoring pins.
207
207
 
208
208
  That mutual gate is the point: anyone can verify these provenance records with **no Sanning code in the trust path**, write your own verifier against the specs that ship inside the [`sanning-proof`](https://pypi.org/project/sanning-proof/) sdist, and the corpus will tell you if it is conformant.
209
209
 
@@ -15,7 +15,7 @@
15
15
  // 5. (optional) Re-fetch checkpoint_tx_id from a gateway and byte-compare.
16
16
  import { hexToBytes, utf8 } from "./crypto.js";
17
17
  import { leafHash, verifyInclusion } from "./merkle.js";
18
- import { MAX_CANONICAL_DEPTH, exceedsDepth, jcs, verifyEnvelope } from "./verifier.js";
18
+ import { MAX_CANONICAL_DEPTH, exceedsDepth, jcs, jcsParse, verifyEnvelope } from "./verifier.js";
19
19
  export const AGENT_PROOF_SPEC_PREFIX = "sanning.agent.proof/v1";
20
20
  function stringifyErr(e) {
21
21
  return e instanceof Error ? e.message : String(e);
@@ -170,7 +170,13 @@ async function refetch(envelope, txId, gateways, fetchImpl) {
170
170
  if (toHex(onChain) === expectedHex)
171
171
  return { ok: true, unavailable: false };
172
172
  try {
173
- const parsed = JSON.parse(new TextDecoder().decode(onChain));
173
+ // `jcsParse`, matching `_probe_gateway` in
174
+ // `src/sanning_proof/evidence.py`. These bytes arrive from a gateway or
175
+ // a CDN in front of one, so a lenient decoder is reachable without any
176
+ // party building a pack: a CDN serving the envelope with a byte-order
177
+ // mark split this probe from the one in `evidence.ts`, and two honest
178
+ // readers of ONE transaction disagreed (proof#140 gate 5).
179
+ const parsed = jcsParse(onChain);
174
180
  if (toHex(utf8(jcs(parsed))) === expectedHex)
175
181
  return { ok: true, unavailable: false };
176
182
  }