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/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>
|