verifyhash 0.1.0 → 0.1.2

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 (64) hide show
  1. package/README.md +5 -3
  2. package/cli/agent-hook.js +431 -0
  3. package/docs/ADOPT.md +15 -5
  4. package/docs/AGENT-HOOK.md +111 -0
  5. package/docs/ANCHORING.md +43 -22
  6. package/docs/PUBLISH-VERIFY-VH.md +45 -0
  7. package/examples/README.md +185 -0
  8. package/examples/policy.lenient.json +5 -0
  9. package/examples/policy.strict.json +6 -0
  10. package/examples/run.js +366 -0
  11. package/examples/sample-dataset/README.txt +10 -0
  12. package/examples/sample-dataset/corpus/cc-by-poem.txt +8 -0
  13. package/examples/sample-dataset/corpus/mit-notes.txt +4 -0
  14. package/examples/sample-dataset/data/unlabeled.txt +5 -0
  15. package/examples/sample-dataset/vendored/gpl-snippet.txt +5 -0
  16. package/examples/sample-dataset.hints.json +7 -0
  17. package/examples/sample-parcel/data/manifest-of-contents.txt +7 -0
  18. package/examples/sample-parcel/data/records.csv +4 -0
  19. package/examples/sample-parcel/delivery-note.txt +9 -0
  20. package/package.json +26 -3
  21. package/verifier/README.md +584 -0
  22. package/verifier/action/README.md +87 -0
  23. package/verifier/action/action.yml +146 -0
  24. package/verifier/build-standalone-html.js +1287 -0
  25. package/verifier/build-standalone.js +989 -0
  26. package/verifier/ci/journal.generic.sh +96 -0
  27. package/verifier/ci/journal.github-actions.yml +99 -0
  28. package/verifier/ci/reproduce-vh.generic.sh +59 -0
  29. package/verifier/ci/reproduce-vh.github-actions.yml +49 -0
  30. package/verifier/ci/verify-service.generic.sh +96 -0
  31. package/verifier/ci/verify-service.github-actions.yml +88 -0
  32. package/verifier/ci/verify-vh.generic.sh +75 -0
  33. package/verifier/ci/verify-vh.github-actions.yml +56 -0
  34. package/verifier/dist/BUILD-PROVENANCE.json +210 -0
  35. package/verifier/dist/seal-vh-standalone.js +876 -0
  36. package/verifier/dist/seal-vh-standalone.js.sha256 +1 -0
  37. package/verifier/dist/verify-vh-standalone.html +3373 -0
  38. package/verifier/dist/verify-vh-standalone.html.sha256 +1 -0
  39. package/verifier/dist/verify-vh-standalone.js +5123 -0
  40. package/verifier/dist/verify-vh-standalone.js.sha256 +1 -0
  41. package/verifier/lib/canonical.js +141 -0
  42. package/verifier/lib/keccak.js +30 -0
  43. package/verifier/lib/keccak256-vendored.js +206 -0
  44. package/verifier/lib/merkle.js +145 -0
  45. package/verifier/lib/revocation-core.js +606 -0
  46. package/verifier/lib/revocation.js +200 -0
  47. package/verifier/lib/seal-cli.js +374 -0
  48. package/verifier/lib/seal-evidence.js +237 -0
  49. package/verifier/lib/secp256k1-recover.js +249 -0
  50. package/verifier/package.json +39 -0
  51. package/verifier/verify-vh.js +3376 -0
  52. package/docs/ADOPTION.json +0 -11
  53. package/docs/AUDIT.md +0 -55
  54. package/docs/DECIDE.md +0 -47
  55. package/docs/DECISIONS-PENDING.md +0 -27
  56. package/docs/DEPLOY-PUBLIC-SITE.md +0 -301
  57. package/docs/ENGINE-LEDGER.json +0 -12
  58. package/docs/LOOP-AUDIT-2026-07-03.json +0 -580
  59. package/docs/LOOP-HARDENING-PLAN.md +0 -44
  60. package/docs/METRICS.jsonl +0 -31
  61. package/docs/MORNING.md +0 -204
  62. package/docs/STRATEGY-ARCHIVE.md +0 -5055
  63. package/docs/SUPERVISOR-RUNBOOK.md +0 -52
  64. package/docs/USAGE-BUDGET.json +0 -121
