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
@@ -0,0 +1,307 @@
1
+ # verifyhash contribution lineage — the `parent` edge, the graph, and how to walk it
2
+
3
+ This is the canonical spec for the **contribution lineage graph**: the optional, immutable `parent`
4
+ edge every record may carry (task **T-10.1**), how a record names a predecessor (`vh anchor/claim
5
+ --parent`), and how anyone reads the graph back (`vh lineage` / `vh show`, task **T-10.2**). It is
6
+ pure documentation of behaviour that already ships; no new runtime behaviour is introduced here.
7
+
8
+ A contribution is not a flat island — v2 fixes v1, a fork derives from an upstream, a patch builds on
9
+ a base. Before lineage, the registry could not express the single most basic relationship a
10
+ contribution has: "this came after / revises / builds on that." The `parent` edge turns the registry
11
+ from a pile of unrelated hashes into a contribution **history you can walk and audit**.
12
+
13
+ > **Trust posture (read this first).** A `parent` edge is the **child author's CLAIM** of a
14
+ > predecessor, in exactly the spirit of [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md). It does
15
+ > **NOT** prove the predecessor's content is a genuine ancestor of the child's content (re-derive
16
+ > **both** contents yourself and reason about the relationship), and it does **NOT** transfer the
17
+ > parent's authorship to the child — each record's `contributor`/`authorBound` stands on its own.
18
+ > See [What a `parent` edge does and does NOT prove](#what-a-parent-edge-does-and-does-not-prove)
19
+ > below; that section reuses the TRUST-BOUNDARIES wording verbatim so the caveats stay consistent.
20
+
21
+ ---
22
+
23
+ ## The on-chain `parent` edge
24
+
25
+ Every `Record` carries one optional, immutable `parent` field (`contracts/ContributionRegistry.sol`):
26
+
27
+ ```solidity
28
+ struct Record {
29
+ address contributor; // who is recorded — meaning depends on authorBound
30
+ bool authorBound; // true => commit-reveal (proven first claimant); false => first anchorer
31
+ uint64 timestamp; // block.timestamp at anchor time
32
+ uint64 blockNumber; // block.number at anchor time
33
+ string uri; // off-chain pointer hint (UNTRUSTED)
34
+ bytes32 parent; // OPTIONAL predecessor edge; bytes32(0) == "no predecessor / lineage root"
35
+ }
36
+ ```
37
+
38
+ The edge has exactly these properties:
39
+
40
+ - **Optional.** `parent == bytes32(0)` (the 32-byte zero hash) means "no predecessor": this record is
41
+ a **lineage root**. Every legacy `anchor`/`reveal` record is a root by construction (it never names a
42
+ parent).
43
+ - **Acyclic by construction (a DAG).** A non-zero `parent` is **REQUIRED to already be anchored** at
44
+ the moment the child is written, else the write reverts. A new record cannot be named as anyone's
45
+ parent until after it is itself written, so an edge can only ever point at an *earlier* (lower-index)
46
+ record — no forward edge and no cycle can form. There is no on-chain cycle check; acyclicity falls
47
+ out of the "parent must pre-exist" rule alone.
48
+ - **O(1) per write, no on-chain walk.** The parent check is a single `_records[parent]` existence read
49
+ — **no loop, no walk of the ancestry chain** — so it preserves the contract's hard "no unbounded
50
+ loop / no gas-DoS" invariant. The chain is walked **off-chain** (see [Reading the graph](#reading-the-graph-vh-lineage--vh-show)).
51
+ - **Immutable.** Like every other field, once written the `parent` can never change. First-writer-wins
52
+ still applies to the child's own `contentHash`.
53
+ - **Self-reference rejected.** `parent == contentHash` reverts `SelfParent(contentHash)` (a self-loop);
54
+ this is a distinct error from naming a not-yet-anchored hash.
55
+
56
+ ### What a `parent` edge does and does NOT prove
57
+
58
+ This reuses the `parent` clause of the contract-level `TRUST BOUNDARIES` NatSpec and
59
+ [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md) verbatim so the caveats stay consistent:
60
+
61
+ > `parent` is an OPTIONAL, immutable predecessor edge (bytes32(0) == "no predecessor / root of a
62
+ > lineage"). It asserts ONLY that the author of THIS record CLAIMED the named predecessor. It does
63
+ > NOT prove the predecessor's content is genuinely an ancestor of this content — consumers must still
64
+ > independently re-derive BOTH contents and judge the relationship themselves — and it does NOT
65
+ > transfer or imply the predecessor's authorship/attribution to this record (each record's
66
+ > `contributor`/`authorBound` stand alone).
67
+
68
+ Concretely, a `parent` edge **proves**:
69
+
70
+ - the named predecessor `contentHash` **was anchored on-chain before** this child (acyclic-by-
71
+ construction — the contract enforced it at write time), and
72
+ - the child's author **chose to point at it** as a predecessor.
73
+
74
+ It does **NOT** prove:
75
+
76
+ - **content ancestry** — that the predecessor's bytes are genuinely an earlier version of, or were
77
+ derived into, the child's bytes. Anyone can name any already-anchored hash as a parent. To reason
78
+ about a real derivation you must independently obtain and **re-derive both** contents (`vh hash`)
79
+ and compare them yourself.
80
+ - **authorship transfer** — naming a parent grants the child nothing from it. The parent's
81
+ `contributor`/`authorBound` say nothing about the child's, and vice versa. Attribution is per-record.
82
+
83
+ > Rule of thumb: **a `parent` edge is a claim of "I built on that", not a proof of "that became this".**
84
+
85
+ ---
86
+
87
+ ## The log shape an indexer reconstructs the graph from
88
+
89
+ The graph's full edge set is reconstructable **purely from logs**, so an off-chain indexer never needs
90
+ to read storage to build it:
91
+
92
+ ```solidity
93
+ event Linked(bytes32 indexed child, bytes32 indexed parent);
94
+ ```
95
+
96
+ - A `Linked(child, parent)` log is emitted **in addition to** `Anchored`/`Revealed` **iff** the record
97
+ was written with a **non-zero** `parent`.
98
+ - A record written with **no** predecessor (`parent == 0x0`, including **every** legacy
99
+ `anchor`/`reveal` call) emits **no** `Linked` log. The **absence** of a `Linked` log for a child is
100
+ exactly "this record is a lineage root."
101
+ - `Linked` is a **parallel** event: the legacy `Anchored`/`Revealed` signatures are left byte-for-byte
102
+ unchanged, so pre-lineage indexers keep working without modification.
103
+ - **Both** `child` and `parent` are `indexed`, so an indexer can query "all edges **into** a node"
104
+ (filter on the `parent` topic) or "all edges **out of** a node" (filter on the `child` topic)
105
+ directly by topic — and assemble the whole DAG from the union of `Anchored`/`Revealed` (the nodes)
106
+ and `Linked` (the edges).
107
+
108
+ `Linked` carries the same trust boundary as the `Record.parent` field: it records only that the author
109
+ of `child` CLAIMED `parent` as a predecessor; it proves neither content ancestry nor an authorship
110
+ transfer.
111
+
112
+ ---
113
+
114
+ ## Writing an edge — `vh anchor/claim --parent <hash>`
115
+
116
+ A revision is written by pointing a new record at an already-anchored predecessor with `--parent`:
117
+
118
+ ```
119
+ vh anchor <path> --parent <0xhash> [--uri u] [--git] # one-shot revision (FRONT-RUNNABLE attribution)
120
+ vh claim <path> --parent <0xhash> [--uri u] [--git] # commit-reveal revision (authorBound = true)
121
+ ```
122
+
123
+ - `--parent <0xhash>` takes a 32-byte (`0x` + 64 hex) content hash that **must already be anchored**.
124
+ Its shape is validated **before any network call** (a malformed/short hash, or a `parent` equal to
125
+ the child's own hash, is a usage error that fails locally); the contract then enforces existence and
126
+ self-reference at write time.
127
+ - On `vh anchor`, a non-zero `--parent` routes the write to `anchorWithParent(contentHash, uri,
128
+ parent)` instead of `anchor(...)`. Omitting it (or passing the zero hash) anchors a **lineage root**
129
+ via the legacy `anchor`, emitting **no** `Linked` event.
130
+ - On `vh claim`, a non-zero `--parent` routes the **reveal leg** to `revealWithParent(contentHash,
131
+ salt, uri, parent)`; the commit leg is unchanged (the edge is recorded at reveal time). `--parent`
132
+ works on the **one-shot `vh claim`** AND on the **resumable `vh commit`/`vh reveal` split**:
133
+ `vh commit --parent <hash>` validates the edge up front (same parser as `vh anchor --parent`) and
134
+ persists it into the claim receipt (schema **v4**); a later, separate `vh reveal --receipt <p>` then
135
+ reads `parent` from the receipt and routes to `revealWithParent` — no `--parent` flag on `vh reveal`.
136
+ The `commit()` transaction itself carries no parent (the contract's commit takes only the commitment),
137
+ so the commitment binding is identical with or without a parent; the edge is recorded at reveal time.
138
+ A malformed/self-referential `--parent` on `vh commit` hard-errors **before any network call** (a typo
139
+ never silently drops the edge). See `B-10.1`.
140
+ - **The parent is checked on-chain at REVEAL time, never at commit time.** `vh commit --parent` only
141
+ validates the hash's *shape* locally and persists it; `commit()` does not touch the parent at all
142
+ (it sees only the opaque commitment). So if the named `parent` is stale — never anchored, or anchored
143
+ only after you committed — the **`vh reveal`** reverts `UnknownParent(parent)` (a self-referencing
144
+ parent reverts `SelfParent(contentHash)`), and **`vh commit` still succeeds**. A failed reveal leaves
145
+ the claim receipt **untouched and reusable**: anchor the missing parent (or fix the edge) and re-run
146
+ the same `vh reveal --receipt <p>` — the secret `salt` is never lost (see [`docs/RECEIPTS.md`](RECEIPTS.md)).
147
+ - If the named `parent` was never anchored, the transaction reverts `UnknownParent(parent)`; a
148
+ self-referencing parent reverts `SelfParent(contentHash)`.
149
+ - `--dry-run` prints the plan including a `parent:` line (the predecessor hash, or `(none) — lineage
150
+ root`) and which function it routes to (`anchor` vs `anchorWithParent`), so you can preview the exact
151
+ edge you would record before sending anything — no key needed.
152
+
153
+ The edge does not change the child's own attribution: a `vh anchor --parent` child is still
154
+ `authorBound = false` (first anchorer only), and a `vh claim --parent` child is still `authorBound =
155
+ true` (proven first claimant). Lineage and attribution are orthogonal.
156
+
157
+ ---
158
+
159
+ ## Reading the graph — `vh lineage` + `vh show`
160
+
161
+ Reading the graph is **read-only and needs no key**: both commands take an RPC **provider** only and
162
+ **never construct a signer** — walking a public, immutable lineage must never require the ability to
163
+ write to it.
164
+
165
+ ### `vh lineage <0xhash>` — walk UP the parent chain to the root
166
+
167
+ ```
168
+ vh lineage <0xhash> [--contract a] [--rpc u] [--max-depth n] [--json]
169
+ ```
170
+
171
+ `vh lineage` follows `record.parent` from **child → parent → … → root**, issuing one bounded
172
+ `getRecord` per hop (the contract deliberately never walks an unbounded set on-chain, so the walk is
173
+ **off-chain**). It prints each ancestor in order — `contentHash`, `contributor`, attribution strength,
174
+ `timestamp` (+ ISO-8601), `blockNumber`, `uri`, and the `parent` edge — and flags the lineage root
175
+ (`<- lineage root (no predecessor)`).
176
+
177
+ - **Order is child → root**, with `depth` 0 at the start and increasing toward the root.
178
+ - **Per-record attribution is preserved exactly**: a commit-reveal child reads back
179
+ `authorBound = true` ("proven first claimant"), a plain-anchor ancestor reads back
180
+ `authorBound = false` ("first anchorer only — NOT authorship"). The walk never conflates them — this
181
+ is the same per-record `authorBound` rule from [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md).
182
+ - **`--max-depth <n>`** (default **256**) caps the walk. A finite acyclic chain always terminates at a
183
+ root well before the cap; the cap exists only so a pathological/huge chain can't hang the client.
184
+ Reaching the cap prints a clear note naming the next un-walked predecessor and how to resume — it
185
+ never loops forever. (A non-positive/non-integer `--max-depth` is a usage error.)
186
+ - **`--json`** emits an **ordered ancestor array** (child → root) carrying the same fields, with a root
187
+ serialized as `parent: null, isRoot: true`. A `NOT ANCHORED` start is a first-class value
188
+ (`anchored: false`, empty `ancestors`), not an error object, so a script can branch on it — while the
189
+ CLI still exits non-zero (exit code **4**, mirroring `vh show`'s NOT ANCHORED exit).
190
+ - The human output **always leads with both trust caveats** — the shared record caveat (untrusted
191
+ `uri`; `contributor` only proves authorship when `authorBound` is true) **and** the lineage-specific
192
+ caveat (a `parent` edge is the child author's CLAIM; re-derive both; it transfers no authorship).
193
+
194
+ ### `vh show <0xhash>` — one record, including its `parent`
195
+
196
+ `vh show` looks up a single record by hash (read-only, no key) and now surfaces its `parent` edge: a
197
+ parented record shows the predecessor hash (and suggests `vh show <parent>` to step back one hop); a
198
+ root renders `parent: (none) — lineage root (no predecessor)` so a deliberate root is distinguishable
199
+ from a missing field. As always, `show` proves only that the hash is on-chain — it does **not**
200
+ re-derive content; to bind a record to real bytes you still run `vh verify <path>`.
201
+
202
+ ---
203
+
204
+ ## Worked end-to-end example: anchor a root → anchor a revision → walk the lineage
205
+
206
+ Suppose `0xROOT…` is the anchored Merkle root of `v1` of a contribution and `0xCHILD…` is `v2`. (All
207
+ write commands here also work with `--git` to anchor exactly the files git tracks at a commit — see
208
+ [`docs/MERKLE-LEAVES.md`](MERKLE-LEAVES.md).)
209
+
210
+ **1. Anchor the root (v1) — no `--parent`, so it is a lineage root.**
211
+
212
+ ```
213
+ $ vh anchor ./repo --uri ipfs://root-v1
214
+ anchored 0xROOT… (contributor = 0xAlice…, first anchorer only)
215
+ ```
216
+
217
+ **2. Anchor the revision (v2) — point it at the root with `--parent`.**
218
+
219
+ ```
220
+ $ vh anchor ./repo-v2 --parent 0xROOT… --uri ipfs://child-v2
221
+ anchored 0xCHILD… (parent 0xROOT…) # routed to anchorWithParent; emits Linked(0xCHILD…, 0xROOT…)
222
+ ```
223
+
224
+ If `0xROOT…` had not already been anchored, step 2 would revert `UnknownParent(0xROOT…)`. Passing
225
+ `--parent 0xCHILD…` (the child's own hash) would revert `SelfParent`.
226
+
227
+ **3. Walk the lineage of the child — read-only, no key.**
228
+
229
+ ```
230
+ $ vh lineage 0xCHILD… --contract 0x… --rpc <url>
231
+ NOTE: `uri` is an UNTRUSTED hint (never fetched/validated — re-fetch + re-hash yourself); `contributor` only means proven authorship when authorBound is true (commit-reveal), otherwise it is merely the first anchorer.
232
+
233
+ NOTE (lineage): a `parent` edge is the CHILD author's CLAIM of a predecessor. It does NOT prove the predecessor's content is a genuine ancestor of the child's content (re-derive BOTH yourself and reason about the relationship), and it does NOT transfer the parent's authorship to the child. Each record's contributor/authorBound stands on its own.
234
+
235
+ registry authenticated: REGISTRY_ID ok (v1), chainId 137
236
+ start: 0xchild…
237
+ result: WALKED 2 records (child -> root order)
238
+
239
+ [0] 0xchild…
240
+ contributor: 0xAlice…
241
+ attribution: first anchorer only — NOT authorship
242
+ timestamp: 1750000123 (2025-06-15T12:02:03Z)
243
+ blockNumber: 42
244
+ uri: ipfs://child-v2
245
+ parent: 0xroot…
246
+
247
+ [1] 0xroot… <- lineage root (no predecessor)
248
+ contributor: 0xAlice…
249
+ attribution: first anchorer only — NOT authorship
250
+ timestamp: 1750000001 (2025-06-15T12:00:01Z)
251
+ blockNumber: 41
252
+ uri: ipfs://root-v1
253
+ parent: (none) — lineage root
254
+ ```
255
+
256
+ Like every read command, `vh lineage` first **authenticates the registry** (T-11.2): it prints a
257
+ `registry authenticated: REGISTRY_ID ok (vN), chainId N` line (above) before any ancestor, and the
258
+ `--json` walk carries a top-level `registry: { id, version, chainId }` block — so a consumer knows the
259
+ walk was read from a genuine verifyhash registry, not a look-alike contract. A loud, non-default
260
+ `--skip-identity-check` bypasses the preflight for a known local-dev contract (`registry` then becomes
261
+ `{ "skipped": true, "note": … }`). See the README's
262
+ [authenticated reads](../README.md#authenticated-reads-registry-identity--chainid) section.
263
+
264
+ The same walk as machine-readable JSON for tooling/an indexer:
265
+
266
+ ```
267
+ $ vh lineage 0xCHILD… --contract 0x… --rpc <url> --json
268
+ {
269
+ "start": "0xchild…",
270
+ "registry": { "id": "0x0395e2ec…", "version": 1, "chainId": 137 },
271
+ "anchored": true,
272
+ "ancestors": [
273
+ { "depth": 0, "contentHash": "0xchild…", "contributor": "0xAlice…", "authorBound": false,
274
+ "attribution": "first anchorer only — NOT authorship", "uri": "ipfs://child-v2",
275
+ "parent": "0xroot…", "isRoot": false },
276
+ { "depth": 1, "contentHash": "0xroot…", "contributor": "0xAlice…", "authorBound": false,
277
+ "attribution": "first anchorer only — NOT authorship", "uri": "ipfs://root-v1",
278
+ "parent": null, "isRoot": true }
279
+ ],
280
+ "cappedAtDepth": false,
281
+ "maxDepth": 256,
282
+ "nextParent": null
283
+ }
284
+ ```
285
+
286
+ **What this shows you — and what it does not.** You have proven, on-chain, that `0xROOT…` was anchored
287
+ **before** `0xCHILD…` and that `0xCHILD…`'s author **claimed** `0xROOT…` as a predecessor. You have
288
+ **not** proven that `0xCHILD…`'s bytes are a genuine revision of `0xROOT…`'s — to establish that you
289
+ must independently obtain both contents, **re-derive both hashes** (`vh hash`), and judge the
290
+ relationship yourself. And `0xCHILD…` inherits **no** authorship from `0xROOT…`: each record's
291
+ `contributor`/`authorBound` stands alone. This is the lineage trust boundary, identical in spirit to
292
+ every other field in [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md).
293
+
294
+ ---
295
+
296
+ ## See also
297
+
298
+ - [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md) — what each record field proves and does not,
299
+ including the `parent` clause this doc reuses verbatim.
300
+ - [`docs/MERKLE-LEAVES.md`](MERKLE-LEAVES.md) — what a directory/repo root commits to (paths + bytes),
301
+ including the `--git` scope used to make a revision's root reproducible.
302
+ - [`docs/RECEIPTS.md`](RECEIPTS.md) — how the resumable `vh commit`/`vh reveal` split carries a
303
+ `--parent` in the claim receipt (schema v4, B-10.1) and the receipt trust posture.
304
+
305
+
306
+ ---
307
+ <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>