verifyhash 0.1.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 (154) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +883 -0
  3. package/cli/abi/ContributionRegistry.json +881 -0
  4. package/cli/agent.js +2173 -0
  5. package/cli/anchor-artifact.js +853 -0
  6. package/cli/anchor.js +400 -0
  7. package/cli/claim.js +881 -0
  8. package/cli/core/agent-commit.js +448 -0
  9. package/cli/core/agent-session.js +598 -0
  10. package/cli/core/anchor-binding.js +663 -0
  11. package/cli/core/attestation.js +580 -0
  12. package/cli/core/evidence-plans.js +495 -0
  13. package/cli/core/fixtures/evidence-plans/baseline.json +19 -0
  14. package/cli/core/fulfill-intake.js +1082 -0
  15. package/cli/core/go-live-preflight.js +481 -0
  16. package/cli/core/license.js +534 -0
  17. package/cli/core/manifest.js +243 -0
  18. package/cli/core/packetseal.js +591 -0
  19. package/cli/core/registryArtifact.js +49 -0
  20. package/cli/core/revocation.js +539 -0
  21. package/cli/core/rfc3161.js +389 -0
  22. package/cli/core/timestamp.js +482 -0
  23. package/cli/core/trust-asof.js +479 -0
  24. package/cli/dataset.js +2950 -0
  25. package/cli/evidence.js +2227 -0
  26. package/cli/fulfill-webhook-http.js +438 -0
  27. package/cli/git.js +220 -0
  28. package/cli/hash.js +550 -0
  29. package/cli/identity.js +1072 -0
  30. package/cli/journal-cli.js +1110 -0
  31. package/cli/journal-log.js +454 -0
  32. package/cli/journal.js +334 -0
  33. package/cli/lineage.js +447 -0
  34. package/cli/list.js +287 -0
  35. package/cli/parcel.js +1509 -0
  36. package/cli/proof.js +578 -0
  37. package/cli/prove.js +300 -0
  38. package/cli/receipt.js +631 -0
  39. package/cli/registry.js +331 -0
  40. package/cli/reputation.js +344 -0
  41. package/cli/revocation.js +495 -0
  42. package/cli/serve-verify-http.js +298 -0
  43. package/cli/serve-verify.js +333 -0
  44. package/cli/show.js +339 -0
  45. package/cli/verify.js +383 -0
  46. package/cli/vh.js +3927 -0
  47. package/docs/ADOPT.md +183 -0
  48. package/docs/ADOPTION.json +11 -0
  49. package/docs/AGENTTRACE.md +247 -0
  50. package/docs/ANCHORING.md +167 -0
  51. package/docs/AUDIT.md +55 -0
  52. package/docs/CONFORMANCE.md +107 -0
  53. package/docs/DATALEDGER.md +638 -0
  54. package/docs/DECIDE.md +47 -0
  55. package/docs/DECISIONS-PENDING.md +27 -0
  56. package/docs/DEPLOY-PUBLIC-SITE.md +301 -0
  57. package/docs/ENGINE-LEDGER.json +12 -0
  58. package/docs/EVIDENCE.md +519 -0
  59. package/docs/GO-LIVE.md +66 -0
  60. package/docs/IDENTITY.md +123 -0
  61. package/docs/INDEPENDENT-VERIFICATION.md +377 -0
  62. package/docs/INTEGRITY-JOURNAL.md +337 -0
  63. package/docs/KEY-LIFECYCLE.md +179 -0
  64. package/docs/LICENSING.md +46 -0
  65. package/docs/LINEAGE.md +307 -0
  66. package/docs/LOOP-AUDIT-2026-07-03.json +580 -0
  67. package/docs/LOOP-HARDENING-PLAN.md +44 -0
  68. package/docs/MERKLE-LEAVES.md +113 -0
  69. package/docs/METRICS.jsonl +31 -0
  70. package/docs/MORNING.md +204 -0
  71. package/docs/PILOT.md +444 -0
  72. package/docs/PROOFPARCEL.md +227 -0
  73. package/docs/PROOFS.md +262 -0
  74. package/docs/RECEIPTS.md +341 -0
  75. package/docs/REPUTATION.md +158 -0
  76. package/docs/SDK.md +301 -0
  77. package/docs/STRATEGY-ARCHIVE.md +5055 -0
  78. package/docs/SUPERVISOR-RUNBOOK.md +52 -0
  79. package/docs/TRUST-BOUNDARIES.md +335 -0
  80. package/docs/TRUSTLEDGER.md +1976 -0
  81. package/docs/USAGE-BUDGET.json +121 -0
  82. package/docs/VERIFY-SERVICE.md +168 -0
  83. package/index.js +160 -0
  84. package/package.json +41 -0
  85. package/trustledger/build-standalone.js +796 -0
  86. package/trustledger/cli.js +3179 -0
  87. package/trustledger/close.js +391 -0
  88. package/trustledger/corpus.js +159 -0
  89. package/trustledger/dist/BUILD-PROVENANCE.json +99 -0
  90. package/trustledger/dist/trustledger-standalone.html +6197 -0
  91. package/trustledger/dist/trustledger-standalone.html.sha256 +1 -0
  92. package/trustledger/door-core.js +442 -0
  93. package/trustledger/fixtures/bank.csv +7 -0
  94. package/trustledger/fixtures/bank.malformed.csv +3 -0
  95. package/trustledger/fixtures/bank.noalias.csv +5 -0
  96. package/trustledger/fixtures/bank.ofx +34 -0
  97. package/trustledger/fixtures/bank.real.csv +5 -0
  98. package/trustledger/fixtures/corpus/_shared/prior-close.json +22 -0
  99. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/inputs.json +14 -0
  100. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/meta.json +7 -0
  101. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/inputs.json +14 -0
  102. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/meta.json +7 -0
  103. package/trustledger/fixtures/corpus/continuity-break--benign-twin/inputs.json +15 -0
  104. package/trustledger/fixtures/corpus/continuity-break--benign-twin/meta.json +7 -0
  105. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/inputs.json +15 -0
  106. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/meta.json +7 -0
  107. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/inputs.json +13 -0
  108. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/meta.json +7 -0
  109. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/inputs.json +13 -0
  110. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/meta.json +7 -0
  111. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/inputs.json +15 -0
  112. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/meta.json +7 -0
  113. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/inputs.json +15 -0
  114. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/meta.json +7 -0
  115. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/inputs.json +16 -0
  116. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/meta.json +7 -0
  117. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/inputs.json +13 -0
  118. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/meta.json +7 -0
  119. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/inputs.json +13 -0
  120. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/meta.json +7 -0
  121. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/inputs.json +13 -0
  122. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/meta.json +7 -0
  123. package/trustledger/fixtures/e2e/bank.aliased.csv +4 -0
  124. package/trustledger/fixtures/e2e/bank.csv +4 -0
  125. package/trustledger/fixtures/e2e/bank.nsf.csv +4 -0
  126. package/trustledger/fixtures/e2e/quickbooks.csv +6 -0
  127. package/trustledger/fixtures/e2e/quickbooks.nsf.csv +8 -0
  128. package/trustledger/fixtures/e2e/rentroll.csv +6 -0
  129. package/trustledger/fixtures/e2e/rentroll.nsf.csv +8 -0
  130. package/trustledger/fixtures/e2e/rentroll.short.csv +5 -0
  131. package/trustledger/fixtures/plans/baseline.json +25 -0
  132. package/trustledger/fixtures/plans/price-binding.example.json +27 -0
  133. package/trustledger/fixtures/policy/ambiguous-deposit-example.json +12 -0
  134. package/trustledger/fixtures/policy/baseline.json +19 -0
  135. package/trustledger/fixtures/policy/ca-example.json +12 -0
  136. package/trustledger/fixtures/policy/negative-tenant-ledger-example.json +12 -0
  137. package/trustledger/fixtures/policy/owner-overdraw-example.json +12 -0
  138. package/trustledger/fixtures/quickbooks.csv +7 -0
  139. package/trustledger/fixtures/quickbooks.real.csv +5 -0
  140. package/trustledger/fixtures/rentroll.csv +6 -0
  141. package/trustledger/fixtures/rentroll.real.csv +4 -0
  142. package/trustledger/ingest.js +1163 -0
  143. package/trustledger/lib/policy-bundled-loader.js +44 -0
  144. package/trustledger/lib/sha256-vendored.js +227 -0
  145. package/trustledger/license.js +563 -0
  146. package/trustledger/match.js +551 -0
  147. package/trustledger/plans.js +551 -0
  148. package/trustledger/policy.js +398 -0
  149. package/trustledger/public/index.html +512 -0
  150. package/trustledger/reconcile.js +1486 -0
  151. package/trustledger/report.js +887 -0
  152. package/trustledger/seal.js +854 -0
  153. package/trustledger/server.js +391 -0
  154. package/trustledger/valueproof.js +350 -0