package/docs/ANCHORING.md CHANGED
@@ -43,11 +43,12 @@ logged), and a chainId outside the known local/testnet set refuses without `--i-
43
43
  Exit contract: `0` anchored / `3` named reject (an invalid artifact, or the registry's own revert
44
44
  such as `AlreadyAnchored`) / `2` usage / `1` IO-network-key.
45
45
 
46
- `verify-anchored` is **OFFLINE by default** — no key, no network (but it runs through the producer
47
- `cli/` stack, which loads `ethers`; "OFFLINE" here means **no key and no network**, NOT "no producer
48
- stack" — the standalone `verifier/` tree does not yet verify anchored receipts, see **Independent
49
- verification** below): it validates the receipt strictly and **recomputes** the artifact's digest
50
- through the same closed table; any deviation is a specific named reject (`digest-mismatch` /
46
+ `verify-anchored` is **OFFLINE by default** — no key, no network. The same offline binding leg also
47
+ runs with **zero producer stack**: the standalone `verifier/` tree recognizes `vh-anchored-receipt@1`
48
+ (`verify-vh <receipt> --anchored-artifact <sealed-file>`, see **Independent verification** below), so
49
+ "verify without installing the producer's stack" now covers the receipt too. Either way the check is
50
+ the same: the receipt is validated strictly and the artifact's digest is **recomputed** through the
51
+ same closed table; any deviation is a specific named reject (`digest-mismatch` /
51
52
  `kind-mismatch` / `how-mismatch` / `bad-receipt` / the artifact's own named reject). With **both** `--rpc` and `--contract` it additionally authenticates the registry
52
53
  (the standing identity probe — no record is believed until the contract self-identifies) and
53
54
  re-checks every chain fact the receipt claims. Exit `0` ACCEPTED / `3` REJECTED / `2` / `1` — the
@@ -86,31 +87,51 @@ Spelled out:
86
87
  `timestamp-request`/`timestamp-wrap`/`verify-timestamp`) is a third party's *existed-by-genTime*
87
88
  attestation. An on-chain anchor (C) complements them; it does not replace them.
88
89
 
89
- ## Independent verification — the one axis this does NOT yet cover
90
+ ## Independent verification — the anchored receipt verifies standalone too (T-70.4)
90
91
 
91
92
  The verifyhash family's headline is that a counterparty can verify a sealed artifact **OFFLINE,
92
93
  without installing the producer's stack**: the standalone `verifier/` bundle
93
94
  ([`../verifier/dist/verify-vh-standalone.js`](../verifier/dist/verify-vh-standalone.js)) is a single,
94
95
  dependency-free file — **no `ethers`, no `npm install`** — that re-derives an evidence packet's or
95
- agent packet's digest by itself. That promise **DOES** cover the **sealed artifact underneath** an
96
+ agent packet's digest by itself. That promise DOES cover the **sealed artifact underneath** an
96
97
  anchored receipt: hand a counterparty the `*.vhevidence.json` (or any closed-table artifact) and they
97
98
  verify it standalone, exactly as before, with no producer code.
98
99
 
99
- It does **NOT yet extend to the anchored receipt itself.** `vh verify-anchored` — including its
100
- OFFLINE binding leg runs **only through the producer `cli/` stack, which loads `ethers` at module
101
- load** (`cli/anchor-artifact.js` `cli/core/attestation.js`); and `vh-anchored-receipt@1` is **NOT a
102
- recognized kind** in the standalone `verifier/verify-vh.js` tree, which scopes itself to sealed
103
- artifacts and explicitly puts on-chain anchoring out of scope. So the family's zero-install
104
- "verify without the producer's stack" promise **does not YET reach the anchored-receipt binding
105
- leg**: a counterparty who wants to check the receipt's binding today must run the producer cli
106
- (`node cli/vh.js verify-anchored`).
107
-
108
- This is a **packaging gap, not a proof gap.** (1) The binding check is pure hashing — the standalone
109
- verifier already re-derives evidence-seal and agent-packet digests with no `ethers` — so
110
- `vh-anchored-receipt@1` can be added to the standalone tree to actually close it (tracked as
111
- **T-70.4**). (2) The `--rpc` chain re-check needs the chain anyway, so the offline binding leg's
112
- standalone value is limited until then. Until T-70.4 lands: verify the **sealed artifact** standalone
113
- (zero-install, independent), and verify the **anchor** via the producer cli.
100
+ Since **T-70.4** it also covers the **anchored receipt's OFFLINE binding leg itself**:
101
+ `vh-anchored-receipt@1` **IS a recognized kind in the standalone `verifier/verify-vh.js` tree**. A
102
+ counterparty handed a receipt plus the sealed artifact it anchors checks the binding with zero
103
+ producer code from this repo, or from the single-file bundle:
104
+
105
+ ```bash
106
+ node verifier/verify-vh.js examples/anchoring/anchored-receipt.local.json \
107
+ --anchored-artifact examples/anchoring/sample-seal.vhevidence.json
108
+ # zero-install, same command: node verify-vh-standalone.js <receipt> --anchored-artifact <sealed-file>
109
+ ```
110
+
111
+ The standalone validates the receipt strictly (an edited in-band trust note is the named
112
+ `bad-receipt`) and **recomputes** the artifact's digest through the SAME closed kind table the
113
+ producer core uses each leg re-validating the artifact through a strict, `ethers`-free port of its
114
+ shipped validator, over the verifier's own dependency-free keccak/sha256. The verdicts match the
115
+ producer cli's on the same inputs: ACCEPTED exit `0`, or the specific named reject
116
+ (`digest-mismatch` / `kind-mismatch` / `how-mismatch` / `bad-receipt` / the artifact's own named
117
+ reject) exit `3` — the packaging gap disclosed here before T-70.4 is **closed**.
118
+
119
+ **Chain-class guidance (so a local-dev receipt is never mistaken for a public proof).** On ACCEPT the
120
+ standalone also classifies the chain the receipt *claims* and surfaces it as a stable, machine-gateable
121
+ part of the `--json` contract — `chainClass` (`local-dev` / `public-testnet` / `unknown`) plus a
122
+ `publiclyMeaningful` boolean (and a leading human-readable `WARNING`/`ADVISORY` line). The offline leg
123
+ cannot (by definition) confirm the digest is actually on-chain, but it *can* tell a counterparty that a
124
+ receipt from a **local dev chain** proves mechanism only and is worth nothing publicly (STRATEGY.md
125
+ **P-2**) — the committed `chainId 31337` fixture is flagged exactly that way. The classification never
126
+ changes the accept/reject decision; it is additive trust context the counterparty's own tooling can
127
+ gate on. The id sets mirror the producer's `cli/anchor.js` known-testnet set (pinned by a drift guard).
128
+
129
+ **The honest remaining boundary.** The standalone checks the OFFLINE **binding leg only**: the
130
+ receipt's `chain` facts remain the **anchorer's claim** until re-checked against the chain, and that
131
+ re-check needs a chain endpoint by definition — it stays with the producer cli
132
+ (`vh verify-anchored --rpc <url> --contract <addr>`, whose `cli/` stack loads `ethers` at module
133
+ load: `cli/anchor-artifact.js` → `cli/core/attestation.js`). What a receipt proves — and does NOT
134
+ prove — is unchanged either way (see the trust note above).
114
135
 
115
136
  ## The free line
116
137
 
@@ -0,0 +1,45 @@
1
+ # Publish `verify-vh` — the HUMAN checklist (one page)
2
+
3
+ **Publishing stays human.** The build loop only builds and tests locally — it never runs
4
+ `npm publish`, never deploys, never holds registry credentials (STRATEGY.md hard guardrails).
5
+ This page is what the human follows when they decide to publish the standalone verifier package.
6
+
7
+ **Why publish at all.** The front-door line in [docs/ADOPT.md](ADOPT.md) —
8
+ `npx --yes verify-vh demo` — **404s until `verify-vh` is published to npm**. Everything else about
9
+ that line is already true and machine-tested from the repo; publish is the single missing (human) step.
10
+
11
+ **The gate that must be green first.** `test/verify-vh.pack.test.js` runs `npm pack` on `verifier/`
12
+ (offline), extracts the tarball OUTSIDE the repo, proves the bare tree *cannot* fall back to the
13
+ repo's modules, provisions ONLY the one declared dependency (`js-sha3`), and then proves `demo` and
14
+ the real `--vendor` verify path work from the extracted tree alone — so a file missing from
15
+ `verifier/package.json` `files` fails that suite, not the first stranger's `npx` run.
16
+
17
+ ## Checklist
18
+
19
+ 1. **Gate** (repo root): `npx hardhat test test/verify-vh.pack.test.js` → must be green.
20
+ 2. **Inspect the shipment** (from `verifier/`): `cd verifier && npm pack --dry-run` — expect
21
+ `verify-vh.js`, `lib/*.js`, `README.md`, `package.json`, and nothing else.
22
+ 3. **Log in as the human npm account**: `npm whoami` (then `npm login` if needed). The loop holds no
23
+ npm credentials and must never be given any.
24
+ 4. **Publish from `verifier/` — NOT the repo root** (the root package is `verifyhash`, a different
25
+ surface): `cd verifier && npm publish`. Re-publishing later requires a version bump in
26
+ `verifier/package.json` first (npm rejects a reused version).
27
+ 5. **Post-publish smoke**, from any directory OUTSIDE this repo (a clean machine is even better):
28
+
29
+ ```bash
30
+ npx --yes verify-vh demo
31
+ ```
32
+
33
+ Expect exit `0`; the transcript must ACCEPT the genuine packet naming signer
34
+ `0x70997970c51812dc3a010c7d01b50e0d17dc79c8` (the fixed TEST-ONLY hardhat #1 key — never a real
35
+ key), then REJECT the one-byte-tampered copy naming `model-card.md`.
36
+ 6. **Hands-on smoke** (optional): `npx --yes verify-vh demo ./vh-demo`, then run the verify / tamper /
37
+ restore commands it prints.
38
+ 7. **If the smoke fails**: `npm deprecate verify-vh@<version> "broken — do not use"`, fix, bump, and
39
+ re-run this checklist from step 1. Never leave a broken version as `latest`.
40
+
41
+ ## The honest boundary (say no more than this, anywhere the package is announced)
42
+
43
+ The demo proves tamper-evidence + signer-pin, NOT a trusted timestamp, NOT a legal opinion.
44
+ A verified packet means: the bytes you hold match what the named signer sealed — nothing about
45
+ *when* it was sealed, and nothing about whether the content is true, licensed, or lawful.
@@ -0,0 +1,185 @@
1
+ # verifyhash — runnable examples
2
+
3
+ Two committed, self-checking, fully-offline examples. Zero setup, one command each.
4
+
5
+ ## `sdk-verify.js` — embed the SDK exactly as an external developer would
6
+
7
+ ```bash
8
+ node examples/sdk-verify.js
9
+ ```
10
+
11
+ This is the **consumer** example: it imports the package **only** through its single public entrypoint,
12
+ `require("verifyhash")` (plus `ethers`, verifyhash's **own** declared dependency, used only to mint an
13
+ **ephemeral throwaway** signing key that stands in for a real, out-of-band vendor key) — **no** deep
14
+ `cli/core/...` reach-in, no network, no third-party non-core dependency, no real key. It runs **two acts**:
15
+ the free-tier tamper-evidence path, and the paid, revenue-relevant **signed + vendor-pinned verify gate**.
16
+
17
+ **Act 1 — UNSIGNED tamper-evidence (free tier):**
18
+
19
+ 1. **`buildSeal`** — seal an in-memory `{ relPath, bytes }` file set (no directory, no disk).
20
+ 2. **`verifySeal`** (untouched bytes) → **ACCEPTED** — the root is re-derived from the bytes you hold.
21
+ 3. **`verifySeal`** (one byte flipped) → **REJECTED** — and it prints the per-file **diff** the verdict is
22
+ built from (which `relPath` changed, expected vs. actual hash).
23
+ 4. **`serializeSeal` / `readSeal`** — the canonical, byte-deterministic packet a counterparty can re-read.
24
+
25
+ **Act 2 — SIGNED + vendor-PINNED verify gate (the paid embed).** This is the integration a downstream
26
+ service pays for: **verify in-process that a packet was signed by _our_ published vendor address**, with
27
+ **no** shell-out to the `vh` binary (STRATEGY.md **P-9** / EPIC-58 — "verified by verifyhash, signed &
28
+ pinned, inside _your_ product").
29
+
30
+ 5. **`signSealWith`** — a publisher signs the seal (ephemeral key here; a real out-of-band key in prod).
31
+ 6. **`verifySignedSeal`** pinned to **our** vendor address → **ACCEPTED**.
32
+ 7. **`verifySignedSeal`** pinned to a **different** vendor → **REJECTED** — the signature is *genuine*; only
33
+ the **pin** fails. "Signed by someone, but not by us" must reject; that is the security property a paying
34
+ integrator's gate enforces (it is **not** tamper-evidence — the bytes are fine).
35
+ 8. **`verifySignedSeal`** on a one-byte-tampered signature → **REJECTED** (recovered signer ≠ claimed).
36
+
37
+ It leads with the standing **trust note** (a seal proves *tamper-evidence*; a valid **signature** proves
38
+ *who vouched* — the pinned address's key-holder — for those bytes; **neither** proves a trusted timestamp
39
+ and **neither** is a legal opinion — timestamping rides the human-owned trust-root, `needs-human`, P-3 in
40
+ [`STRATEGY.md`](../STRATEGY.md)), prints a clear **PASS** summary naming both acts, and exits 0. The only
41
+ key it ever uses is an **ephemeral, in-memory throwaway** (never persisted, funded, or logged). It is
42
+ test-gated by [`test/sdk.example.test.js`](../test/sdk.example.test.js) on every `npx hardhat test` — a grep
43
+ there asserts the example uses **only** the public surface (`require("verifyhash")` + `ethers`, no deep
44
+ `cli/*` import), so the "public API stands alone" claim can never silently rot.
45
+
46
+ ## `verify-service-client.js` — call the `vh serve-verify` HTTP endpoint as a drop-in dependency
47
+
48
+ ```bash
49
+ node examples/verify-service-client.js
50
+ ```
51
+
52
+ The **verify-service client** example: it boots the `vh serve-verify` HTTP endpoint (the *"CI plugin that
53
+ imports rather than shells out"*), then treats it as a drop-in dependency. It **builds** a seal with
54
+ `require("verifyhash")`, **POSTs** a clean seal to the booted service (HTTP **200 ACCEPTED** → prints
55
+ `ACCEPT`), then **POSTs** the same seal with **one byte flipped** (HTTP **422 REJECTED** → prints `REJECT`),
56
+ tears the service down, and exits 0. It imports **only** `require("verifyhash")`, the `vh` **command**
57
+ (spawned from the package's own `bin.vh`), and Node built-ins (`http`, `child_process`, `path`) — **no**
58
+ deep `cli/*` reach-in and **no** third-party dependency. See
59
+ [`docs/VERIFY-SERVICE.md`](../docs/VERIFY-SERVICE.md) for the request/response schema, the status mapping,
60
+ and the trust boundary. It is test-gated by
61
+ [`test/verify-service.example.test.js`](../test/verify-service.example.test.js) on every `npx hardhat test`,
62
+ so it can never silently rot.
63
+
64
+ ## `journal-ci.js` — continuous-integrity CI step (the `vh journal` integrity journal)
65
+
66
+ ```bash
67
+ node examples/journal-ci.js
68
+ ```
69
+
70
+ The **continuous-integrity** example: it treats the `vh journal` **append-only, hash-chained integrity
71
+ journal** as a drop-in CI step. It **builds** a seal with `require("verifyhash")`, writes the sealed bytes +
72
+ packet to a throwaway workdir, then shells out to `vh journal append` **twice** (recording two hash-chained
73
+ verdicts, strictly additively), runs `vh journal verify` (reporting an **unbroken chain**, exit 0), and exits
74
+ 0. It imports **only** `require("verifyhash")`, the `vh` **command** (spawned from the package's own `bin.vh`),
75
+ and Node built-ins (`fs`, `os`, `path`, `child_process`) — **no** deep `cli/*` reach-in (not even the pure
76
+ journal core) and **no** third-party dependency. The `ts` on each entry is **self-asserted**, **not** a
77
+ trusted timestamp — the journal never claims *"unaltered since date T"* on its own (STRATEGY.md **P-3**). See
78
+ [`docs/INTEGRITY-JOURNAL.md`](../docs/INTEGRITY-JOURNAL.md) for the schema, the chain guarantee, the 0/3
79
+ contract, and the honesty boundary. It is test-gated by
80
+ [`test/journal.example.test.js`](../test/journal.example.test.js) on every `npx hardhat test`, so it can
81
+ never silently rot.
82
+
83
+ ## `sdk-verify-signed.js` — the SIGNED + vendor-PINNED verify gate, in-process
84
+
85
+ ```bash
86
+ node examples/sdk-verify-signed.js
87
+ ```
88
+
89
+ This is the **buyer's** signed-verify example: it plays a downstream service that **receives** a signed,
90
+ vendor-address-pinned deliverable (a model, a dataset, a build artifact) and clears the **two gates that
91
+ gate a real purchase** — **(a)** *"was this signed by our published vendor address?"* and **(b)** *"are the
92
+ exact files I received **on disk** the ones that vendor signed?"* — **in-process**, with **no** shell-out to
93
+ the `vh` binary. It is the SIGNED twin of `vh evidence verify-signed` (including its `--signer` pin and
94
+ `--dir` binding), byte-identical because it **is** the same code (`index.js` is a thin identity re-export;
95
+ see [`../docs/SDK.md`](../docs/SDK.md)). This is the paid, revenue-relevant embed (STRATEGY.md **P-9** /
96
+ EPIC-58 — "verified by verifyhash, signed & pinned, inside _your_ product").
97
+
98
+ The verify example imports **only** `require("verifyhash")` and **relative** example files — nothing else,
99
+ **no** `child_process`, **no** built-in (`fs`/`os`/`path`), **no** network, **no** deep `cli/*` reach-in.
100
+ Because a buyer **never signs** and does not do its own disk plumbing, both the publisher-side key handling
101
+ (minting an **ephemeral throwaway** key that stands in for the publisher's real out-of-band key) **and** the
102
+ "receive the deliverable to a throwaway temp dir / corrupt one received file / clean up" plumbing are
103
+ quarantined in [`lib/ephemeral-publisher.js`](./lib/ephemeral-publisher.js) — the only place `ethers`
104
+ (verifyhash's own dependency) and Node built-ins are touched. It runs one **ACCEPT** then **four** REJECT/
105
+ ACCEPT steps that **escalate in value**:
106
+
107
+ 1. **`verifySignedSeal`** pinned to **our** vendor address → **ACCEPTED**.
108
+ 2. **`verifySignedSeal`** pinned to a **different** vendor → **REJECTED** — a **wrong-signer** reject: the
109
+ signature is *genuine* and the bytes are fine; it just recovers to a signer we do **not** pin. "Signed by
110
+ someone, but not by us" must reject; that is the security property a paying integrator's gate enforces.
111
+ 3. **`verifySignedSeal`** on a one-byte-**tampered** signature → **REJECTED** — the recovered signer no
112
+ longer matches the claimed one, so it rejects even under the correct vendor pin.
113
+ 4. **`verifySignedSealAttestation`** — the **strict, on-disk BINDING gate** a paying integrator actually
114
+ buys — pinned to **our** vendor address **and bound to the actual files received on disk**:
115
+ - **[4a]** the **untouched** received deliverable → **ACCEPTED** — both gates pass: our vendor signed it
116
+ **and** the bytes on disk are byte-identical to what was signed (`manifestBindsAttestation=true`).
117
+ - **[4b]** the received deliverable with **one file corrupted on disk** → **REJECTED** — the vendor
118
+ signature over the **original** bytes is **still genuine** and the pin **still** matches; only the
119
+ on-disk bytes drifted, so `manifestBindsAttestation=false`. This is the **real fraud** the buyer cares
120
+ about — a **genuine our-vendor signature attached to a substituted download** — and it is the case the
121
+ signature-only path (1–3) **cannot** catch. The on-disk binding does.
122
+
123
+ It leads with the standing **trust note** (a valid **signature** proves *who vouched* — the pinned address's
124
+ key-holder — for those exact sealed bytes; it does **not** prove a trusted timestamp and is **not** a legal
125
+ opinion — timestamping rides the human-owned trust-root, `needs-human`, P-3 in
126
+ [`../STRATEGY.md`](../STRATEGY.md)), prints a clear **PASS** summary, and exits 0. The received deliverable
127
+ is written to a **throwaway OS temp dir** (never the repo tree) and cleaned up in a `finally`. Verification
128
+ is **offline and key-free**: it recovers a **public** address from the signature, holds no private key, and
129
+ contacts nothing. It is test-gated by [`test/sdk.example.signed.test.js`](../test/sdk.example.signed.test.js)
130
+ on every `npx hardhat test` — a grep there asserts the example imports **only** `require("verifyhash")` +
131
+ relative files, with **no** deep `cli/*` import, **no** `child_process`, **no** built-in, and **no** network,
132
+ and a snapshot check asserts it leaves no temp dir in the repo — so the "in-process public verify stands
133
+ alone" claim can never silently rot.
134
+
135
+ ## `run.js` — the end-to-end DataLedger + ProofParcel buyer pipeline
136
+
137
+ One command, zero setup, fully offline:
138
+
139
+ ```bash
140
+ node examples/run.js
141
+ ```
142
+
143
+ This drives the **real** DataLedger + ProofParcel buyer pipeline against the tiny committed sample data
144
+ in this directory, using the **same module entrypoints the `vh` CLI dispatches to** (`cli/dataset.js`,
145
+ `cli/parcel.js`) — it is not a brittle shell pipeline of string parsing. It prints a clear **PASS/FAIL**
146
+ summary with the produced artifact paths.
147
+
148
+ ## What it runs
149
+
150
+ - **DataLedger:** `dataset build` → `check --policy` (a PASS against a lenient policy **and** a FAIL
151
+ against a strict one) → `verify` (a MATCH against the untouched sample **and** a MISMATCH after a
152
+ one-byte tamper) → `report` (one filed evidence document) → `attest` (the canonical UNSIGNED bytes).
153
+ - **ProofParcel:** `parcel build` → `verify` (MATCH **and** a tamper MISMATCH) → `attest`.
154
+
155
+ The sample contains **deliberate** problems so the gates have something real to catch:
156
+
157
+ - `vendored/gpl-snippet.txt` carries a `GPL-3.0` license hint → flagged by `denyLicenses`.
158
+ - `data/unlabeled.txt` carries no license hint → flagged by `requireLicense`.
159
+
160
+ ## Where it writes
161
+
162
+ The committed sample under `examples/` is **read-only** to the script. Everything it produces (manifests,
163
+ the report, the unsigned attestation bytes, and the working copies it deliberately tampers) goes to a
164
+ fresh **OS temp dir**. Override the location with `VH_EXAMPLE_OUT=/some/path`; keep the artifacts for
165
+ inspection with `VH_EXAMPLE_KEEP=1`. **Nothing is ever scattered into the repo working tree.**
166
+
167
+ ## Trust posture (read this — the script will not let you forget it)
168
+
169
+ The example proves **tamper-evidence** (any edit, rename, add, or remove flips the Merkle root) and emits
170
+ the canonical **UNSIGNED** attestation bytes a trust-root would sign. It does **not**, and cannot, prove
171
+ *"unaltered since date T"*: that standing claim rides the **human-owned** signing / timestamp / anchor
172
+ trust-root (`needs-human`, P-3 in [`STRATEGY.md`](../STRATEGY.md)). The script **references but never
173
+ executes** those `sign` / `timestamp` / anchor steps, and says exactly where the human handoff is. See
174
+ [`docs/TRUST-BOUNDARIES.md`](../docs/TRUST-BOUNDARIES.md), [`docs/DATALEDGER.md`](../docs/DATALEDGER.md),
175
+ and [`docs/PROOFPARCEL.md`](../docs/PROOFPARCEL.md).
176
+
177
+ ## It cannot rot
178
+
179
+ `test/cli.examples.test.js` runs this example end-to-end against the committed sample on every
180
+ `npx hardhat test`, asserting the pipeline completes, the policy violation is flagged, the tamper is
181
+ caught, and the run leaves the working tree clean.
182
+
183
+
184
+ ---
185
+ <sub>© 2026 verifyhash.com · Licensed under Apache-2.0 (SPDX-License-Identifier: Apache-2.0) — see the [LICENSE](https://verifyhash.com/LICENSE) and [NOTICE](https://verifyhash.com/NOTICE) served with this file.</sub>
@@ -0,0 +1,5 @@
1
+ {
2
+ "kind": "verifyhash.dataset-policy",
3
+ "schemaVersion": 1,
4
+ "denyLicenses": ["AGPL-3.0"]
5
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "kind": "verifyhash.dataset-policy",
3
+ "schemaVersion": 1,
4
+ "denyLicenses": ["GPL-3.0", "AGPL-3.0"],
5
+ "requireLicense": true
6
+ }