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/IDENTITY.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Producer identity card (`vh identity`)
|
|
2
|
+
|
|
3
|
+
A **signed, offline-verifiable "who is this vendor, and what exactly do they attest?"** card. A producer
|
|
4
|
+
SIGNS — with the **same key** that signs their evidence packets / signed licenses / dataset attestations —
|
|
5
|
+
a small, self-describing container that binds their **`vendorAddress`** to a bounded `claims[]` set (what
|
|
6
|
+
they attest) and an honest `nonClaims[]` set (what they explicitly do **NOT**). A recipient — or a **cold
|
|
7
|
+
prospect** who has never met you — recovers the signer from the card, confirms it **equals the card's own
|
|
8
|
+
`vendorAddress`** (the key controls the address it claims), and OPTIONALLY pins it to an address they
|
|
9
|
+
learned out of band. All of it is **OFFLINE, key-free, network-free, I/O-free**.
|
|
10
|
+
|
|
11
|
+
It is the **recipient's / cold prospect's pin-point**: every other artifact this family mints (an evidence
|
|
12
|
+
seal, a signed license, a dataset attestation) pins its producer by a vendor **address** the recipient must
|
|
13
|
+
learn out of band — an email, a slide, a README line. The identity card is the **one** artifact whose
|
|
14
|
+
whole job is to answer, verifiably, "**does this 0x-address really belong to THIS vendor, and what exactly
|
|
15
|
+
do they attest — and, just as load-bearing, what do they explicitly NOT?**" before the recipient trusts a
|
|
16
|
+
single packet.
|
|
17
|
+
|
|
18
|
+
**Trust boundary (the output leads with this, verbatim — the standing `IDENTITY_CARD_TRUST_NOTE`):**
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
This is a verifyhash producer IDENTITY CARD: the holder of `vendorAddress`'s key SIGNED it, binding that address to the `claims` it attests and the `nonClaims` it explicitly does NOT. verify RE-DERIVES the signer from these exact bytes and REQUIRES it to equal `vendorAddress` — it never trusts the file's own claims. It proves IDENTITY + the claim SET ONLY: it does NOT prove any specific sealed/signed packet is true (each packet carries its own proof), it is NOT a trusted TIMESTAMP ("published since T" rides the human-owned signing/timestamp trust-root, STRATEGY.md P-3), and it is NOT a legal opinion.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> A card proves **IDENTITY + the claim SET**, **NOT packet truth** (each sealed/signed packet carries its
|
|
25
|
+
> own proof — `vh evidence verify` / `verify-signed`), **NOT a trusted timestamp** (P-3), and **NOT a legal
|
|
26
|
+
> opinion**. See [`docs/TRUST-BOUNDARIES.md`](TRUST-BOUNDARIES.md).
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
vh identity publish --address <0xaddr> --product-line <line> --claim <text> [--claim ...] --non-claim <text> [--non-claim ...] [--published-at <ISO>] (--key-env <VAR> | --key-file <path>) [--out <p>] [--json]
|
|
32
|
+
vh identity verify <card> [--signer <0xaddr>] [--json]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### `vh identity publish` — mint the card
|
|
36
|
+
|
|
37
|
+
`publish` MINTS a signed `*.vhidentity.json` card binding `--address` to the `--claim` set it attests + the
|
|
38
|
+
`--non-claim` set it explicitly does NOT. It signs with a **HUMAN-provisioned key** (EXACTLY ONE of
|
|
39
|
+
`--key-env` / `--key-file`, **read-used-discarded** via the shared `loadSigningWallet` — the loop **NEVER**
|
|
40
|
+
generates, persists, or logs a key, and the key never appears in any output).
|
|
41
|
+
|
|
42
|
+
- **The load-bearing mint invariant — the key controls the address it claims.** `publish` mints **ONLY**
|
|
43
|
+
when the provisioned key's address **EQUALS** `--address`. A key that does **NOT** control `--address`
|
|
44
|
+
**hard-errors (exit 2) BEFORE writing anything** — so a card can never assert an identity the key cannot
|
|
45
|
+
back, and a published card **always** round-trips to ACCEPTED by construction.
|
|
46
|
+
- **Filesystem hygiene.** Default **prints the card + writes NOTHING**; `--out <p>` writes ONLY to the
|
|
47
|
+
caller-chosen path — **never silently to cwd**.
|
|
48
|
+
- `--product-line` is one of the closed set `["dataledger", "evidence", "trustledger"]` (an out-of-set line
|
|
49
|
+
is a usage error); `claims` and `nonClaims` are each a **non-empty** list (a card that attests nothing, or
|
|
50
|
+
that drops the honest boundary, is refused).
|
|
51
|
+
- The output **LEADS with the trust line**; `--json` carries the PUBLIC card summary (vendorAddress, signer,
|
|
52
|
+
productLine, claims, nonClaims, publishedAt) + the artifact — and **never the key**.
|
|
53
|
+
- **Exit:** **0** ok / **2** usage (missing/invalid field, key-source error, key does not control
|
|
54
|
+
`--address`) / **1** IO (`--out` write).
|
|
55
|
+
|
|
56
|
+
### `vh identity verify` — check + pin the card
|
|
57
|
+
|
|
58
|
+
`verify <card>` is the **OFFLINE / key-free / network-free** read path. It RECOVERS the signer from the
|
|
59
|
+
embedded canonical card bytes + signature and:
|
|
60
|
+
|
|
61
|
+
1. confirms the signature backs the container's claimed `signer` (**ALWAYS**);
|
|
62
|
+
2. confirms the recovered signer **IS** the card's own `vendorAddress` — the load-bearing "the key controls
|
|
63
|
+
the address it claims" check (**ALWAYS**);
|
|
64
|
+
3. OPTIONALLY pins the recovered signer to an expected **`--signer <0xaddr>`** you learned out of band.
|
|
65
|
+
|
|
66
|
+
The verdict is **ACCEPTED** only when **every requested check passes**; a **forged / tampered / wrong-vendor
|
|
67
|
+
card, or a wrong `--signer`, is a clean REJECTED — NEVER a silent pass**. It **LEADS with the trust line**,
|
|
68
|
+
prints the `claims` + `nonClaims` + per-check **PASS / FAIL / [skip]**, and writes **NOTHING**.
|
|
69
|
+
|
|
70
|
+
- **Exit:** **0** ACCEPTED / **3** REJECTED / **2** usage / **1** IO — the same read contract as
|
|
71
|
+
`vh evidence verify-signed` / `vh dataset verify-attest`.
|
|
72
|
+
|
|
73
|
+
## Pin once, trust across handoffs
|
|
74
|
+
|
|
75
|
+
The card exists so a recipient does the **address-to-vendor** trust step **ONCE**, then reuses that pin
|
|
76
|
+
across **every later handoff** — without re-establishing trust out of band each time.
|
|
77
|
+
|
|
78
|
+
1. **Pin once.** The vendor publishes their card once (`vh identity publish … --out vendor.vhidentity.json`)
|
|
79
|
+
and the recipient confirms it once: `vh identity verify vendor.vhidentity.json --signer <addr-you-were-given>`.
|
|
80
|
+
That single ACCEPTED verdict is the recipient's durable answer to "this 0x-address really is this vendor,
|
|
81
|
+
and here is exactly what they attest / do NOT." The `--signer` pin binds the card to the address the
|
|
82
|
+
recipient learned out of band (the email/slide/contract) — so the out-of-band step happens **once**.
|
|
83
|
+
2. **Trust across handoffs.** Every subsequent evidence packet, signed license, or dataset attestation the
|
|
84
|
+
vendor hands over is signed by the **same** key. The recipient verifies each artifact's signer
|
|
85
|
+
(`vh evidence verify-signed <p> --signer <addr>`, etc.) against the **same pinned address** — no new
|
|
86
|
+
out-of-band step, no re-pinning. The card's `vendorAddress` IS the address every later `--signer` pin
|
|
87
|
+
reuses, so one verified card amortizes the trust cost across an unbounded stream of handoffs.
|
|
88
|
+
|
|
89
|
+
This is why the card is the **cold prospect's first stop**: it converts a single out-of-band "is this
|
|
90
|
+
address really them?" into a verifiable, reusable pin — closing the gap between "interesting claim" and "I
|
|
91
|
+
trust this vendor enough to run a pilot."
|
|
92
|
+
|
|
93
|
+
## What the card does and does NOT prove
|
|
94
|
+
|
|
95
|
+
- **It DOES prove IDENTITY + the claim SET.** The key that controls `vendorAddress` signed a bounded,
|
|
96
|
+
self-describing list of what the vendor attests (`claims`) and what they explicitly do **NOT** (`nonClaims`).
|
|
97
|
+
verify re-derives the signer from the exact bytes and requires it to equal `vendorAddress` — it never
|
|
98
|
+
trusts the file's own claims.
|
|
99
|
+
- **It does NOT prove any specific packet's contents are true.** Each sealed/signed packet carries its OWN
|
|
100
|
+
proof — re-derive it (`vh evidence verify`) and check the signer (`vh evidence verify-signed`). The card
|
|
101
|
+
vouches for **who** the vendor is and **what** they claim to attest, not for the truth of any one packet.
|
|
102
|
+
- **It is NOT a trusted timestamp.** A `publishedAt` is the vendor's self-asserted instant; "published
|
|
103
|
+
since T" rides the human-owned signing/timestamp trust-root (`needs-human`, **P-3** in
|
|
104
|
+
[`STRATEGY.md`](../STRATEGY.md)), exactly like every other dated artifact in this family.
|
|
105
|
+
- **It is NOT a legal opinion.** The card makes no compliance/legal claim; it is tamper-evidence + a signed
|
|
106
|
+
identity binding, nothing more.
|
|
107
|
+
|
|
108
|
+
## Where it fits
|
|
109
|
+
|
|
110
|
+
The card is **one more product on the shared signed-attestation envelope** — no new crypto, no new
|
|
111
|
+
dependency, no new scheme. It defines its own KIND (`vh-identity-card` / `vh-identity-card-signed`) + a
|
|
112
|
+
closed field set, then hands the canonical payload to [`cli/core/attestation.js`](../cli/core/attestation.js),
|
|
113
|
+
which does all the crypto: it embeds the EXACT canonical payload bytes, attaches the detached **EIP-191**
|
|
114
|
+
signature, and later re-derives the signer — byte-for-byte the same shared paths the evidence seal and the
|
|
115
|
+
signed license use. The publish/verify core lives in [`cli/identity.js`](../cli/identity.js).
|
|
116
|
+
|
|
117
|
+
Provisioning the real vendor key, setting the price, and landing the first design partner remain **human
|
|
118
|
+
steps** — the producer publishes their card with the same key they provision for signed evidence/licenses
|
|
119
|
+
(STRATEGY.md › **P-7 step 1** / **P-6 step 1**).
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
<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>
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# Independent verification — the buyer/counterparty deliverable
|
|
2
|
+
|
|
3
|
+
> **Audience: a NON-customer.** You were handed a sealed verifyhash artifact by a counterparty (a
|
|
4
|
+
> "producer") and you need to decide whether to believe it — without trusting the producer's software,
|
|
5
|
+
> their servers, or us. This document is the contract that makes that possible. The runnable tool and a
|
|
6
|
+
> quickstart live in [`../verifier/README.md`](../verifier/README.md); this is the deeper specification.
|
|
7
|
+
|
|
8
|
+
The whole point of a provenance seal is that **the party relying on it is not the party who made it.**
|
|
9
|
+
A proof you can only check with the producer's own toolchain is not a proof — it is a request to trust
|
|
10
|
+
them. `verify-vh` exists so the relying party can recompute everything themselves, offline, for free.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 0. Get it in 10 seconds (zero-install — start here)
|
|
15
|
+
|
|
16
|
+
You do not need to clone this repo, run `npm install`, create an account, or install our toolchain to
|
|
17
|
+
check a seal. Verification is **one self-contained file**,
|
|
18
|
+
[`../verifier/dist/verify-vh-standalone.js`](../verifier/dist/verify-vh-standalone.js), that depends on
|
|
19
|
+
**nothing but Node core**. The fastest honest path:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Save ONE file — verifier/dist/verify-vh-standalone.js — next to the packet you were handed.
|
|
23
|
+
# (Copy it from the repo, or your counterparty sends it to you. No clone, no install.)
|
|
24
|
+
|
|
25
|
+
# 2. (Optional, recommended) Check its PUBLISHED checksum so you know the file itself wasn't swapped.
|
|
26
|
+
# The producer publishes the SHA-256 out-of-band; we ship it next to the file as
|
|
27
|
+
# verify-vh-standalone.js.sha256 (standard `sha256sum` line format):
|
|
28
|
+
sha256sum -c verify-vh-standalone.js.sha256 # -> "verify-vh-standalone.js: OK"
|
|
29
|
+
# (macOS: `shasum -a 256 -c verify-vh-standalone.js.sha256`. Or eyeball it:
|
|
30
|
+
# `sha256sum verify-vh-standalone.js` and compare to the published hex.)
|
|
31
|
+
|
|
32
|
+
# 2b. (Even better — needs NO second file.) The bundle is SELF-DESCRIBING: it carries its own
|
|
33
|
+
# build-provenance and can check its own bytes. From the ONE file alone:
|
|
34
|
+
node verify-vh-standalone.js --self-attest # -> "[MATCH] ... this file is intact"; exit 1 if edited
|
|
35
|
+
node verify-vh-standalone.js --provenance # -> the ordered source modules + sha256 it was built from
|
|
36
|
+
# `--provenance` lists the exact `verifier/lib/*` files (each pinned by its own sha256) the bundle inlines;
|
|
37
|
+
# those same hashes are in verifier/dist/BUILD-PROVENANCE.json, which
|
|
38
|
+
# `node verifier/build-standalone.js --check` reproduces end-to-end from source.
|
|
39
|
+
|
|
40
|
+
# 3. Run it on the packet — no clone, no `npm install`, no node_modules, no account:
|
|
41
|
+
node verify-vh-standalone.js <packet>.vhevidence.json --vendor 0xPRODUCER_ADDRESS
|
|
42
|
+
# exit 0 = verifies; exit 3 = REJECTED (and it names the file that changed / the wrong signer).
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
That single file is **byte-for-byte the same verifier** documented below — it is built deterministically
|
|
46
|
+
from the `verifier/` sources (the keccak provider is swapped for a vendored pure-JS one, cross-checked
|
|
47
|
+
against `js-sha3` and `ethers`), and a stale bundle FAILS CI
|
|
48
|
+
([`../test/verifier.standalone.test.js`](../test/verifier.standalone.test.js)). The split-source,
|
|
49
|
+
`npm install`-the-`verifier/`-tree path in [`../verifier/README.md`](../verifier/README.md) and §4 below
|
|
50
|
+
remains for auditors who want to read each lib file on its own; both paths compute the **identical**
|
|
51
|
+
verdict and exit code.
|
|
52
|
+
|
|
53
|
+
**The easier path does not change what is proven.** Whether you run the one-file bundle or the split tree,
|
|
54
|
+
the seal proves **tamper-evidence + signer-pin**, NOT a trusted "sealed at T" (that still requires
|
|
55
|
+
**P-3** — see §3). The convenience is in the *install*, never in the *claim*.
|
|
56
|
+
|
|
57
|
+
> **New here? Start with the guided 60-second challenge.** If you were handed a seal cold and want the
|
|
58
|
+
> *fastest* "prove it to me" path — verify a real pre-sealed sample packet, then tamper one byte and watch
|
|
59
|
+
> it get rejected and the changed file named — run [`../challenge/`](../challenge/) (one command, zero
|
|
60
|
+
> install, zero account). It is the cold-prospect entry point to everything specified below, on the
|
|
61
|
+
> **FREE, UNSIGNED** path (so there is no signer to pin in the sample), and it points free-verify →
|
|
62
|
+
> free-produce → **paid-produce**. See [`../challenge/README.md`](../challenge/README.md).
|
|
63
|
+
|
|
64
|
+
> **No Node on the machine? Run that challenge in your browser.** The same engine also ships as ONE
|
|
65
|
+
> committed, fully offline HTML page —
|
|
66
|
+
> [`../verifier/dist/verify-vh-standalone.html`](../verifier/dist/verify-vh-standalone.html): open it,
|
|
67
|
+
> click the built-in sample packet (**ACCEPT**), change one character of the sample file on the page and
|
|
68
|
+
> re-verify (**REJECT**, naming the file), then drag a real packet + its files in for the same verdict +
|
|
69
|
+
> per-file localization specified below. The file contains **no network API at all** — check the browser
|
|
70
|
+
> **devtools Network tab**: it stays empty, so your bytes never leave your machine. The boundary is the
|
|
71
|
+
> one in §3, unchanged; for CI/production gating use the node standalone (`verify-vh-standalone.js`).
|
|
72
|
+
|
|
73
|
+
> **On the checksum's trust model — read this so step 2 isn't oversold.** `sha256sum -c` proves the file
|
|
74
|
+
> you saved is bit-for-bit the file whose hash was published; it does **not**, by itself, prove *who*
|
|
75
|
+
> published that hash. The hash is a **transport-integrity** check (did the bundle arrive intact / un-swapped?),
|
|
76
|
+
> pinned to a value you obtain **out-of-band** from the producer — exactly like the `--vendor` signer
|
|
77
|
+
> address. The real root of trust is still the cross-checked, dependency-grepped, network-poisoned audit of
|
|
78
|
+
> the source in §5: the checksum only saves you from re-reading 80 KB every time once you have audited it once.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 0a. Produce your own seal in 10 seconds, then verify it (the free round-trip)
|
|
83
|
+
|
|
84
|
+
§0 shows the **verify** half. There is a matching FREE **produce** half, so you can drive the *entire*
|
|
85
|
+
loop yourself before you ever talk to us — **seal your own files, hand them off, verify** — with **no
|
|
86
|
+
clone, no `npm install`, no account, no key** on either side. The producer half is also **one
|
|
87
|
+
self-contained file**, [`../verifier/dist/seal-vh-standalone.js`](../verifier/dist/seal-vh-standalone.js),
|
|
88
|
+
depending on **nothing but Node core** (the same vendored keccak the verifier uses):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 1. Save ONE file — verifier/dist/seal-vh-standalone.js (optionally check its published
|
|
92
|
+
# seal-vh-standalone.js.sha256 the same way as the verifier in §0).
|
|
93
|
+
|
|
94
|
+
# 2. Seal up to 25 of YOUR OWN files into one tamper-evident packet — no install, no key, no account:
|
|
95
|
+
node seal-vh-standalone.js <your-folder> -o packet.vhevidence.json # exit 0 = sealed
|
|
96
|
+
|
|
97
|
+
# 3. Hand packet.vhevidence.json + your folder to a counterparty; they run the FREE verifier from §0:
|
|
98
|
+
node verify-vh-standalone.js packet.vhevidence.json --dir <your-folder> # exit 0 = verifies; 3 = REJECTED
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
That is the whole self-service adoption loop, free on both ends: one file to **seal**, one file to
|
|
102
|
+
**verify**, and the `.vhevidence.json` is all that changes hands. The standalone sealer is built
|
|
103
|
+
deterministically from the `verifier/` sources and a stale bundle FAILS CI
|
|
104
|
+
([`../test/freeseal.standalone.test.js`](../test/freeseal.standalone.test.js)); its seal bytes are
|
|
105
|
+
byte-for-byte identical to the producer's own `cli/evidence.js` seal over the same folder, so a free seal
|
|
106
|
+
is the **same** artifact the paid tool wraps — not a toy.
|
|
107
|
+
|
|
108
|
+
**The honest boundary is identical to §0, and the free seal is narrower still.** A standalone seal proves
|
|
109
|
+
**tamper-evidence + offline-recompute** — these are exactly those files, independently re-derivable by
|
|
110
|
+
anyone — and **NOT** a trusted "sealed at T" without **P-3** (see §3). On top of that the FREE seal is
|
|
111
|
+
**UNSIGNED** (no signer to pin; there is no `--sign`/`--license`/`--key` flag here at all) and **capped at
|
|
112
|
+
25 files** (a folder of more than 25 hard-errors and writes nothing). **SIGNING** (an EIP-191 signer-pin
|
|
113
|
+
you can then check with `--vendor`) and **UNLIMITED** sealing are the PAID upgrade —
|
|
114
|
+
`vh evidence seal --sign` / the `evidence_unlimited` entitlement (`--license`) through the full producer
|
|
115
|
+
CLI. The free round-trip is the funnel; the paid upgrade adds *who signed it* and *no file cap*.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 1. The deliverable, precisely
|
|
120
|
+
|
|
121
|
+
`verify-vh` is a **standalone, read-only, OFFLINE** verifier that, given an artifact plus the files it
|
|
122
|
+
references and the producer's signer address:
|
|
123
|
+
|
|
124
|
+
1. **re-derives** the keccak-256 Merkle root from the bytes you hold,
|
|
125
|
+
2. **compares** it to the root embedded (and signed) in the artifact,
|
|
126
|
+
3. **recovers** the secp256k1 signer of the signature, and
|
|
127
|
+
4. **pins** that signer to an address you supply out-of-band.
|
|
128
|
+
|
|
129
|
+
It emits a deterministic verdict and a CI-gateable exit code. It is **free** — verification is never a
|
|
130
|
+
paid tier (the producer pays to seal; anyone may verify forever). It ships with near-zero dependencies
|
|
131
|
+
(`js-sha3` + a tiny vendored secp256k1 routine), so a third party can `npm install` it alone and audit
|
|
132
|
+
the entire thing in an afternoon.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 2. The exact bytes that are verified
|
|
137
|
+
|
|
138
|
+
### 2.1 Content hash (per file)
|
|
139
|
+
```
|
|
140
|
+
contentHash(file) = keccak256( raw file bytes )
|
|
141
|
+
```
|
|
142
|
+
No normalization, no encoding, no line-ending fixups — the literal bytes on disk. A single changed byte
|
|
143
|
+
produces a different hash, and the verifier reports that file as `CHANGED` with both the sealed and the
|
|
144
|
+
on-disk hash so the diff is attributable.
|
|
145
|
+
|
|
146
|
+
### 2.2 Merkle root (per artifact)
|
|
147
|
+
Each referenced file contributes a leaf binding its `relPath` to its `contentHash`. Reconciliation
|
|
148
|
+
seals additionally fold in a synthetic **header leaf** computed from the seal's own `verdict` + input
|
|
149
|
+
role bindings, so editing the verdict (which lives in the seal, not in any file) still changes the
|
|
150
|
+
recomputed root. All leaves are folded into one keccak-256 **root**. The exact leaf encoding and
|
|
151
|
+
pairing order are in [`../verifier/lib/merkle.js`](../verifier/lib/merkle.js) — short and
|
|
152
|
+
dependency-free by design, so you can re-implement it.
|
|
153
|
+
|
|
154
|
+
### 2.3 Signature (EIP-191 `personal_sign` / keccak)
|
|
155
|
+
A signed artifact carries a 65-byte `r(32) || s(32) || v(1)` secp256k1 signature whose **message is the
|
|
156
|
+
canonical UTF-8 bytes of the artifact's unsigned payload** — re-derived by the verifier in
|
|
157
|
+
[`../verifier/lib/canonical.js`](../verifier/lib/canonical.js), **not** read back from a self-asserting
|
|
158
|
+
field. The digest is the standard EIP-191 personal-sign pre-image:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
digest = keccak256( "\x19Ethereum Signed Message:\n" + decimal(byteLength(message)) + message )
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The signer address is recovered via standard secp256k1 public-key recovery (SEC 1 §4.1.6) and rendered
|
|
165
|
+
as `"0x" + lastBytes20( keccak256( X32 || Y32 ) )`, lowercased. With `--vendor 0xADDR` the recovered
|
|
166
|
+
address must equal it (20 raw bytes; checksum casing ignored) or the verdict is `wrong_issuer`. This is
|
|
167
|
+
a second, independent implementation of the family's recovery, continuously cross-checked against the
|
|
168
|
+
production `ethers` path so the two cannot silently diverge
|
|
169
|
+
([`../test/verifier.crypto.test.js`](../test/verifier.crypto.test.js)).
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 3. The trust boundary
|
|
174
|
+
|
|
175
|
+
`verify-vh` makes exactly three claims, all derivable from the bytes in your hands, and explicitly
|
|
176
|
+
disclaims everything else.
|
|
177
|
+
|
|
178
|
+
### What it DOES prove
|
|
179
|
+
- **Tamper-evidence.** The referenced files are byte-for-byte the ones sealed; any change is localized
|
|
180
|
+
and attributable (which file, sealed-hash vs on-disk-hash).
|
|
181
|
+
- **Offline recompute.** The root is re-derivable by you alone — no trusted server, no "it matched on
|
|
182
|
+
our end." No network access occurs (proven mechanically; see §5).
|
|
183
|
+
- **Signer-pin.** *Which key* vouched, pinned to an address you obtained out-of-band, so a different
|
|
184
|
+
key cannot impersonate the producer.
|
|
185
|
+
- **Revocation-aware (opt-in).** With `--revocations <file-or-dir> [--as-of <ISO>]` `verify-vh`
|
|
186
|
+
consults the producer's signed key revocations and **downgrades** an otherwise-ACCEPTED artifact to
|
|
187
|
+
**REVOKED** (exit 3) when the signing key was revoked **at or before** the as-of instant (default:
|
|
188
|
+
now). A revocation dated *after* the as-of leaves the verdict ACCEPTED with an informational
|
|
189
|
+
later-revoked note; a forged / tampered / third-party revocation is **ignored** with a warning (a
|
|
190
|
+
revocation only ever *removes* trust, never adds it — a key revokes itself, so a third party cannot
|
|
191
|
+
grief you into rejecting a good artifact). This reaches the **same** downgrade the producer-stack
|
|
192
|
+
`vh ... verify-signed --revocations <f> --as-of <T>` reaches on the identical inputs — fully OFFLINE,
|
|
193
|
+
no producer stack, no network, no key (see [`KEY-LIFECYCLE.md`](KEY-LIFECYCLE.md)). A directory is
|
|
194
|
+
read as a flat pool of revocation files; a single file may be one revocation or a JSON array.
|
|
195
|
+
|
|
196
|
+
### What it does NOT prove
|
|
197
|
+
- **NOT a trusted "sealed at time T."** A signature attests *this key vouched for these bytes* — not
|
|
198
|
+
*when*. Any `timestamp`/`sealedAt`/`reportDate` field inside an artifact is **producer-asserted** and
|
|
199
|
+
rides the human-owned signing/timestamp trust-root (proposal **P-3** in
|
|
200
|
+
[`../STRATEGY.md`](../STRATEGY.md)). For an *independent* time anchor the family ships a separate,
|
|
201
|
+
also-offline **RFC-3161** timestamp path (`vh dataset/parcel verify-timestamp`, P-3 Option B) — that
|
|
202
|
+
is a different deliverable, and `verify-vh` does not assert it.
|
|
203
|
+
- **NOT a legal/accounting opinion.** A green verdict means the bytes and the signer check out — not
|
|
204
|
+
that the producer's underlying conclusion (a reconciliation result, a dataset's lawful provenance) is
|
|
205
|
+
correct. That judgement stays with the producer and their reviewers.
|
|
206
|
+
|
|
207
|
+
> **One sentence:** `verify-vh` tells you the bytes are unchanged and which key signed them — **not
|
|
208
|
+
> when, and not whether the producer's conclusion is true.**
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 4. Worked example: `producer seals → hands over packet → counterparty runs verify-vh`
|
|
213
|
+
|
|
214
|
+
A real end-to-end run (test-only ephemeral keys — never a real key or real funds; the same path the
|
|
215
|
+
acceptance suite [`../test/verifier.cli.test.js`](../test/verifier.cli.test.js) exercises against the
|
|
216
|
+
REAL producer code).
|
|
217
|
+
|
|
218
|
+
**(a) Producer side.** With their paid evidence tool, the producer seals a directory and publishes
|
|
219
|
+
their signer address (`0xb463…3221`) somewhere the counterparty trusts (contract, site, email sig):
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
data/
|
|
223
|
+
model-card.md
|
|
224
|
+
weights.bin
|
|
225
|
+
packet.vhevidence.json # the signed seal, handed to the counterparty alongside the files
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**(b) Hand-over.** The counterparty receives the three files. No producer software, account, or license
|
|
229
|
+
is involved.
|
|
230
|
+
|
|
231
|
+
**(c) Counterparty verifies** (one runtime dependency, no network):
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
cd verifier && npm install
|
|
235
|
+
node verify-vh.js ../data/packet.vhevidence.json \
|
|
236
|
+
--vendor 0xb463f30cf53d1e0365130363ae9b9867998c3221
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Accepted output (exit `0`):
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
kind: vh.evidence-seal-signed
|
|
243
|
+
embedded kind: vh.evidence-seal
|
|
244
|
+
signed: yes
|
|
245
|
+
recovered signer: 0xb463f30cf53d1e0365130363ae9b9867998c3221
|
|
246
|
+
claimed signer: 0xb463f30cf53d1e0365130363ae9b9867998c3221
|
|
247
|
+
pinned --vendor: 0xb463f30cf53d1e0365130363ae9b9867998c3221
|
|
248
|
+
signer matches vendor: yes
|
|
249
|
+
sealed root: 0x51004f29ea5b0081be2943d377b2c1572b0543af4bfea724642fa73db3589dd5
|
|
250
|
+
recomputed root: 0x51004f29ea5b0081be2943d377b2c1572b0543af4bfea724642fa73db3589dd5
|
|
251
|
+
root matches: yes
|
|
252
|
+
files: 2 matched, 0 changed, 0 missing, 0 rejected, 0 unexpected
|
|
253
|
+
|
|
254
|
+
OK — the artifact verifies.
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`--json` returns a stable verdict object — e.g.
|
|
258
|
+
`{"verdict":"OK","reason":"OK","accepted":true,"rootMatches":true,"signerMatchesVendor":true,"counts":{"matched":2,"changed":0,"missing":0,"escaped":0,"unexpected":0}}`
|
|
259
|
+
— so a counterparty's CI can gate on `accepted`.
|
|
260
|
+
|
|
261
|
+
**(d) The rejections you should be able to reproduce** (each a clean exit `3`, never a crash):
|
|
262
|
+
|
|
263
|
+
| you change | verdict | exit |
|
|
264
|
+
|------------|---------|------|
|
|
265
|
+
| any sealed byte (`echo x >> model-card.md`) | `CHANGED` (names the file, sealed vs on-disk hash) | 3 |
|
|
266
|
+
| a sealed file is absent | `MISSING` | 3 |
|
|
267
|
+
| pass a `--vendor` that is not the signer | `wrong_issuer` | 3 |
|
|
268
|
+
| corrupt the embedded signature | `bad_signature` | 3 |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 4a. Batch / manifest mode — gate a whole release in one invocation
|
|
273
|
+
|
|
274
|
+
A release is rarely one artifact. To make `verify-vh` a wired-in CI **merge gate** rather than a one-off
|
|
275
|
+
demo, a single invocation can verify **every** artifact a release produces and return **one** exit code.
|
|
276
|
+
|
|
277
|
+
**Two ways to name the set:**
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# (i) repeated positionals — each inherits the one top-level --vendor/--dir:
|
|
281
|
+
verify-vh a.vhevidence.json b.vhseal --vendor 0xADDR --dir ./out
|
|
282
|
+
|
|
283
|
+
# (ii) a manifest file — each entry carries its OWN optional --vendor/--dir:
|
|
284
|
+
verify-vh --manifest release.manifest [--vendor 0xADDR] [--dir <d>] [--json]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Manifest format.** A manifest is EITHER a **newline list** OR a **JSON array**:
|
|
288
|
+
|
|
289
|
+
- *Newline form* — one entry per line; blank lines and lines beginning with `#` are skipped. A line is an
|
|
290
|
+
artifact path followed by optional `--vendor <0xaddr>` / `--dir <d>` tokens:
|
|
291
|
+
```text
|
|
292
|
+
# 2026-Q2 release
|
|
293
|
+
datasets/march.vhevidence.json --vendor 0xb463…3221 --dir datasets/march
|
|
294
|
+
recon/q2.vhseal --vendor 0xb463…3221
|
|
295
|
+
proofs/claim-7.vhproof.json
|
|
296
|
+
```
|
|
297
|
+
- *JSON form* — an array of strings and/or `{ "artifact": "...", "vendor"?: "0x...", "dir"?: "..." }`
|
|
298
|
+
objects.
|
|
299
|
+
|
|
300
|
+
Artifact paths resolve relative to the **manifest file's own directory** (a release ships its manifest
|
|
301
|
+
alongside its artifacts). A per-entry `--dir` likewise resolves against the manifest directory and
|
|
302
|
+
localizes where THAT artifact's sibling files are read. A **top-level** `--vendor`/`--dir` is a
|
|
303
|
+
**default** every entry inherits unless the entry overrides it. The manifest is parsed in-process; it
|
|
304
|
+
introduces **no new crypto and no network** — it is a list, and nothing more.
|
|
305
|
+
|
|
306
|
+
**Aggregate exit contract** (the existing `0/3/2/1` codes, now over the whole set):
|
|
307
|
+
|
|
308
|
+
| exit | when |
|
|
309
|
+
|------|------|
|
|
310
|
+
| `0` (OK) | **and only if** EVERY artifact verifies (each `accepted`) |
|
|
311
|
+
| `3` (REJECTED) | ANY artifact is rejected — the report names WHICH artifact failed and why (per-entry `reason`) |
|
|
312
|
+
| `2` (USAGE) | a bad flag, a malformed per-entry `--vendor`, an empty manifest, or `--manifest` passed together with a positional artifact |
|
|
313
|
+
| `1` (IO) | the manifest itself, or any listed artifact, is unreadable / not the expected shape |
|
|
314
|
+
|
|
315
|
+
Usage and IO faults are evaluated per entry and **short-circuit** the whole run with the matching code:
|
|
316
|
+
a release gate must never report `ok` while one of its artifacts could not even be read or parsed.
|
|
317
|
+
|
|
318
|
+
**Stable `--json` aggregate.** With `--json`, batch mode emits one object:
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{ "ok": false, "total": 3, "passed": 2, "failed": 1, "results": [ /* …per-artifact… */ ] }
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Each element of `results[]` is **byte-identical in shape** to the single-artifact `--json` verdict object
|
|
325
|
+
(§4) — the SAME core (`verifyArtifact`) verifies every entry, so the per-artifact body cannot drift from
|
|
326
|
+
the single path. Gate your CI on `ok` (or the exit code). Every entry preserves the same per-entry
|
|
327
|
+
path-escape / no-network guarantees (§3, §5) as a lone verify. The **single-artifact** invocation
|
|
328
|
+
(`verify-vh <artifact>`) is a strict subset and is unchanged: a lone positional emits the single-artifact
|
|
329
|
+
object, not an aggregate.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## 4b. A copy-paste CI merge gate — wire it into the partner's pipeline
|
|
334
|
+
|
|
335
|
+
Batch mode answers the most common B2B adoption question — *"how do I make my pipeline AUTOMATICALLY
|
|
336
|
+
reject a tampered/forged artifact on every merge?"* — only once it is actually **wired into CI**. Two
|
|
337
|
+
shipped snippets do that with a single paste, and both install **only** the standalone verifier
|
|
338
|
+
(`js-sha3`, never the producer's ethers/hardhat stack):
|
|
339
|
+
|
|
340
|
+
- **[`../verifier/ci/verify-vh.generic.sh`](../verifier/ci/verify-vh.generic.sh)** — a portable `set -e`
|
|
341
|
+
shell gate for GitLab CI / CircleCI / Jenkins / a Makefile / a git hook. Configured purely by env vars
|
|
342
|
+
(`VH_VENDOR`, plus `VH_MANIFEST` *or* `VH_ARTIFACTS`, optional `VH_DIR`), it runs `verify-vh` over the
|
|
343
|
+
release and passes the `0/3/2/1` exit code straight through, so any non-zero verdict **fails the job**.
|
|
344
|
+
- **[`../verifier/ci/verify-vh.github-actions.yml`](../verifier/ci/verify-vh.github-actions.yml)** — a
|
|
345
|
+
GitHub Actions workflow dropped at `.github/workflows/verify-vh.yml` that gates every push / pull
|
|
346
|
+
request.
|
|
347
|
+
|
|
348
|
+
These are shipped **examples the loop never executes**. To prevent doc-rot, their exact gate command is
|
|
349
|
+
mechanically extracted and run by [`../test/verifier.ci-snippet.test.js`](../test/verifier.ci-snippet.test.js):
|
|
350
|
+
the shipped command MUST exit `0` on a good release and `3` on a tampered one. A snippet a partner copies
|
|
351
|
+
is therefore known-good, not aspirational — the gate that converts a one-off pilot into a wired-in
|
|
352
|
+
renewal.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## 5. Why you can trust the verifier itself (proven, not promised)
|
|
357
|
+
|
|
358
|
+
Independence is enforced by [`../test/verifier.isolation.test.js`](../test/verifier.isolation.test.js),
|
|
359
|
+
which is part of the standard `npx hardhat test` suite:
|
|
360
|
+
|
|
361
|
+
- **No producer stack / no back-edge.** It statically greps **every** `require(` across the whole
|
|
362
|
+
`verifier/` tree and asserts none resolves to `ethers`, `hardhat`, `@nomicfoundation/*`, or anything
|
|
363
|
+
under `cli/` or `trustledger/`. The only runtime dependency is `js-sha3`.
|
|
364
|
+
- **No network.** It runs a real verify and asserts the process opens **no socket or network handle**,
|
|
365
|
+
and that the source never `require`s `http`/`https`/`net`/`dns`/`tls`. The tool cannot phone home —
|
|
366
|
+
it has nothing to phone home with.
|
|
367
|
+
- **Read-only.** Holds no key, writes nothing, leaves the working tree untouched
|
|
368
|
+
([`../test/verifier.cli.test.js`](../test/verifier.cli.test.js)).
|
|
369
|
+
- **No silent crypto drift.** The vendored secp256k1 recovery is cross-checked against the production
|
|
370
|
+
`ethers` recovery ([`../test/verifier.crypto.test.js`](../test/verifier.crypto.test.js)).
|
|
371
|
+
|
|
372
|
+
This is what lets a counterparty audit the verifier in an afternoon and then rely on its verdict
|
|
373
|
+
without relying on us.
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
<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>
|