package/README.md ADDED
@@ -0,0 +1,883 @@
1
+ # verifyhash
2
+
3
+ A tamper-evident, permissionless, immutable on-chain registry of code-contribution hashes
4
+ (Polygon-targeted). Anchor the hash of a file or an entire repository on-chain; anyone can later
5
+ prove that some content is byte-for-byte what was anchored — without trusting any server, any
6
+ admin, or any private key to read.
7
+
8
+ The registry contract (`contracts/ContributionRegistry.sol`) is deliberately ownerless: no admin,
9
+ no pause, no upgrade path, and it never holds funds. Each content hash can be anchored exactly once
10
+ (first-writer-wins) and can never be altered or deleted. That immutability is the product.
11
+
12
+ **Live deployment — Polygon mainnet (chain id 137):**
13
+ [`0x77d8eF881D5aeEda64788968D13f9146fE1A609B`](https://polygonscan.com/address/0x77d8eF881D5aeEda64788968D13f9146fE1A609B)
14
+ (deployed 2026-07-03; ownerless — the deploying key holds no special power over it). Pin this address
15
+ out-of-band; `vh` reads/writes against it with `--registry 0x77d8eF881D5aeEda64788968D13f9146fE1A609B`.
16
+
17
+ > **Ready to charge for it?** [`docs/GO-LIVE.md`](docs/GO-LIVE.md) is the decision-ready "first dollar" page (`npm run go-live`): the **self-serve evidence license** is the recommended default; the design-partner **pilot** is the enterprise fallback.
18
+
19
+ ## Install / Quickstart
20
+
21
+ > **Adopt in one line.** Want to *receive* and *check* sealed artifacts without an account or our toolchain?
22
+ > The self-serve on-ramp is [`docs/ADOPT.md`](docs/ADOPT.md): `npx --yes verify-vh demo` for a 5-second
23
+ > proof, or a one-line GitHub Action (`uses: <owner>/<repo>/verifier/action@<ref>`) to gate your CI — and
24
+ > the free→paid bridge to issuing **your own** signed, customer-verifiable seals (the paid producer surface).
25
+
26
+ `verifyhash` ships the `vh` command. You do **not** need to clone the repo to use it.
27
+
28
+ ```bash
29
+ # from a published package (publishing to npm is a human step — see below):
30
+ npm install -g verifyhash # puts `vh` on your PATH
31
+ # or run it without installing:
32
+ npx verifyhash --help
33
+
34
+ vh --help # the full usage block
35
+ vh hash ./myfile.txt # keccak256 of a file (offline, no key, no network)
36
+ ```
37
+
38
+ Building a tamper-evident dataset manifest works the same way — fully offline — via the
39
+ DataLedger subcommands documented below.
40
+
41
+ ### See the whole pipeline in one command (offline, no key, no network)
42
+
43
+ The fastest way to see what `verifyhash` does is to run the committed end-to-end example. From a
44
+ checkout (`npm install`, then):
45
+
46
+ ```bash
47
+ node examples/run.js
48
+ ```
49
+
50
+ It exercises the **real** DataLedger + ProofParcel buyer pipeline against tiny committed sample data —
51
+ `dataset build → check --policy → verify → report → attest` and `parcel build → verify → attest` —
52
+ prints a clear **PASS/FAIL** summary with the produced artifact paths, and demonstrates that a deliberate
53
+ **policy violation is FLAGGED** and a **one-byte tamper is caught**. It writes its artifacts to an OS
54
+ temp dir (override with `VH_EXAMPLE_OUT`, keep them with `VH_EXAMPLE_KEEP=1`) — **never** into the repo.
55
+ It needs **no key, no TSA, no RPC, no network**, and it references — but never runs — the human-gated
56
+ `sign` / `timestamp` / anchor steps so you can see exactly where the trust-root handoff is. The example
57
+ is gated by `test/cli.examples.test.js`, so it can never silently rot. See
58
+ [`examples/README.md`](examples/README.md).
59
+
60
+ The offline commands need only the package's
61
+ runtime dependencies (`ethers`, `js-sha3`) — no chain, no key, no `hardhat`. The on-chain
62
+ read/write commands (`vh anchor/claim/verify/list/show/lineage/reputation`) additionally
63
+ need an RPC endpoint; the registry ABI is bundled in the package, so they run from a clean
64
+ install without any compile step.
65
+
66
+ **Local install (works today, no registry needed):** from a checkout of this repo,
67
+
68
+ ```bash
69
+ npm install # installs dependencies (incl. devDependencies)
70
+ npm install -g . # OR: npm link — both create a global `vh` from this checkout
71
+ vh --help
72
+ ```
73
+
74
+ > Publishing `verifyhash` to the public npm registry (so `npm install -g verifyhash`
75
+ > resolves remotely) is a **human action** and is intentionally not performed by the build.
76
+ > Until then, use the local install path above.
77
+
78
+ ## What it proves (and what it does NOT)
79
+
80
+ > **Read this before relying on a record:** [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md)
81
+
82
+ A record attests to **one** thing: the exact 32-byte `contentHash` you queried was anchored
83
+ on-chain, by some address, in some block, and has not changed since. The other fields are weaker
84
+ than they look:
85
+
86
+ - **`uri` is an untrusted hint.** The contract never fetches, validates, or hashes it. To trust a
87
+ record you must independently fetch the content, **re-derive its hash** (`vh hash`), and check that
88
+ the recomputed hash equals the anchored `contentHash`. The `uri` proves nothing on its own.
89
+ - **`timestamp` / `blockNumber` prove on-chain ordering and an upper bound on existence time**
90
+ ("this content existed no later than this block") — **not** authorship time and not who authored
91
+ it. `block.timestamp` is set by the block proposer (validator-influenced), so it is not a precise
92
+ wall clock; prefer `blockNumber` for hard ordering.
93
+ - **`contributor` has two strengths, told apart by the `authorBound` flag** (decision D-1 /
94
+ task T-0.3). A one-shot `anchor()` is **front-runnable** — `contributor` is only the "first
95
+ anchorer", not a proven author (`authorBound = false`). The **commit-reveal** path (`vh claim`)
96
+ binds the claimant to the content before the hash is public, so a mempool copier cannot steal
97
+ attribution; those records have `authorBound = true` and `contributor` is the proven first
98
+ *claimant*.
99
+
100
+ Full detail, including the table of "trust it for / do NOT trust it for", is in
101
+ [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md). The exact directory-root construction is in
102
+ [`docs/MERKLE-LEAVES.md`](docs/MERKLE-LEAVES.md).
103
+
104
+ ## CLI (`cli/vh.js`)
105
+
106
+ ```
107
+ vh hash <path> [--git [--ref r]] # keccak256 of a file, or the Merkle root of a directory
108
+ vh anchor <path> [--uri u] [--git] [--parent 0xhash] # one-shot anchor (FRONT-RUNNABLE: contributor = first anchorer only); --parent records a lineage edge
109
+ vh claim <path> [--uri u] [--git] [--parent 0xhash] # commit-reveal in one shot: front-running-resistant claim (authorBound); --parent records a lineage edge
110
+ vh commit <path> [--receipt p] [--parent 0xhash] # commit-reveal step 1: commit + persist a resumable claim receipt (records --parent into the receipt, schema v4)
111
+ vh reveal --receipt <p> # commit-reveal step 2: resume from the receipt and reveal (carries the receipt's --parent edge — no flag needed)
112
+ vh verify <path> [--git [--ref r]] # recompute the hash, look it up on-chain, report MATCH / MISMATCH
113
+ vh prove <file> --root dir [--out p] # Merkle-prove a file against an anchored root; --out exports a portable artifact (read-only, no key, no repo needed to verify)
114
+ vh verify-proof <p> # read-only: independently verify a portable proof artifact (offline fold + on-chain; no key, no repo needed)
115
+ vh list [filters] # read-only: enumerate the registry (discovery + audit, NO key)
116
+ vh show <0xhash> # read-only: look up ONE record by hash, no local content (NO key)
117
+ vh lineage <0xhash> [--max-depth n] # read-only walk UP the parent chain to the lineage root (no key)
118
+ vh reputation <addr> # read-only, no key, authenticated: a non-transferable, re-derivable contribution SCORE for one address (NOT a token)
119
+ vh dataset build <dir> --out <p> # DataLedger: tamper-evident dataset manifest (Merkle root + per-file leaves); offline, no key, no network
120
+ vh dataset verify <dir> --manifest <p> # re-derive the root + per-file ADDED/REMOVED/CHANGED diff vs a manifest; offline, no key, no network
121
+ vh dataset diff <manifestA> <manifestB> # the exact change set between two dataset versions; offline, no key, no network
122
+ vh dataset summary <manifest> # provenance/license roll-up over the trusted file set; offline, no key, no network
123
+ vh dataset check <manifest> --policy <p> # OFFLINE license/source policy gate (PASS/FAIL, CI-gateable exit 0/3); offline, no key, no network
124
+ vh dataset report <manifest> [--verify <dir>] [--policy <p>] # ONE deterministic evidence document the reviewer files; offline, no key, no network
125
+ vh dataset attest <manifest> # canonical UNSIGNED attestation payload a human trust-root signs (P-3); offline, no key, no network
126
+ vh dataset sign <manifest> --key-env <VAR>|--key-file <p> # sign the UNSIGNED attestation with a key YOU provisioned -> signed container; reads YOUR key, never generates/persists/logs one; offline, no network
127
+ vh dataset verify-attest <signed> [--manifest <m>] [--signer <addr>] # OFFLINE-verify a SIGNED attestation container (recover signer, pin publisher, bind manifest); no key, no network, CI-gateable exit 0/3
128
+ vh dataset timestamp-request <manifest> # emit the SHA-256 digest your RFC-3161 TSA stamps (P-3 Option B); offline, no key, no network
129
+ vh dataset timestamp-wrap <manifest> --token <p> # wrap a TSA's RFC-3161 token -> verifiable timestamped container; offline, no key, no network
130
+ vh dataset verify-timestamp <container> [--manifest <m>] # OFFLINE-verify an RFC-3161 timestamped attestation (genTime/serial/policy; bind manifest); no key, no network, CI-gateable exit 0/3
131
+ vh dataset prove --file <p> --manifest <m> # set-membership proof for ONE file; offline, no key, no network
132
+ vh dataset verify-proof <proof> # fold a membership proof back to the recorded root; offline, no key, no network
133
+ vh parcel build <dir> --out <p> # ProofParcel: tamper-evident B2B delivery receipt (root + per-file leaves + untrusted parcel meta); offline, no key, no network
134
+ vh parcel verify <dir> --manifest <p> # re-derive the root + per-file diff vs a parcel manifest; offline, no key, no network
135
+ vh parcel attest <manifest> # canonical UNSIGNED parcel-attestation payload a human trust-root signs (P-3); offline, no key, no network
136
+ vh parcel sign <manifest> --key-env <VAR>|--key-file <p> # sign the UNSIGNED parcel attestation with a key YOU provisioned -> signed container; reads YOUR key, never generates/persists/logs one; offline, no network
137
+ vh parcel verify-attest <signed> [--manifest <m>] [--signer <addr>] # OFFLINE-verify a SIGNED parcel attestation (recover signer, pin sender, bind parcel); no key, no network, CI-gateable exit 0/3
138
+ vh parcel timestamp-request <manifest> # emit the SHA-256 digest your RFC-3161 TSA stamps (P-3 Option B); offline, no key, no network
139
+ vh parcel timestamp-wrap <manifest> --token <p> # wrap a TSA's RFC-3161 token -> verifiable timestamped container; offline, no key, no network
140
+ vh parcel verify-timestamp <container> [--manifest <m>] # OFFLINE-verify an RFC-3161 timestamped parcel attestation (genTime/serial/policy; bind parcel); no key, no network, CI-gateable exit 0/3
141
+ vh identity publish --address <0xaddr> --product-line <line> --claim <text> --non-claim <text> (--key-env <VAR>|--key-file <p>) [--out <p>] # mint a signed producer IDENTITY CARD binding a vendorAddress to a bounded claim set; signs with a key YOU provisioned (reads/never holds it); mints ONLY when the key controls --address; default PRINTS + writes NOTHING; offline, no network
142
+ vh identity verify <card> [--signer <0xaddr>] # OFFLINE/key-free: RECOVER the signer, require it to BE the card's vendorAddress, OPTIONALLY pin --signer + print the claims/non-claims; forged/tampered/wrong-vendor/wrong-signer is a clean REJECTED; exit 0 ACCEPTED / 3 REJECTED / 2 usage / 1 IO
143
+ vh revocation publish --address <0xaddr> --reason <reason> (--key-env <VAR>|--key-file <p>) [--superseded-by <0xaddr>] [--revoked-at <ISO>] [--out <p>] # mint a signed producer KEY REVOCATION marking --address REVOKED for --reason; signs with a key YOU provisioned (reads/never holds it); mints ONLY when the key controls --address (a key revokes ITSELF); default PRINTS + writes NOTHING; a SIGNED CLAIM, NOT a trusted timestamp without P-3; offline
144
+ vh revocation verify <revocation> [--signer <0xaddr>] # OFFLINE/key-free: RECOVER the signer, require it to BE the revocation's vendorAddress, OPTIONALLY pin --signer + print reason/revokedAt/supersededBy; forged/tampered/third-party is a clean REJECTED; exit 0 ACCEPTED / 3 REJECTED / 2 usage / 1 IO
145
+ vh anchor-artifact <sealed-file> --contract <a> --rpc <url> (--key-env <VAR>|--key-file <p>) [--author-bound] [--out <receipt>] # anchor ANY sealed artifact's ONE canonical digest on-chain (evidence/agent/journal/trustledger/dataset/parcel) -> portable vh-anchored-receipt@1; FREE (gas is your own); --author-bound = commit-reveal (D-1, authorBound:true)
146
+ vh verify-anchored <receipt> <sealed-file> [--rpc <url> --contract <a>] # OFFLINE by default (no key, no network): recompute the digest through the same closed table, every deviation a named reject; with --rpc+--contract also authenticate the registry + re-check the receipt's chain facts; exit 0/3, CI-gateable
147
+ ```
148
+
149
+ > **Read commands authenticate the registry by default.** Every read command (`verify` / `show` /
150
+ > `list` / `lineage` / `verify-proof`) **authenticates the registry before reporting anything** — a
151
+ > wrong/rogue RPC+address could otherwise fabricate a `MATCH`/`ACCEPTED`. The preflight confirms a
152
+ > contract is deployed there (bytecode), reads its immutable `REGISTRY_ID`/version, and (for
153
+ > `verify-proof`) cross-checks the artifact's `chainId`; it prints a `registry authenticated: …` line
154
+ > (`--json`: a `registry` block). A **loud, non-default `--skip-identity-check`** opts out for a known
155
+ > local-dev contract (the output then says the verdict is only as trustworthy as the RPC you supplied).
156
+ > The `REGISTRY_ID` is a "right interface" signal verified alongside bytecode + chainId, **not a sole
157
+ > root of trust** — a fork can reuse it, so pin the address out-of-band if you need a SPECIFIC
158
+ > deployment. See [authenticated reads](#authenticated-reads-registry-identity--chainid) and
159
+ > [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md).
160
+
161
+ > **`--parent <0xhash>` records a contribution lineage edge.** `vh anchor/claim <path> --parent
162
+ > <hash>` anchors the record AS a revision of an ALREADY-anchored predecessor (the parent must already
163
+ > exist on-chain or the tx reverts `UnknownParent`); omit it for a lineage root. A `parent` edge is the
164
+ > **child author's CLAIM** of a predecessor — it neither proves genuine content ancestry (re-derive
165
+ > **both** contents) nor transfers the parent's authorship. `vh lineage <0xhash>` is the **read-only
166
+ > walk, no key**: it follows the parent chain from a record UP to its lineage root. See
167
+ > [contribution lineage](#contribution-lineage-vh-anchorclaim---parent--vh-lineage).
168
+
169
+ > **`--git` scopes a directory to exactly what git tracks.** `vh hash/anchor/claim/verify <dir> --git
170
+ > [--ref <ref>]` hashes **EXACTLY the files git tracks at that commit** (`--ref` defaults to `HEAD`),
171
+ > reading their bytes from the work tree. It deliberately **never** includes `.git/` internals,
172
+ > untracked files, secrets like `.env`, `node_modules/`, or build output — so the git-scoped root is
173
+ > **reproducible from a fresh clone of the same commit** and is not perturbed by whatever junk happens
174
+ > to sit in your working tree. See [git-scoped, reproducible anchoring](#git-scoped-reproducible-anchoring).
175
+
176
+ `vh anchor` is a single cheap transaction but its `contentHash` is public in the mempool, so anyone
177
+ can copy and anchor it first — use it only for existence/timestamp proofs where attribution does not
178
+ matter. `vh claim` runs the two-step commit-reveal flow (`commit` a sender-bound, salt-blinded
179
+ commitment, wait `MIN_REVEAL_DELAY` blocks, then `reveal`) so a front-runner cannot become the
180
+ recorded contributor. See `docs/TRUST-BOUNDARIES.md` for the threat model and why it holds.
181
+
182
+ ### Resumable claims (`vh commit` + `vh reveal`)
183
+
184
+ The commit-reveal flow spans two transactions separated by a maturation window of `MIN_REVEAL_DELAY`
185
+ blocks (minutes on a live testnet). The secret salt that binds your commitment exists only in memory
186
+ during that wait — if the one-shot `vh claim` process crashes or is interrupted, the salt is lost and
187
+ the claim is **permanently unrevealable by anyone** (reveal requires that exact salt). To make a claim
188
+ durable and crash-recoverable, split it:
189
+
190
+ ```
191
+ vh commit ./src --uri ipfs://cid # sends commit(), writes the receipt, prints its EXACT path, then exits
192
+ # ...wait out MIN_REVEAL_DELAY (a few blocks)...
193
+ vh reveal --receipt <that exact path> # resumes from the receipt and reveals
194
+ ```
195
+
196
+ `vh commit` persists a versioned JSON **claim receipt** (salt, commitment, contentHash, committer,
197
+ contract, chainId, commit tx/block, `MIN_REVEAL_DELAY`) **before it returns**, so a separate `vh reveal`
198
+ invocation — even after a reboot — can finish the claim.
199
+
200
+ **The receipt holds the SECRET `salt`** that binds your commitment. Where it is written is always
201
+ something you opt into, and `vh commit` **never writes it silently**:
202
+
203
+ - `--receipt <path>` writes it to that exact file;
204
+ - `--receipt-dir <dir>` writes it into that directory under a tidy default file name;
205
+ - with neither, `vh commit` defaults to `<cwd>/<contentHashPrefix>.vhclaim.json` — **but the success
206
+ output always names the EXACT file written** (`receipt written: <abs path>`), so you can see, move, or
207
+ delete it. It is never dropped where you can't find it. (`*.vhclaim.json` is also git-ignored.)
208
+
209
+ **Keep it private until you reveal** — anyone who holds the salt before reveal could front-run the open;
210
+ after a successful reveal the commitment is single-use and spent, so the receipt is no longer sensitive.
211
+ This reuses the receipt trust posture in [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md): the
212
+ receipt is an *untrusted local convenience*; the authoritative attribution is always the on-chain record.
213
+
214
+ If you reveal before the window matures the contract reverts with `RevealTooSoon` and the receipt is left
215
+ intact, so you can simply retry. **`vh claim`** remains the one-shot convenience (commit + reveal in one
216
+ process); to keep it safe it persists a receipt **only if you ask** (`--receipt`/`--receipt-dir`) — by
217
+ default it writes nothing and you use `vh commit` for a durable, resumable claim.
218
+
219
+ The full receipt JSON schema (every field, which are trusted vs untrusted hints), the commit→reveal
220
+ resume lifecycle, and the directory-manifest diff semantics are specified in
221
+ [`docs/RECEIPTS.md`](docs/RECEIPTS.md).
222
+
223
+ ### Discovery & audit (`vh list` + `vh show`)
224
+
225
+ `vh verify` answers "is THIS content anchored?"; the read side answers "WHAT is in the registry?".
226
+ Both are **read-only and need no key** — they take an RPC provider only and never construct a signer,
227
+ because enumerating or reading a public, immutable registry must never require the ability to write
228
+ to it.
229
+
230
+ ```
231
+ vh list # every record, in insertion order
232
+ vh list --contributor 0xABC… --json # filter by address; machine-readable JSON
233
+ vh list --author-bound --limit 20 # only commit-reveal records; page with --limit/--offset
234
+ vh show 0x<64-hex> # one record by content hash — no files on disk needed
235
+ ```
236
+
237
+ `vh list` pages through the registry and prints one block per record (contentHash, contributor,
238
+ attribution strength, timestamp, blockNumber, uri), filterable by `--contributor` / `--author-bound`
239
+ and sliceable with `--limit` / `--offset` (or `--json` for tooling). `vh show <0xhash>` looks up a
240
+ single record by a hash you already have (copied from `vh list`, a receipt, or a PR) and exits
241
+ non-zero with `NOT ANCHORED` when there is no such record.
242
+
243
+ > **`vh list --json` is an ENVELOPE, not a bare array** (changed in T-11.2). The output is
244
+ > `{ "registry": { "id", "version", "chainId" }, "records": [ … ] }` — the `registry` block proves the
245
+ > records were read from an [authenticated registry](#authenticated-reads-registry-identity--chainid)
246
+ > (or carries `{ "skipped": true, "note": … }` when `--skip-identity-check` was used), and `records` is
247
+ > the array a consumer iterates. **This is a breaking change for any consumer that previously did
248
+ > `JSON.parse(out)[0]`** — iterate `JSON.parse(out).records` instead. (`vh show` / `vh lineage` /
249
+ > `vh verify-proof` each likewise carry a top-level `registry` block.)
250
+
251
+ > **Listing or showing a record does NOT validate its content.** Both commands only read what is
252
+ > on-chain — they never touch your files, so a hit binds nothing to real bytes you hold. `uri` stays
253
+ > an **untrusted hint** the contract never fetched or validated, and `contributor` only means proven
254
+ > authorship when `authorBound` is `true` (commit-reveal); otherwise it is merely the first anchorer.
255
+ > To bind a record to actual content you must still independently fetch it, **re-derive its hash**,
256
+ > and run `vh verify <path>` (re-derive-and-compare). These are exactly the caveats in
257
+ > [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md), which the read commands lead their output
258
+ > with verbatim.
259
+
260
+ `vh verify` is read-only: it re-derives the content hash and compares it to what is anchored, which
261
+ is exactly the integrity check the trust model requires. It needs only an RPC URL — no key, no
262
+ funds.
263
+
264
+ ### Authenticated reads (registry identity + chainId)
265
+
266
+ The project's core promise is to prove things **without trusting any server**. But the `(rpc, address)`
267
+ pair a reader uses is itself untrusted — it comes from a prover, a receipt's `contractAddress`, a
268
+ README, or a forwarded event. A *rogue or wrong* contract that implements the same ABI shape could
269
+ return `isAnchored = true` / fabricated records and make the CLI print `MATCH` / `ACCEPTED`. So before
270
+ believing any record, **every read command authenticates the registry first** (T-11.2):
271
+
272
+ - `vh verify`, `vh show`, `vh list`, `vh lineage`, and `vh verify-proof` run a shared preflight
273
+ (`cli/registry.js › assertRegistry`) that (a) confirms a contract is actually deployed at the address
274
+ (`getCode`), (b) reads the contract's immutable `REGISTRY_ID()` / `REGISTRY_VERSION()` self-identity
275
+ marker and refuses to trust a contract that is not a genuine verifyhash registry, and (c) — for
276
+ `vh verify-proof`, whose artifact records the `chainId` it was anchored on — cross-checks the
277
+ provider's chainId so a verdict is never reported against the wrong network.
278
+ - The human output gains a one-line confirmation so you can **see** the check ran:
279
+ `registry authenticated: REGISTRY_ID ok (vN), chainId N` — printed **before** any verdict/record.
280
+ - `--json` carries a machine-readable `registry: { id, version, chainId }` block on every read command.
281
+ - A genuine RPC/network error is surfaced **as itself** — it is never masqueraded as an identity
282
+ failure (mirroring the `isNotAnchoredError` discipline `vh verify` already uses).
283
+
284
+ ```
285
+ vh verify ./repo --git # prints "registry authenticated: …" then MATCH/MISMATCH
286
+ vh show 0x<hash> --json # → { "registry": { id, version, chainId }, … }
287
+ vh verify-proof proof.json --rpc <url> # rejects if the provider's chainId != the artifact's
288
+ ```
289
+
290
+ > **Opt-out (`--skip-identity-check`) is loud and never the default.** If you KNOW you are pointed at a
291
+ > not-yet-deployed / local-dev contract, every read command accepts `--skip-identity-check` to bypass
292
+ > the preflight. When you use it the output says so unmistakably — human:
293
+ > `registry authentication: SKIPPED (--skip-identity-check) … the verdict is only as trustworthy as the
294
+ > RPC/address you supplied`; `--json`: `registry: { "skipped": true, "note": … }`. Without the flag,
295
+ > **every read command authenticates**.
296
+
297
+ > **The `REGISTRY_ID` is a "right interface" signal, NOT a sole root of trust.** It is verified
298
+ > alongside the deployed bytecode + chainId and proves you are talking to a contract that *implements
299
+ > the verifyhash interface on the expected chain* — it does **not** make the records honest beyond the
300
+ > contract's own first-writer-wins + commit-reveal rules, and because the constant is open source, **a
301
+ > fork can compile and return the same `REGISTRY_ID`**. So a consumer who needs a SPECIFIC deployment
302
+ > (not merely *some* contract that speaks the interface) must **also pin the address out-of-band**.
303
+ > This is exactly the caveat in [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md) and the
304
+ > contract's "ON-CHAIN IDENTITY MARKER" NatSpec.
305
+
306
+ ### Contribution lineage (`vh anchor/claim --parent` + `vh lineage`)
307
+
308
+ A contribution evolves — v2 fixes v1, a fork derives from an upstream, a patch builds on a base. Each
309
+ record may optionally name **one already-anchored predecessor**, turning the registry from a pile of
310
+ unrelated hashes into a contribution **history you can walk and audit**.
311
+
312
+ ```
313
+ vh anchor ./repo-v2 --parent 0xROOT… # anchor v2 AS a revision of the already-anchored root 0xROOT…
314
+ vh claim ./repo-v2 --parent 0xROOT… # same, via commit-reveal (the revision is authorBound)
315
+ vh lineage 0xCHILD… --rpc <url> # read-only walk UP the parent chain: child -> parent -> … -> root
316
+ ```
317
+
318
+ `--parent <0xhash>` records an **immutable predecessor edge** to a hash that **must already be
319
+ anchored** (the contract reverts `UnknownParent` otherwise, and `SelfParent` if a record names itself);
320
+ omit it for a **lineage root**. Because a parent must pre-exist, the graph is **acyclic by
321
+ construction** and the on-chain check is O(1) — no on-chain walk. `--parent` works on the one-shot
322
+ `vh anchor`/`vh claim` **and** on the resumable `vh commit`/`vh reveal` split: `vh commit --parent
323
+ <hash>` persists the edge into the claim receipt (schema **v4**) and a later, separate `vh reveal
324
+ --receipt <p>` reads it back and records it — no `--parent` flag on `vh reveal`. The parent is checked
325
+ on-chain at **reveal** time, so a stale/unanchored parent reverts the *reveal* (not the commit) and
326
+ leaves the receipt reusable for a retry. Naming a parent does not change the child's own attribution
327
+ (lineage and `authorBound` are orthogonal).
328
+
329
+ `vh lineage <0xhash>` is **read-only and needs no key** — it takes a provider only, never a signer —
330
+ and follows `record.parent` from a record UP to its lineage root, printing each ancestor in child→root
331
+ order (`contentHash`, `contributor`, attribution strength, timestamp, blockNumber, uri, parent). The
332
+ walk is **off-chain** and bounded by `--max-depth` (default 256, so a pathological chain can't hang the
333
+ client); `--json` emits an ordered ancestor array an indexer/UI can reconstruct the graph from, and a
334
+ `NOT ANCHORED` start exits non-zero. `vh show <0xhash>` also surfaces a record's `parent` (or
335
+ `(none) — lineage root`).
336
+
337
+ > **A `parent` edge is the child author's CLAIM — not proof of ancestry, not a transfer of authorship.**
338
+ > It proves only that the named predecessor was anchored *before* this child and that the child's author
339
+ > chose to point at it. It does **NOT** prove the predecessor's content is a genuine ancestor of the
340
+ > child's content — re-derive **both** contents (`vh hash`) and judge the relationship yourself — and it
341
+ > does **NOT** transfer the parent's authorship: each record's `contributor`/`authorBound` stands alone.
342
+ > An indexer reconstructs the graph from the `Linked(child, parent)` event (emitted only for non-root
343
+ > records, alongside the unchanged `Anchored`/`Revealed`). These are exactly the caveats in
344
+ > [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md); the full graph spec, the log shape, and a
345
+ > worked anchor-root → anchor-revision → walk-lineage example are in [`docs/LINEAGE.md`](docs/LINEAGE.md).
346
+
347
+ ### Contribution score (`vh reputation <addr>`)
348
+
349
+ `vh reputation <addr>` reports a **verifiable contribution score** for one address: the `total`
350
+ records under it, the **authorBound vs anchor-only** breakdown, the **lineage-root vs revision**
351
+ breakdown, and the earliest/latest block + timestamp. It is **read-only, needs no key, and
352
+ authenticates the registry** (the same [authenticated-read](#authenticated-reads-registry-identity--chainid)
353
+ preflight every read command runs) before reporting anything.
354
+
355
+ ```
356
+ vh reputation 0xABC… --rpc <url> # human block, led by the trust caveat
357
+ vh reputation 0xABC… --json # { registry, address, total, authorBound, anchorOnly, … }
358
+ ```
359
+
360
+ The score is computed from a **single ownerless on-chain read** — the paged, clamped
361
+ `getRecordsByContributor(addr, start, count)` walk (`total` = `records.length` from that walk) — so
362
+ scoring one address is **O(that address's own records), never O(total)**. The companion T-12.1 O(1)
363
+ read `contributorRecordCount(addr)` (which `vh reputation` does **not** call) returns the same `total`
364
+ without paging, for an external consumer that wants the count alone.
365
+
366
+ > **The score is a NON-TRANSFERABLE DERIVED VIEW — NOT a token.** It is re-derivable by anyone from the
367
+ > same registry, holds no value, grants no rights, and `vh reputation` takes a **provider only, never a
368
+ > key**. Any tradeable/reputation-**token** layer is a separate, **human-gated** decision (D-2 / P-1 in
369
+ > `STRATEGY.md`) and is not built here. It does **NOT** validate record content (re-derive + `vh verify`
370
+ > for that), does **NOT** upgrade a front-runnable anchor's attribution, and for anchor-only records the
371
+ > grouping address is merely the **first anchorer**. **Anti-sybil:** the meaningful signal is the
372
+ > **`authorBound` (commit-reveal) count** — producing a front-running-resistant claim has a real cost,
373
+ > whereas anchor-only records and address creation are cheap — so authorBound and anchor-only are
374
+ > reported **separately and never summed**. These are exactly the caveats in
375
+ > [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md); the full definition is in
376
+ > [`docs/REPUTATION.md`](docs/REPUTATION.md).
377
+
378
+ ### Portable proofs (`vh prove --out` + `vh verify-proof`)
379
+
380
+ `vh prove <file> --root <dir>` builds a Merkle proof that a single file is part of an anchored repo
381
+ root, but on its own that proof only lives in the prover's terminal. `--out <p>` exports it as a
382
+ **self-contained, portable proof artifact** — a versioned JSON file carrying everything a verifier
383
+ needs:
384
+
385
+ ```
386
+ vh prove src/index.js --root ./repo --out proof.json # build + export (no key; works with --dry-run)
387
+ vh verify-proof proof.json --rpc <url> # independently verify, needing ONLY the file + an RPC URL
388
+ ```
389
+
390
+ `vh verify-proof <p>` is **read-only and needs no key, no repo, and no working tree** — just the
391
+ artifact and an RPC URL. That is the portability property: hand someone the artifact and they can
392
+ **independently** confirm the file is in the anchored root with **no trust in the prover**. It:
393
+
394
+ 1. **Re-derives the leaf** from the artifact's `contentHash` + `relPath` and **re-folds** the `proof`
395
+ **purely offline**, using the same sorted-pair / domain-separated convention the contract's
396
+ `verifyLeaf` uses (the leaf must equal `pathLeaf(contentHash, relPath)`, then the fold must reach
397
+ `root`). The artifact is an **untrusted transport container** — verify-proof never trusts its
398
+ claims; it re-computes them.
399
+ 2. Makes **one read-only on-chain check** that the root is actually anchored (`isAnchored`) and that
400
+ the contract's own `verifyLeaf` accepts the proof.
401
+
402
+ It prints `ACCEPTED` **only** when the offline fold **and** both on-chain checks pass. A tampered
403
+ `proof`/`leaf`/`contentHash` is caught (offline, no network even needed) and `REJECTED`; an artifact
404
+ whose `root` was never anchored reports `NOT ANCHORED` (a distinct, non-zero exit) rather than a false
405
+ accept. The artifact records its `contractAddress`/`chainId` when built on the on-chain path, so
406
+ verify-proof can run with no `--contract` flag; an explicit `--contract`/`--rpc` always overrides.
407
+
408
+ Before the on-chain leg runs, verify-proof [authenticates the
409
+ registry](#authenticated-reads-registry-identity--chainid) AND cross-checks the artifact's recorded
410
+ `chainId` against the provider's chainId — so it **hard-errors** rather than report a verdict against
411
+ the wrong network (the portability promise made trustworthy: the consumer no longer trusts the prover's
412
+ RPC blindly). `--json` therefore carries a `registry: { id, version, chainId }` block alongside
413
+ `offline.*` / `onChain.*` / `accepted` / `status` / `trustNote`.
414
+
415
+ > **This proves SET-MEMBERSHIP in a root — not authorship, not the `uri`.** An `ACCEPTED` verdict
416
+ > binds the file's path + bytes to an anchored Merkle root. It says nothing about who anchored that
417
+ > root or what `contributor`/`uri` mean — exactly the boundary the contract's `verifyLeaf` draws.
418
+ > `vh verify-proof` leads its output with this caveat verbatim. See
419
+ > [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md).
420
+
421
+ The artifact schema is `{ kind, schemaVersion, root, leaf, contentHash, relPath, proof, contractAddress?, chainId? }`,
422
+ strictly validated on read (a malformed/short hash or a non-hex proof hard-errors), reusing the same
423
+ validation style as the receipt schema in [`docs/RECEIPTS.md`](docs/RECEIPTS.md). The full proof-artifact
424
+ spec — every field (all UNTRUSTED transport, verification re-derives), the offline-fold + on-chain-check
425
+ steps, and a worked prove → hand over → verify-proof example — is in [`docs/PROOFS.md`](docs/PROOFS.md).
426
+
427
+ ### Git-scoped, reproducible anchoring
428
+
429
+ A "code contribution" is a git tree, not whatever files happen to be on disk. By default `vh hash
430
+ <dir>` walks the raw filesystem and hashes **every** regular file it finds — including `.git/`
431
+ internals, untracked files, `node_modules/`, build artifacts, and secrets like `.env`. That makes a
432
+ directory root **non-reproducible** (two clones of the same commit yield different roots because of
433
+ local junk) and is a privacy footgun (it silently hashes secrets).
434
+
435
+ `--git` fixes both. `vh hash/anchor/claim/verify <dir> --git [--ref <ref>]` feeds **EXACTLY the set
436
+ of files git tracks at the chosen commit** (`git ls-tree -r`, `--ref` defaults to `HEAD`) through the
437
+ *same* path-bound, sorted-leaf Merkle machinery — the leaf formula is unchanged; only the file **set**
438
+ differs (see [`docs/MERKLE-LEAVES.md`](docs/MERKLE-LEAVES.md)). Concretely the git-scoped root:
439
+
440
+ - anchors **exactly the files git tracks at that commit** and nothing else — it **never** includes
441
+ `.git/`, untracked files, `.env`/secrets, `node_modules/`, or build output;
442
+ - is **reproducible from a fresh clone**: anyone who checks out the same commit and runs
443
+ `vh verify <dir> --git --ref <commit>` re-derives the identical root and gets `MATCH`, with no
444
+ server, admin, or key to trust (the project's core promise, now true for repos, not just single files);
445
+ - still binds each file's **path** into its leaf, so renaming or moving a tracked file changes the root.
446
+
447
+ ```
448
+ vh hash ./repo --git # root over the files tracked at HEAD (prints the resolved commit oid)
449
+ vh anchor ./repo --git --uri https://… # anchor that reproducible root; records a git provenance hint
450
+ vh verify ./repo --git --ref <commit> # re-derive over the same tracked set and report MATCH / MISMATCH
451
+ ```
452
+
453
+ `--git` requires `<dir>` to be inside a git work tree and errors clearly otherwise (it **never**
454
+ silently falls back to the raw filesystem walk); `--ref` is only meaningful with `--git`. When you
455
+ `anchor`/`claim` with `--git`, the receipt records a `git` block (`{ commit, scope }`) as an
456
+ **UNTRUSTED hint** so a reader can reproduce the enumeration — exactly the trust posture of every other
457
+ receipt field (see [`docs/RECEIPTS.md`](docs/RECEIPTS.md) and [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md)).
458
+ The authoritative verdict is still the recomputed root vs the on-chain record; the `git.commit` is
459
+ never re-checked against the chain.
460
+
461
+ ### Dataset provenance (DataLedger)
462
+
463
+ DataLedger turns an AI training dataset into a **reproducible, tamper-evident manifest** plus the
464
+ diff/summary/proof artifacts a data-provenance reviewer (enterprise due-diligence, EU AI Act technical
465
+ documentation) consumes. Every command below is **offline, needs NO key, and needs NO network** — a
466
+ manifest, diff, summary, or single-file proof can be handed to a third party and re-derived on an
467
+ air-gapped machine with only the `vh` CLI, trusting no server.
468
+
469
+ ```
470
+ vh dataset build <dir> --out <p> # tamper-evident manifest: Merkle root over every (relPath, content) pair + per-file leaves
471
+ vh dataset verify <dir> --manifest <p> # re-derive the root from a fresh copy on disk + per-file ADDED/REMOVED/CHANGED diff
472
+ vh dataset diff <manifestA> <manifestB> # the precise add/remove/change set between two dataset versions (offline, no tree)
473
+ vh dataset summary <manifest> # provenance/license roll-up over the trusted file set (counts CLAIMS, not facts)
474
+ vh dataset check <manifest> --policy <p> [--json] # OFFLINE license/source policy gate: PASS/FAIL + violating files; CI-gateable exit code (0 PASS / 3 FAIL); no key, no network
475
+ vh dataset report <manifest> [--verify <dir>] [--policy <p>] [--json] [--out <p>] # ONE deterministic evidence document the reviewer files; offline, no key, no network
476
+ vh dataset attest <manifest> [--json] [--out <p>] # canonical UNSIGNED attestation payload (root+fileCount+manifestDigest) a human trust-root signs; offline, no key, no network
477
+ vh dataset sign <manifest> --key-env <VAR>|--key-file <p> [--out <p>] [--json] # sign the UNSIGNED attestation with a key YOU provisioned -> the signed container verify-attest accepts; reads YOUR key (never generates/persists/logs one); offline, no network
478
+ vh dataset verify-attest <signed> [--manifest <m>] [--signer <addr>] [--json] # OFFLINE-verify a SIGNED attestation container: recover the signer, pin the publisher, bind YOUR manifest; offline, no key, no network, CI-gateable exit code (0 ACCEPTED / 3 REJECTED)
479
+ vh dataset timestamp-request <manifest> [--out <p>] [--json] # emit the SHA-256 digest your RFC-3161 TSA stamps (P-3 Option B); offline, no key, no network
480
+ vh dataset timestamp-wrap <manifest> --token <p> [--out <p>] [--json] # wrap a TSA's RFC-3161 token -> a verifiable verifyhash.dataset-attestation-timestamped container; offline, no key, no network
481
+ vh dataset verify-timestamp <container> [--manifest <m>] [--json] # OFFLINE-verify a timestamped attestation: re-derive the digest, confirm the RFC-3161 token binds it, bind YOUR manifest; ACCEPTED (genTime/serial/policy) or REJECTED; offline, no key, no network, CI-gateable exit code (0 ACCEPTED / 3 REJECTED)
482
+ vh dataset prove --file <p> --manifest <m> --out <a> # portable set-membership proof for ONE file
483
+ vh dataset verify-proof <proof> # fold a membership proof back to the recorded root (no dataset, no manifest, no key, no net)
484
+ ```
485
+
486
+ `vh dataset check` GATES a manifest against a written license/source policy (allow/deny lists +
487
+ `requireLicense`) and emits PASS/FAIL plus the exact violating files — **offline, no key, no network, with
488
+ a CI-gateable exit code** (0 PASS / 3 FAIL) a pipeline job blocks a build on. `vh dataset report`
489
+ consolidates dataset identity + the provenance/license roll-up + the trust caveats (and, with
490
+ `--verify <dir>`, a live-tree MATCH/MISMATCH verdict; with `--policy <p>`, the SAME policy verdict
491
+ embedded as a "Policy compliance" section) into ONE deterministic document a reviewer files; `vh dataset
492
+ attest` emits the canonical, byte-deterministic **UNSIGNED** payload a human signing/timestamp trust-root
493
+ signs over (`needs-human`, P-3 in [`STRATEGY.md`](STRATEGY.md)); `vh dataset verify-attest` is the
494
+ **offline, no key, no network, CI-gateable exit code** (0 ACCEPTED / 3 REJECTED) VERIFIER a buyer runs on a
495
+ SIGNED attestation container — it recovers the signer, optionally pins the expected publisher (`--signer`)
496
+ and binds the signature to the buyer's own dataset (`--manifest`). All are **offline, need NO key, and need
497
+ NO network**. A PASS attests only that the dataset's UNTRUSTED, self-asserted hints satisfy the policy —
498
+ NOT that the licenses are genuinely correct.
499
+
500
+ This build ships the signed-attestation **FORMAT, the offline VERIFIER, AND the `vh dataset sign` command**
501
+ (all proved with throwaway test keys). `vh dataset sign` reads a key YOU provisioned OUTSIDE the loop
502
+ (`--key-env`/`--key-file`), signs the canonical `vh dataset attest` bytes, and writes the container
503
+ `verify-attest` accepts — it **never generates, persists, or logs a key** and touches no network. So the
504
+ human's remaining P-3 step (`needs-human`, [`STRATEGY.md`](STRATEGY.md)) collapses to: **PROVISION a real
505
+ key, choose the trust-root option, and run `vh dataset sign --key-env <VAR>`**. A verified signature proves
506
+ the key-holder vouched for the dataset identity, NOT a "unaltered since date T" timestamp (still P-3).
507
+
508
+ The Merkle root commits to file **names AND bytes** (the SAME path-bound convention as `vh hash <dir>`),
509
+ so any edit/rename/add/remove changes it. What DataLedger does **NOT** prove: it is **not a timestamp**
510
+ — "unaltered since date T" needs the human-owned signing/timestamp trust-root, a `needs-human` step in
511
+ [`STRATEGY.md`](STRATEGY.md) — and the per-file `{source, license}` **hints are UNTRUSTED self-asserted
512
+ metadata** that are NOT bound into the root (the summary counts what the dataset CLAIMS). Do not
513
+ overclaim. Full buyer-facing spec, worked example, and the auditor / EU-AI-Act evidence mapping:
514
+ [`docs/DATALEDGER.md`](docs/DATALEDGER.md).
515
+
516
+ ### Data-delivery receipts (ProofParcel)
517
+
518
+ ProofParcel is a **second income product on the SAME provenance core**, aimed at a different paying
519
+ buyer: B2B data exchange where a delivery dispute ("you never sent file X" / "the file you sent was
520
+ altered") is expensive. It issues a portable, independently-verifiable **proof-of-delivery receipt** that
521
+ pins exactly which files (names AND bytes) were delivered for a parcel, plus a signable attestation over
522
+ that parcel's identity. Every command is **offline, needs NO key, and needs NO network**.
523
+
524
+ ```
525
+ vh parcel build <dir> --out <p> # tamper-evident delivery receipt: Merkle root + per-file leaves + optional UNTRUSTED parcel meta
526
+ vh parcel verify <dir> --manifest <p> # re-derive the root from a fresh copy on disk + per-file ADDED/REMOVED/CHANGED diff; exit 0 MATCH / 3 MISMATCH
527
+ vh parcel attest <manifest> [--json] [--out <p>] # canonical UNSIGNED parcel-attestation payload (root+fileCount+manifestDigest) a human trust-root signs; offline, no key, no network
528
+ vh parcel sign <manifest> --key-env <VAR>|--key-file <p> [--out <p>] [--json] # sign the UNSIGNED parcel attestation with a key YOU provisioned -> the signed container verify-attest accepts; reads YOUR key (never generates/persists/logs one); offline, no network
529
+ vh parcel verify-attest <signed> [--manifest <m>] [--signer <addr>] [--json] # OFFLINE-verify a SIGNED parcel attestation: recover the signer, pin the sender, bind YOUR parcel; offline, no key, no network, CI-gateable exit code (0 ACCEPTED / 3 REJECTED)
530
+ vh parcel timestamp-request <manifest> [--out <p>] [--json] # emit the SHA-256 digest your RFC-3161 TSA stamps (P-3 Option B); offline, no key, no network
531
+ vh parcel timestamp-wrap <manifest> --token <p> [--out <p>] [--json] # wrap a TSA's RFC-3161 token -> a verifiable verifyhash.parcel-attestation-timestamped container; offline, no key, no network
532
+ vh parcel verify-timestamp <container> [--manifest <m>] [--json] # OFFLINE-verify a timestamped parcel attestation: re-derive the digest, confirm the RFC-3161 token binds it, bind YOUR parcel; ACCEPTED (genTime/serial/policy) or REJECTED; offline, no key, no network, CI-gateable exit code (0 ACCEPTED / 3 REJECTED)
533
+ ```
534
+
535
+ `vh parcel attest` emits the canonical, byte-deterministic **UNSIGNED** payload a sender signs over —
536
+ over the SAME signed-attestation core as `vh dataset attest`, with `signed:false`. `vh parcel
537
+ verify-attest` is the **offline, no key, no network, CI-gateable exit code** (0 ACCEPTED / 3 REJECTED)
538
+ VERIFIER a recipient runs on a SIGNED container: it recovers the signer, optionally pins the expected
539
+ sender (`--signer`) and binds the signature to the recipient's own parcel (`--manifest`). The signed
540
+ container uses ProofParcel's own `verifyhash.parcel-attestation-signed` kind, so a dataset
541
+ signed-container does **not** cross-verify as a parcel one.
542
+
543
+ ProofParcel inherits the **SAME honest trust posture as DataLedger**: the receipt binds the file SET and
544
+ is signable, but is **NOT by itself a trusted delivery TIMESTAMP** — "delivered ON date T" rides the
545
+ human-owned signing/timestamp trust-root (`needs-human`, P-3 in [`STRATEGY.md`](STRATEGY.md)); a valid
546
+ signature proves the key-holder vouched for the parcel identity, NOT a "unaltered since date T"
547
+ timestamp. The `parcel` metadata (parcelId/sender/recipient) is **UNTRUSTED self-asserted metadata** that
548
+ is NOT bound into the root. This build ships the **FORMAT, the offline VERIFIER, AND the `vh parcel sign`
549
+ command** (proved with throwaway test keys); `vh parcel sign` reads a key the sender PROVISIONED outside the
550
+ loop (never generates/persists/logs one), so the human's P-3 step collapses to "provision a key, run
551
+ `vh parcel sign --key-env <VAR>`." Full buyer-facing spec, command table, and worked example:
552
+ [`docs/PROOFPARCEL.md`](docs/PROOFPARCEL.md).
553
+
554
+ ### Evidence packets (`vh evidence`)
555
+
556
+ A **product-agnostic, license-gated, tamper-evident evidence packet** for any directory of files — the
557
+ **second vertical** built on the SAME provenance core, with its OWN sellable license. `vh evidence seal`
558
+ binds a whole folder into one content-addressed `*.vhevidence.json`; `vh evidence verify` re-derives the
559
+ root from the bytes you hold and localizes any tamper to the exact file. Both are **offline, no network**.
560
+
561
+ ```
562
+ vh evidence seal <dir> [--out <p>] [--license <f> --vendor <0xaddr>] [--sign --key-env <VAR>|--key-file <p>] [--json] # build the packet over cli/core/packetseal.js (generic kind, NO trust-reconcile vocabulary); default PRINTS the seal + writes NOTHING; exit 0 ok / 3 seal-build-error / 2 usage / 1 IO
563
+ vh evidence verify <p> [--dir <d>] [--json] # read-only, NO key: RE-DERIVE the root from the bytes referenced + report OK / which file CHANGED/MISSING/UNEXPECTED; exit 0 OK / 3 REJECTED / 2 usage / 1 IO
564
+ ```
565
+
566
+ The seal proves **TAMPER-EVIDENCE + OFFLINE-RECOMPUTE, NOT a trusted timestamp** ("sealed at T" rides the
567
+ human trust-root, P-3); the packet is an **UNTRUSTED transport container** verify never trusts. The **FREE**
568
+ tier — an unsigned baseline seal of up to **25 files** + verify — stays open so a buyer can try before
569
+ buying. The **PAID** surface (the `--sign` signed-attestation wrap; sealing **> 25 files**) is gated behind
570
+ a valid `--license <f> --vendor <0xaddr>` verified OFFLINE via `cli/core/license.js` against a **distinct
571
+ evidence-product entitlement table** (`kind: vh-evidence-license`, **not** `trustledger-license`), reusing
572
+ the SAME `verifyLicense` / named-reject posture as the TrustLedger CLI (a wrong/expired/under-entitled
573
+ license is a hard refuse, never a silent downgrade). Full schema, free-vs-paid surface, a worked
574
+ seal → hand over → verify example, and the core-reuse map: [`docs/EVIDENCE.md`](docs/EVIDENCE.md). The
575
+ vendor keypair, price, and first design partner are human steps (STRATEGY.md › **P-7**).
576
+
577
+ ### Agent-session evidence (`vh agent`) — AgentTrace
578
+
579
+ **Tamper-evident, selectively-REDACTABLE, independently-verifiable AI-agent session records** — the
580
+ agent-evidence vertical for the record-keeping / audit / incident-forensics buyer. `vh agent seal` turns
581
+ an ordered session event log (prompts, completions, tool calls/results, notes) into ONE
582
+ `*.vhagent.json` packet under an RFC-6962-style ordered Merkle head with **redaction-safe** leaves:
583
+ withhold any payload behind its hash commitment and the packet **still verifies with the identical
584
+ head**; disclose + check any ONE event offline; prove a later packet extends a mid-session checkpoint
585
+ **append-only** (a rewritten past is a REJECT naming the seq). All **offline, no network**.
586
+
587
+ ```
588
+ vh agent seal <session.jsonl> [--out <p>] [--sign --license <f> --vendor <0xaddr> ...] [--json] # free unsigned; --sign is the PAID surface
589
+ vh agent verify <packet> [--vendor <0xaddr>] [--json] # re-derive every leaf + the root; exit 0 ACCEPTED / 3 named REJECT (+ seq)
590
+ vh agent redact <packet> --seq <list> --out <p> # withhold payloads; head UNCHANGED — redaction is not tamper
591
+ vh agent prove / verify-proof / checkpoint / verify-growth # single-event disclosure + append-only growth, offline
592
+ ```
593
+
594
+ The honest boundary, carried **in-band** in every packet's trust note: it proves the LOG is unaltered
595
+ since seal, any disclosed event verbatim as recorded, append-only growth across checkpoints, and that
596
+ redaction can only withhold — **NOT** that the log faithfully records what the agent actually did
597
+ (garbage-in is out of scope), `ts` fields are self-asserted, and it is **NOT** a trusted timestamp
598
+ without the human-owned trust-root (P-3). Verify/redact/prove are FREE; `--sign` (a detached head
599
+ attestation one signature of which stays valid across every redacted copy) is license-gated via the
600
+ DRAFT `agent_signed` capability — the same fail-closed mechanism as `vh evidence`. A counterparty
601
+ verifies with the independent `verify-vh` (CLI, zero-install bundle, or the offline browser page —
602
+ [`verifier/README.md`](verifier/README.md) §2c). Buyer-facing spec:
603
+ [`docs/AGENTTRACE.md`](docs/AGENTTRACE.md); a committed third-party-transcript example proving adoption
604
+ is a ~20-line mapping: [`examples/agent-session/`](examples/agent-session/).
605
+
606
+ **Commit-bound sessions** — `vh agent commit-claim` emits ONE ordinary claim event binding the session
607
+ to exactly one git commit (the oid + the `vh hash --git` tracked-set root), sealed under the same head;
608
+ `vh agent verify-commit <packet> --repo <dir>` is the auditor leg: full packet verification FIRST, then
609
+ BOTH facts re-derived from the auditor's OWN clone, with every REJECT named (`packet-invalid` /
610
+ `no-disclosed-claim` / `oid-mismatch` / `root-mismatch`). Both FREE, read-only, key-less; redact every
611
+ other payload and the claim stays checkable. **Containment, NOT causation** — it does NOT prove the
612
+ session's events produced the commit. Spec + honest boundary: [`docs/AGENTTRACE.md`](docs/AGENTTRACE.md)
613
+ › *Binding a session to a git commit*; the scripted flow (map → commit-claim → seal →
614
+ redact-all-but-claim → verify-commit):
615
+ [`examples/agent-session/commit-bound-session.js`](examples/agent-session/commit-bound-session.js).
616
+
617
+ ### Producer identity card (`vh identity`)
618
+
619
+ A **signed, offline-verifiable "who is this vendor, and what exactly do they attest?"** card — the
620
+ recipient's / **cold prospect's** pin-point. Every other artifact pins its producer by a vendor **address**
621
+ the recipient must learn out of band; the identity card is the **one** artifact whose job is to answer,
622
+ verifiably, "**does this 0x-address really belong to THIS vendor, and what do they attest / NOT?**" The
623
+ vendor SIGNS — with the **same key** that signs their evidence/licenses — a card binding their
624
+ `vendorAddress` to a bounded `claims[]` set + an honest `nonClaims[]` set.
625
+
626
+ ```
627
+ vh identity publish --address <0xaddr> --product-line <line> --claim <text> --non-claim <text> (--key-env <VAR>|--key-file <p>) [--out <p>] [--json] # mint the card; signs with a key YOU provisioned, mints ONLY when the key controls --address; default PRINTS + writes NOTHING; offline
628
+ vh identity verify <card> [--signer <0xaddr>] [--json] # OFFLINE/key-free: recover the signer, require it to BE the card's vendorAddress, optionally pin --signer; exit 0 ACCEPTED / 3 REJECTED / 2 usage / 1 IO
629
+ ```
630
+
631
+ **Pin once, trust across handoffs.** The recipient does the address-to-vendor trust step **ONCE**
632
+ (`vh identity verify vendor.vhidentity.json --signer <addr-you-were-given>` → ACCEPTED), then reuses that
633
+ pinned `vendorAddress` across every later signed handoff (`vh evidence verify-signed <p> --signer <addr>`,
634
+ etc.) — no new out-of-band step. A **forged / tampered / wrong-vendor card, or a wrong `--signer`, is a
635
+ clean REJECTED — never a silent pass**. The card proves **IDENTITY + the claim SET only**: **NOT** any
636
+ specific packet's truth (each packet carries its own proof), **NOT** a trusted timestamp (P-3), and
637
+ **NOT** a legal opinion. Full flow + the pin-once-trust-across-handoffs model:
638
+ [`docs/IDENTITY.md`](docs/IDENTITY.md). Publishing the card uses the same vendor key provisioned for signed
639
+ evidence/licenses (STRATEGY.md › **P-7 step 1** / **P-6 step 1**).
640
+
641
+ ### Revoking a key (`vh revocation`)
642
+
643
+ A key has a lifecycle: it is generated, **published** (the identity card), pinned, used to sign — and
644
+ eventually **rotated, retired, or compromised**. `vh revocation` is how a vendor honestly **retires** that
645
+ pinned key: they SIGN — with the **same key** — a self-describing revocation marking their own
646
+ `vendorAddress` revoked as of a date for a bounded reason (and optionally a `supersededBy` successor).
647
+
648
+ ```
649
+ vh revocation publish --address <0xaddr> --reason <reason> (--key-env <VAR>|--key-file <p>) [--superseded-by <0xaddr>] [--revoked-at <ISO>] [--out <p>] [--json] # mint the revocation; signs with a key YOU provisioned, mints ONLY when the key controls --address (a key revokes ITSELF); default PRINTS + writes NOTHING; offline
650
+ vh revocation verify <revocation> [--signer <0xaddr>] [--json] # OFFLINE/key-free: recover the signer, require it to BE the revocation's vendorAddress, optionally pin --signer; exit 0 ACCEPTED / 3 REJECTED / 2 usage / 1 IO
651
+ ```
652
+
653
+ **A key revokes ITSELF.** `verify` recovers the signer and REQUIRES it to equal the revocation's own
654
+ `vendorAddress` — a **third party cannot revoke a key it does not control**, so a planted "revocation" can
655
+ never grief a vendor (it is a clean **REJECTED**, never a silent pass). Recipients pin the revocation next to
656
+ the identity card and pass it to any signed-verify command via **`--revocations <f>`** `[--as-of <ISO>]`: an
657
+ exhibit signed under a key that was **revoked-before-as-of** downgrades from ACCEPTED to **REVOKED**, while an
658
+ exhibit signed while the key was still good keeps its ACCEPTED verdict. It is **strictly optional and
659
+ non-loosening** (with no `--revocations`, every verify command is byte-for-byte what it is today). The
660
+ boundary: a revocation is a **signed CLAIM** by the key-holder (`revokedAt` is self-asserted), **NOT a
661
+ trusted timestamp** without P-3, and **NOT a legal opinion**. Full publish → pin → verify lifecycle:
662
+ [`docs/KEY-LIFECYCLE.md`](docs/KEY-LIFECYCLE.md).
663
+
664
+ ### The 60-second cold-prospect challenge (zero-install, zero-trust)
665
+
666
+ The fastest way for someone who owes us **nothing** — no account, no `npm install`, no repo build, no
667
+ key, no network — to believe a verifyhash seal is to *test* it on their own machine in under a minute.
668
+ [`challenge/`](challenge/) is that entry point: a real pre-sealed sample packet + its seal + a one-command
669
+ `run.sh`. They VERIFY it (exit 0), TAMPER one byte, and watch the committed standalone verifier REJECT it
670
+ (exit 3) and **name the file they changed** — trusting no server, no producer software, and not us. The
671
+ challenge is the **FREE, UNSIGNED** verify end and points free-verify → free-produce → **paid-produce**
672
+ (signing + unlimited sealing). The boundary is the same one stated everywhere: the seal proves
673
+ tamper-evidence + signer-pin, **NOT** a trusted "sealed at T" (that still requires P-3), and the unsigned
674
+ sample has no signer to pin. Walkthrough: [`challenge/README.md`](challenge/README.md).
675
+
676
+ ### The independent verifier (`verify-vh`) — a buyer deliverable
677
+
678
+ A counterparty who is **not** a customer can check any sealed artifact themselves, **offline and for
679
+ free**, without our `ethers`/`hardhat` stack: the standalone [`verifier/`](verifier/) tree
680
+ (`verify-vh`) re-derives the keccak Merkle root from the bytes they hold, recovers the EIP-191
681
+ secp256k1 signer, and pins it to a `--vendor` address they supply out-of-band — near-zero dependencies
682
+ (`js-sha3` only), no network, read-only. Its independence is **mechanically proven**, not just
683
+ promised: [`test/verifier.isolation.test.js`](test/verifier.isolation.test.js) statically greps every
684
+ `require(` in the tree (never `ethers`/`hardhat`/`@nomicfoundation/*`/`cli/`/`trustledger/`) and runs a
685
+ real verify under a poisoned network to assert **no socket is opened**. The trust boundary is honest:
686
+ tamper-evidence + offline-recompute + signer-pin, **NOT** a trusted "sealed at T" (that rides the
687
+ human trust-root, P-3) and **NOT** a legal opinion. Counterparty quickstart + worked
688
+ `seal → hand over → verify-vh` example: [`verifier/README.md`](verifier/README.md); full spec:
689
+ [`docs/INDEPENDENT-VERIFICATION.md`](docs/INDEPENDENT-VERIFICATION.md).
690
+
691
+ ### Run the design-partner pilot in one command (offline, no key, no network)
692
+
693
+ The single artifact you hand a prospective design partner is the **pilot kit**. It drives **both**
694
+ sellable buyer journeys — the **evidence packet** and the **TrustLedger reconciliation seal** — end to
695
+ end against committed sample data, **offline, with no real key, no TSA, no RPC, no network**, and
696
+ prints **one combined PASS/FAIL verdict**:
697
+
698
+ ```bash
699
+ node pilot/run-pilot.js
700
+ ```
701
+
702
+ It issues an ephemeral-key licence, proves the paid surface is **really** licence-gated (refused with
703
+ no licence and with the wrong vendor), hands the artifact to the **independent** `verify-vh` (ACCEPT),
704
+ then **tampers** to prove REJECT — writing only to a throwaway workspace, never the repo. The trust
705
+ boundary is honest: tamper-evidence + signer-pin, **NOT** a trusted "sealed at T" (that rides the
706
+ human-owned trust-root, P-3). The buyer-facing runbook — what each artifact proves, where a partner
707
+ independently verifies it, and the consolidated go-to-market ask (**P-8**) — is
708
+ [`docs/PILOT.md`](docs/PILOT.md); the operator quick reference is [`pilot/README.md`](pilot/README.md).
709
+ The journey and the runbook are both test-gated, so they can never silently rot.
710
+
711
+ ### Embed it: the programmatic API (SDK)
712
+
713
+ Everything the CLI does is available as a library. `require("verifyhash")` (or `require("../index.js")`
714
+ from a checkout) exposes a single, documented, semver-guarded entrypoint — a **thin re-export** of the
715
+ exact same functions `vh` runs (no fork, no second implementation, no new crypto). The embedded seal
716
+ path is byte-identical to `vh evidence seal` / `vh evidence verify`: build a seal, verify it (ACCEPT),
717
+ and a one-byte tamper is REJECTED.
718
+
719
+ ```js
720
+ const vh = require("verifyhash"); // from a checkout: require("./index.js")
721
+
722
+ // Build a tamper-evident seal over an in-memory { relPath, bytes } file set.
723
+ const entries = [
724
+ { relPath: "data/a.txt", bytes: Buffer.from("alpha\n") },
725
+ { relPath: "data/b.txt", bytes: Buffer.from("bravo\n") },
726
+ ];
727
+ const seal = vh.buildSeal(entries);
728
+
729
+ // Verify the SAME bytes: ACCEPTED (root re-derived from the bytes you hold, not the seal's own hashes).
730
+ console.log(vh.verifySeal(seal, entries).verdict); // "ACCEPTED"
731
+
732
+ // Tamper one byte and re-verify: REJECTED.
733
+ const tampered = [entries[0], { relPath: "data/b.txt", bytes: Buffer.from("bravX\n") }];
734
+ console.log(vh.verifySeal(seal, tampered).verdict); // "REJECTED"
735
+
736
+ // Serialize to canonical, byte-deterministic JSON you can hand to a counterparty:
737
+ const json = vh.serializeSeal(seal);
738
+ console.log(vh.readSeal(json).root === seal.root); // true
739
+ ```
740
+
741
+ The surface also re-exports the **signed / vendor-pinned verify path** (the `signed` namespace:
742
+ `signSealWith`, `validateSignedSeal`, `verifySignedSeal`, `verifySignedSealAttestation`, `recoverSigner`,
743
+ `verifySignedAttestation`) — the embedded twin of `vh evidence verify-signed`. So an embedder can verify a
744
+ **signed, address-pinned** seal in-process (ACCEPT under the matching `--signer`; REJECT under a wrong
745
+ expected signer or a one-byte-tampered container) with **no shell-out** to the `vh` binary. A valid
746
+ signature proves *who* vouched, not *when* (a trusted timestamp still rides the human-owned trust-root,
747
+ STRATEGY.md P-3). It also re-exports the receipt codec (`buildReceipt`, `readReceipt`, `diffManifest`, …),
748
+ the keccak/Merkle hashing primitives (`hashBytes`, `hashEntries`, `buildTree`, …), and `apiVersion` (the
749
+ semver-guarded stability marker, mirroring `package.json`). Symbols **not** re-exported from the
750
+ package root (deep `cli/…` internals) carry no stability guarantee. This example is test-gated
751
+ ([`test/sdk.index.test.js`](test/sdk.index.test.js)); the signed path is gated by
752
+ [`test/sdk.signed.test.js`](test/sdk.signed.test.js), so it can never silently rot.
753
+
754
+ ### Verify continuously over time (`vh journal`)
755
+
756
+ Every verify above answers *"do these bytes match RIGHT NOW?"* and exits. The **integrity journal** proves an
757
+ artifact has verified **continuously across runs**: an **append-only, hash-chained** log of verify verdicts
758
+ that is **itself tamper-evident** — a deleted / edited / reordered / inserted past entry **breaks the chain**
759
+ and `vh journal verify` **localizes the first break**.
760
+
761
+ ```bash
762
+ vh journal append <artifact> --to <journalfile> [--dir <d>] [--ts <ISO>] # record ONE verdict (strictly additive)
763
+ vh journal verify <journalfile> # exit 0 PASS (unbroken + every observation ACCEPTED) / 3 broken-or-drifted / 2 usage / 1 IO
764
+ ```
765
+
766
+ Each CI run appends this build's verdict, then verifies the whole chain — a green pipeline MEANS "the artifact
767
+ has verified continuously across every recorded run, and the record itself is tamper-evident." It reuses the
768
+ **same** keccak hash-chain the project already trusts for seals (no new crypto). The `ts` on each entry is
769
+ **self-asserted** (the verifier's own wall clock), **NOT a trusted timestamp** — the journal never claims
770
+ *"unaltered since date T"* on its own; that claim rides the human-owned trust-root (STRATEGY.md **P-3**).
771
+ Schema, chain guarantee, the 0/3 contract, and the honesty boundary:
772
+ [`docs/INTEGRITY-JOURNAL.md`](docs/INTEGRITY-JOURNAL.md); drop-in CI recipes in
773
+ [`examples/journal-ci.js`](examples/journal-ci.js) and
774
+ [`verifier/ci/journal.generic.sh`](verifier/ci/journal.generic.sh).
775
+
776
+ The journal also carries **transparency-log proofs** (RFC-6962 / Certificate-Transparency lineage):
777
+ `vh journal tree-head` publishes a single `{ size, root }` head committing to the whole ordered log,
778
+ `prove-inclusion` / `prove-consistency` emit compact self-contained proof artifacts, and
779
+ `vh journal check-proof` lets a third-party auditor verify them **offline** — no journal, no key, no
780
+ network (though today via the **producer package**, which installs ethers — **not** the standalone,
781
+ zero-dependency `verifier/` bundle a seal enjoys). The head is **self-asserted** until a P-3 trust-root
782
+ signs it. See
783
+ [`docs/INTEGRITY-JOURNAL.md`](docs/INTEGRITY-JOURNAL.md) § "Transparency-log proofs (publish a tree head;
784
+ auditors verify offline)".
785
+
786
+ ## Develop
787
+
788
+ ```
789
+ npm install
790
+ npx hardhat compile
791
+ npx hardhat test
792
+ ```
793
+
794
+ Local hardhat / in-memory EVM only. Deployment to any real network is a human checkpoint
795
+ (see `BACKLOG.md`, EPIC-4); never run automatically.
796
+
797
+ ## Docs
798
+
799
+ - [`docs/DECIDE.md`](docs/DECIDE.md) — **the one human decision, on one screen.** The single recommended
800
+ pilot (EVIDENCE / P-7), the 3 verbatim first actions, the exact `node pilot/run-pilot.js --certificate
801
+ <path>` command, the time box / stop criterion, and the revenue-integrity boundary. Read this first; it
802
+ adds no new ask beyond P-8.
803
+ - [`docs/DATALEDGER.md`](docs/DATALEDGER.md) — the buyer-facing DataLedger product spec: what a dataset
804
+ manifest PROVES (file names + bytes, offline set-membership, version diff, license roll-up) and does
805
+ NOT (not a timestamp; untrusted source/license hints), the build→diff→summary→prove→verify-proof
806
+ workflow with a worked example, and the auditor / EU-AI-Act evidence mapping.
807
+ - [`docs/PROOFPARCEL.md`](docs/PROOFPARCEL.md) — the buyer-facing ProofParcel product spec (B2B
808
+ proof-of-delivery): the command table (build/verify/attest/verify-attest with the offline, no key, no
809
+ network, CI-gateable exit 0/3 property), a worked sender → [signs, P-3] → recipient verify-attests
810
+ example, and the SAME honest trust posture as DataLedger (binds the file SET, signable, NOT a delivery
811
+ timestamp; parcel metadata is untrusted self-asserted).
812
+ - [`docs/EVIDENCE.md`](docs/EVIDENCE.md) — the buyer-facing Evidence-packet product spec (the second
813
+ vertical, with its OWN license): the `*.vhevidence.json` schema (every field UNTRUSTED transport —
814
+ verification re-derives), the free-vs-paid surface (free unsigned baseline of up to 25 files + verify;
815
+ paid `--sign` wrap and over-sample sealing gated by `vh-evidence-license` entitlements), a worked
816
+ seal → hand over → verify example, and how it reuses the shared packetseal/license/attestation cores.
817
+ - [`docs/AGENTTRACE.md`](docs/AGENTTRACE.md) — the buyer-facing AgentTrace product spec (`vh agent`,
818
+ `*.vhagent.json`): what a packet PROVES (log unaltered since seal; disclosed events verbatim;
819
+ append-only growth across checkpoints; redaction withholds — never silently alters) and does NOT
820
+ (garbage-in out of scope; `ts` self-asserted; not a trusted timestamp without P-3), the free-vs-paid
821
+ line (`--sign` gated by the DRAFT `agent_signed` capability), where a counterparty independently
822
+ verifies, the commit-binding leg (`commit-claim` / `verify-commit` — containment, not causation),
823
+ and the committed [`examples/agent-session/`](examples/agent-session/) worked example
824
+ (a third-party OpenAI-style transcript mapped in ~20 lines, then map → seal → redact → verify → prove).
825
+ - [`docs/ANCHORING.md`](docs/ANCHORING.md) — anchoring ANY sealed artifact on-chain (`vh anchor-artifact` /
826
+ `vh verify-anchored`): the closed six-kind digest table, the `vh-anchored-receipt@1` container, what an
827
+ anchored receipt PROVES (an on-chain registry record binds this exact digest at a block whose timestamp
828
+ BOUNDS existence — as trustworthy as the chain + YOUR pinned contract address; `--author-bound` =
829
+ front-run-resistant first-claimant attribution per D-1) and does NOT (a receipt from a LOCAL dev chain
830
+ proves MECHANISM only — worth nothing publicly until a human deploys per STRATEGY.md P-2; not the
831
+ artifact's truth; not attribution beyond the anchoring key; not legal advice), the free line (both verbs
832
+ FREE; gas is your own), and the worked local flow with committed offline fixtures
833
+ ([`examples/anchoring/`](examples/anchoring/)).
834
+ - [`docs/KEY-LIFECYCLE.md`](docs/KEY-LIFECYCLE.md) — the producer-key lifecycle: publish → pin → verify, and
835
+ how a vendor honestly RETIRES a pinned key with a signed `vh revocation publish|verify` (a key revokes
836
+ ITSELF; a third party cannot revoke a key it does not control). The recipient `--revocations <f>`
837
+ `[--as-of <ISO>]` downgrade rule (revoked-before-as-of → REVOKED; strictly optional + non-loosening), and
838
+ the verbatim boundary — a revocation is a SIGNED CLAIM, NOT a trusted wall-clock timestamp without P-3.
839
+ - [`docs/INDEPENDENT-VERIFICATION.md`](docs/INDEPENDENT-VERIFICATION.md) — the counterparty-facing spec
840
+ for the standalone `verify-vh` verifier: the exact bytes verified (keccak content hash → Merkle root →
841
+ EIP-191 personal-sign signature), the FREE/no-network/no-back-edge posture, the trust boundary
842
+ (tamper-evidence + offline-recompute + signer-pin, NOT a trusted "sealed at T"), a worked
843
+ producer-seals → counterparty-runs-`verify-vh` example, and how isolation is proven mechanically.
844
+ - [`docs/TRUST-BOUNDARIES.md`](docs/TRUST-BOUNDARIES.md) — what each record field proves and does not,
845
+ plus "Authenticating the registry you read from" (why read commands authenticate the registry before
846
+ believing it, and why the `REGISTRY_ID` is a "right interface" signal, not a sole root of trust).
847
+ - [`docs/MERKLE-LEAVES.md`](docs/MERKLE-LEAVES.md) — what a directory root commits to (paths + bytes),
848
+ including the `--git` scope note (same leaf formula, reproducible git-tracked file set).
849
+ - [`docs/PROOFS.md`](docs/PROOFS.md) — the portable proof-artifact schema (every field UNTRUSTED
850
+ transport), the offline-fold + on-chain-check verification steps, and a worked
851
+ prove → hand over → `vh verify-proof` example (read-only, no key, no repo needed).
852
+ - [`docs/RECEIPTS.md`](docs/RECEIPTS.md) — the receipt JSON schema (trusted vs hints), the
853
+ commit→reveal resume lifecycle, and the directory-manifest diff semantics.
854
+ - [`docs/LINEAGE.md`](docs/LINEAGE.md) — the contribution lineage graph: the immutable `parent` edge
855
+ (acyclic-by-construction, O(1), a CLAIM that proves no ancestry/authorship), the `Linked` log an
856
+ indexer reconstructs the graph from, the `--parent` write flow, and the `vh lineage`/`vh show` read
857
+ flow with a worked anchor-root → anchor-revision → walk-lineage example.
858
+ - [`docs/REPUTATION.md`](docs/REPUTATION.md) — the contribution score: its exact definition (the single
859
+ `getRecordsByContributor` walk it aggregates, with `contributorRecordCount` the companion O(1) count,
860
+ the authorBound/anchor-only
861
+ and root/revision breakdowns, the block/time bounds), why it is a non-transferable derived view (NOT a
862
+ token — any tradeable layer is D-2/P-1, human-only), what it does NOT prove, and the anti-sybil note
863
+ (the meaningful signal is the authorBound count).
864
+ - [`docs/TRUSTLEDGER.md`](docs/TRUSTLEDGER.md) — the buyer-facing TrustLedger product spec (automated
865
+ three-way trust-account reconciliation for small residential property managers): the three balances
866
+ that must legally agree, the PASS/FAIL exit contract, the per-state policy + period-close-continuity
867
+ layers, the tamper-evident packet seal, and the **web front-door** (`vh trust serve`) — including the
868
+ **in-browser inspect/map onboarding flow** (drop a file → if it won't load, see its columns and map
869
+ them from a dropdown → reconcile), so a non-technical broker never needs a terminal. It also covers
870
+ the **Entitlements & licensing** model: the signed, offline-verifiable `*.vhlicense.json`, the
871
+ free-vs-paid surface, the closed entitlement table, and how a customer verifies a license offline
872
+ against the pinned vendor address (CLI **and** web honour the same gate). Hosting, the CPA review, the
873
+ per-state policy table, license issuance, and pricing all stay human steps (STRATEGY.md › P-5/P-6).
874
+ - [`docs/PILOT.md`](docs/PILOT.md) — the buyer-facing pilot runbook: how a non-author runs
875
+ [`pilot/run-pilot.js`](pilot/run-pilot.js), what each artifact (evidence packet, reconciliation seal,
876
+ licence) PROVES and where a partner independently verifies it with `verify-vh`, the honest trust
877
+ boundary (tamper-evidence + signer-pin, **no trusted "sealed at T" without P-3**), and the
878
+ consolidated go-to-market ask (**P-8**). Operator quick reference: [`pilot/README.md`](pilot/README.md).
879
+ - [`docs/AUDIT.md`](docs/AUDIT.md) — security audit findings and the fix tasks they spawned.
880
+
881
+
882
+ ---
883
+ <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>