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.
- package/LICENSE +201 -0
- package/README.md +883 -0
- package/cli/abi/ContributionRegistry.json +881 -0
- package/cli/agent.js +2173 -0
- package/cli/anchor-artifact.js +853 -0
- package/cli/anchor.js +400 -0
- package/cli/claim.js +881 -0
- package/cli/core/agent-commit.js +448 -0
- package/cli/core/agent-session.js +598 -0
- package/cli/core/anchor-binding.js +663 -0
- package/cli/core/attestation.js +580 -0
- package/cli/core/evidence-plans.js +495 -0
- package/cli/core/fixtures/evidence-plans/baseline.json +19 -0
- package/cli/core/fulfill-intake.js +1082 -0
- package/cli/core/go-live-preflight.js +481 -0
- package/cli/core/license.js +534 -0
- package/cli/core/manifest.js +243 -0
- package/cli/core/packetseal.js +591 -0
- package/cli/core/registryArtifact.js +49 -0
- package/cli/core/revocation.js +539 -0
- package/cli/core/rfc3161.js +389 -0
- package/cli/core/timestamp.js +482 -0
- package/cli/core/trust-asof.js +479 -0
- package/cli/dataset.js +2950 -0
- package/cli/evidence.js +2227 -0
- package/cli/fulfill-webhook-http.js +438 -0
- package/cli/git.js +220 -0
- package/cli/hash.js +550 -0
- package/cli/identity.js +1072 -0
- package/cli/journal-cli.js +1110 -0
- package/cli/journal-log.js +454 -0
- package/cli/journal.js +334 -0
- package/cli/lineage.js +447 -0
- package/cli/list.js +287 -0
- package/cli/parcel.js +1509 -0
- package/cli/proof.js +578 -0
- package/cli/prove.js +300 -0
- package/cli/receipt.js +631 -0
- package/cli/registry.js +331 -0
- package/cli/reputation.js +344 -0
- package/cli/revocation.js +495 -0
- package/cli/serve-verify-http.js +298 -0
- package/cli/serve-verify.js +333 -0
- package/cli/show.js +339 -0
- package/cli/verify.js +383 -0
- package/cli/vh.js +3927 -0
- package/docs/ADOPT.md +183 -0
- package/docs/ADOPTION.json +11 -0
- package/docs/AGENTTRACE.md +247 -0
- package/docs/ANCHORING.md +167 -0
- package/docs/AUDIT.md +55 -0
- package/docs/CONFORMANCE.md +107 -0
- package/docs/DATALEDGER.md +638 -0
- package/docs/DECIDE.md +47 -0
- package/docs/DECISIONS-PENDING.md +27 -0
- package/docs/DEPLOY-PUBLIC-SITE.md +301 -0
- package/docs/ENGINE-LEDGER.json +12 -0
- package/docs/EVIDENCE.md +519 -0
- package/docs/GO-LIVE.md +66 -0
- package/docs/IDENTITY.md +123 -0
- package/docs/INDEPENDENT-VERIFICATION.md +377 -0
- package/docs/INTEGRITY-JOURNAL.md +337 -0
- package/docs/KEY-LIFECYCLE.md +179 -0
- package/docs/LICENSING.md +46 -0
- package/docs/LINEAGE.md +307 -0
- package/docs/LOOP-AUDIT-2026-07-03.json +580 -0
- package/docs/LOOP-HARDENING-PLAN.md +44 -0
- package/docs/MERKLE-LEAVES.md +113 -0
- package/docs/METRICS.jsonl +31 -0
- package/docs/MORNING.md +204 -0
- package/docs/PILOT.md +444 -0
- package/docs/PROOFPARCEL.md +227 -0
- package/docs/PROOFS.md +262 -0
- package/docs/RECEIPTS.md +341 -0
- package/docs/REPUTATION.md +158 -0
- package/docs/SDK.md +301 -0
- package/docs/STRATEGY-ARCHIVE.md +5055 -0
- package/docs/SUPERVISOR-RUNBOOK.md +52 -0
- package/docs/TRUST-BOUNDARIES.md +335 -0
- package/docs/TRUSTLEDGER.md +1976 -0
- package/docs/USAGE-BUDGET.json +121 -0
- package/docs/VERIFY-SERVICE.md +168 -0
- package/index.js +160 -0
- package/package.json +41 -0
- package/trustledger/build-standalone.js +796 -0
- package/trustledger/cli.js +3179 -0
- package/trustledger/close.js +391 -0
- package/trustledger/corpus.js +159 -0
- package/trustledger/dist/BUILD-PROVENANCE.json +99 -0
- package/trustledger/dist/trustledger-standalone.html +6197 -0
- package/trustledger/dist/trustledger-standalone.html.sha256 +1 -0
- package/trustledger/door-core.js +442 -0
- package/trustledger/fixtures/bank.csv +7 -0
- package/trustledger/fixtures/bank.malformed.csv +3 -0
- package/trustledger/fixtures/bank.noalias.csv +5 -0
- package/trustledger/fixtures/bank.ofx +34 -0
- package/trustledger/fixtures/bank.real.csv +5 -0
- package/trustledger/fixtures/corpus/_shared/prior-close.json +22 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/inputs.json +14 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/inputs.json +14 -0
- package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/continuity-break--benign-twin/inputs.json +15 -0
- package/trustledger/fixtures/corpus/continuity-break--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/continuity-break--out-of-trust/inputs.json +15 -0
- package/trustledger/fixtures/corpus/continuity-break--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/inputs.json +13 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/inputs.json +15 -0
- package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/inputs.json +15 -0
- package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/inputs.json +16 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/inputs.json +13 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/meta.json +7 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/inputs.json +13 -0
- package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/meta.json +7 -0
- package/trustledger/fixtures/e2e/bank.aliased.csv +4 -0
- package/trustledger/fixtures/e2e/bank.csv +4 -0
- package/trustledger/fixtures/e2e/bank.nsf.csv +4 -0
- package/trustledger/fixtures/e2e/quickbooks.csv +6 -0
- package/trustledger/fixtures/e2e/quickbooks.nsf.csv +8 -0
- package/trustledger/fixtures/e2e/rentroll.csv +6 -0
- package/trustledger/fixtures/e2e/rentroll.nsf.csv +8 -0
- package/trustledger/fixtures/e2e/rentroll.short.csv +5 -0
- package/trustledger/fixtures/plans/baseline.json +25 -0
- package/trustledger/fixtures/plans/price-binding.example.json +27 -0
- package/trustledger/fixtures/policy/ambiguous-deposit-example.json +12 -0
- package/trustledger/fixtures/policy/baseline.json +19 -0
- package/trustledger/fixtures/policy/ca-example.json +12 -0
- package/trustledger/fixtures/policy/negative-tenant-ledger-example.json +12 -0
- package/trustledger/fixtures/policy/owner-overdraw-example.json +12 -0
- package/trustledger/fixtures/quickbooks.csv +7 -0
- package/trustledger/fixtures/quickbooks.real.csv +5 -0
- package/trustledger/fixtures/rentroll.csv +6 -0
- package/trustledger/fixtures/rentroll.real.csv +4 -0
- package/trustledger/ingest.js +1163 -0
- package/trustledger/lib/policy-bundled-loader.js +44 -0
- package/trustledger/lib/sha256-vendored.js +227 -0
- package/trustledger/license.js +563 -0
- package/trustledger/match.js +551 -0
- package/trustledger/plans.js +551 -0
- package/trustledger/policy.js +398 -0
- package/trustledger/public/index.html +512 -0
- package/trustledger/reconcile.js +1486 -0
- package/trustledger/report.js +887 -0
- package/trustledger/seal.js +854 -0
- package/trustledger/server.js +391 -0
- package/trustledger/valueproof.js +350 -0
package/docs/LINEAGE.md
ADDED
|
@@ -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>
|