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.
- package/README.md +5 -3
- package/cli/agent-hook.js +431 -0
- package/docs/ADOPT.md +15 -5
- package/docs/AGENT-HOOK.md +111 -0
- package/docs/ANCHORING.md +43 -22
- package/docs/PUBLISH-VERIFY-VH.md +45 -0
- package/examples/README.md +185 -0
- package/examples/policy.lenient.json +5 -0
- package/examples/policy.strict.json +6 -0
- package/examples/run.js +366 -0
- package/examples/sample-dataset/README.txt +10 -0
- package/examples/sample-dataset/corpus/cc-by-poem.txt +8 -0
- package/examples/sample-dataset/corpus/mit-notes.txt +4 -0
- package/examples/sample-dataset/data/unlabeled.txt +5 -0
- package/examples/sample-dataset/vendored/gpl-snippet.txt +5 -0
- package/examples/sample-dataset.hints.json +7 -0
- package/examples/sample-parcel/data/manifest-of-contents.txt +7 -0
- package/examples/sample-parcel/data/records.csv +4 -0
- package/examples/sample-parcel/delivery-note.txt +9 -0
- package/package.json +26 -3
- package/verifier/README.md +584 -0
- package/verifier/action/README.md +87 -0
- package/verifier/action/action.yml +146 -0
- package/verifier/build-standalone-html.js +1287 -0
- package/verifier/build-standalone.js +989 -0
- package/verifier/ci/journal.generic.sh +96 -0
- package/verifier/ci/journal.github-actions.yml +99 -0
- package/verifier/ci/reproduce-vh.generic.sh +59 -0
- package/verifier/ci/reproduce-vh.github-actions.yml +49 -0
- package/verifier/ci/verify-service.generic.sh +96 -0
- package/verifier/ci/verify-service.github-actions.yml +88 -0
- package/verifier/ci/verify-vh.generic.sh +75 -0
- package/verifier/ci/verify-vh.github-actions.yml +56 -0
- package/verifier/dist/BUILD-PROVENANCE.json +210 -0
- package/verifier/dist/seal-vh-standalone.js +876 -0
- package/verifier/dist/seal-vh-standalone.js.sha256 +1 -0
- package/verifier/dist/verify-vh-standalone.html +3373 -0
- package/verifier/dist/verify-vh-standalone.html.sha256 +1 -0
- package/verifier/dist/verify-vh-standalone.js +5123 -0
- package/verifier/dist/verify-vh-standalone.js.sha256 +1 -0
- package/verifier/lib/canonical.js +141 -0
- package/verifier/lib/keccak.js +30 -0
- package/verifier/lib/keccak256-vendored.js +206 -0
- package/verifier/lib/merkle.js +145 -0
- package/verifier/lib/revocation-core.js +606 -0
- package/verifier/lib/revocation.js +200 -0
- package/verifier/lib/seal-cli.js +374 -0
- package/verifier/lib/seal-evidence.js +237 -0
- package/verifier/lib/secp256k1-recover.js +249 -0
- package/verifier/package.json +39 -0
- package/verifier/verify-vh.js +3376 -0
- package/docs/ADOPTION.json +0 -11
- package/docs/AUDIT.md +0 -55
- package/docs/DECIDE.md +0 -47
- package/docs/DECISIONS-PENDING.md +0 -27
- package/docs/DEPLOY-PUBLIC-SITE.md +0 -301
- package/docs/ENGINE-LEDGER.json +0 -12
- package/docs/LOOP-AUDIT-2026-07-03.json +0 -580
- package/docs/LOOP-HARDENING-PLAN.md +0 -44
- package/docs/METRICS.jsonl +0 -31
- package/docs/MORNING.md +0 -204
- package/docs/STRATEGY-ARCHIVE.md +0 -5055
- package/docs/SUPERVISOR-RUNBOOK.md +0 -52
- 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
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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>
|