verifyhash 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +883 -0
  3. package/cli/abi/ContributionRegistry.json +881 -0
  4. package/cli/agent.js +2173 -0
  5. package/cli/anchor-artifact.js +853 -0
  6. package/cli/anchor.js +400 -0
  7. package/cli/claim.js +881 -0
  8. package/cli/core/agent-commit.js +448 -0
  9. package/cli/core/agent-session.js +598 -0
  10. package/cli/core/anchor-binding.js +663 -0
  11. package/cli/core/attestation.js +580 -0
  12. package/cli/core/evidence-plans.js +495 -0
  13. package/cli/core/fixtures/evidence-plans/baseline.json +19 -0
  14. package/cli/core/fulfill-intake.js +1082 -0
  15. package/cli/core/go-live-preflight.js +481 -0
  16. package/cli/core/license.js +534 -0
  17. package/cli/core/manifest.js +243 -0
  18. package/cli/core/packetseal.js +591 -0
  19. package/cli/core/registryArtifact.js +49 -0
  20. package/cli/core/revocation.js +539 -0
  21. package/cli/core/rfc3161.js +389 -0
  22. package/cli/core/timestamp.js +482 -0
  23. package/cli/core/trust-asof.js +479 -0
  24. package/cli/dataset.js +2950 -0
  25. package/cli/evidence.js +2227 -0
  26. package/cli/fulfill-webhook-http.js +438 -0
  27. package/cli/git.js +220 -0
  28. package/cli/hash.js +550 -0
  29. package/cli/identity.js +1072 -0
  30. package/cli/journal-cli.js +1110 -0
  31. package/cli/journal-log.js +454 -0
  32. package/cli/journal.js +334 -0
  33. package/cli/lineage.js +447 -0
  34. package/cli/list.js +287 -0
  35. package/cli/parcel.js +1509 -0
  36. package/cli/proof.js +578 -0
  37. package/cli/prove.js +300 -0
  38. package/cli/receipt.js +631 -0
  39. package/cli/registry.js +331 -0
  40. package/cli/reputation.js +344 -0
  41. package/cli/revocation.js +495 -0
  42. package/cli/serve-verify-http.js +298 -0
  43. package/cli/serve-verify.js +333 -0
  44. package/cli/show.js +339 -0
  45. package/cli/verify.js +383 -0
  46. package/cli/vh.js +3927 -0
  47. package/docs/ADOPT.md +183 -0
  48. package/docs/ADOPTION.json +11 -0
  49. package/docs/AGENTTRACE.md +247 -0
  50. package/docs/ANCHORING.md +167 -0
  51. package/docs/AUDIT.md +55 -0
  52. package/docs/CONFORMANCE.md +107 -0
  53. package/docs/DATALEDGER.md +638 -0
  54. package/docs/DECIDE.md +47 -0
  55. package/docs/DECISIONS-PENDING.md +27 -0
  56. package/docs/DEPLOY-PUBLIC-SITE.md +301 -0
  57. package/docs/ENGINE-LEDGER.json +12 -0
  58. package/docs/EVIDENCE.md +519 -0
  59. package/docs/GO-LIVE.md +66 -0
  60. package/docs/IDENTITY.md +123 -0
  61. package/docs/INDEPENDENT-VERIFICATION.md +377 -0
  62. package/docs/INTEGRITY-JOURNAL.md +337 -0
  63. package/docs/KEY-LIFECYCLE.md +179 -0
  64. package/docs/LICENSING.md +46 -0
  65. package/docs/LINEAGE.md +307 -0
  66. package/docs/LOOP-AUDIT-2026-07-03.json +580 -0
  67. package/docs/LOOP-HARDENING-PLAN.md +44 -0
  68. package/docs/MERKLE-LEAVES.md +113 -0
  69. package/docs/METRICS.jsonl +31 -0
  70. package/docs/MORNING.md +204 -0
  71. package/docs/PILOT.md +444 -0
  72. package/docs/PROOFPARCEL.md +227 -0
  73. package/docs/PROOFS.md +262 -0
  74. package/docs/RECEIPTS.md +341 -0
  75. package/docs/REPUTATION.md +158 -0
  76. package/docs/SDK.md +301 -0
  77. package/docs/STRATEGY-ARCHIVE.md +5055 -0
  78. package/docs/SUPERVISOR-RUNBOOK.md +52 -0
  79. package/docs/TRUST-BOUNDARIES.md +335 -0
  80. package/docs/TRUSTLEDGER.md +1976 -0
  81. package/docs/USAGE-BUDGET.json +121 -0
  82. package/docs/VERIFY-SERVICE.md +168 -0
  83. package/index.js +160 -0
  84. package/package.json +41 -0
  85. package/trustledger/build-standalone.js +796 -0
  86. package/trustledger/cli.js +3179 -0
  87. package/trustledger/close.js +391 -0
  88. package/trustledger/corpus.js +159 -0
  89. package/trustledger/dist/BUILD-PROVENANCE.json +99 -0
  90. package/trustledger/dist/trustledger-standalone.html +6197 -0
  91. package/trustledger/dist/trustledger-standalone.html.sha256 +1 -0
  92. package/trustledger/door-core.js +442 -0
  93. package/trustledger/fixtures/bank.csv +7 -0
  94. package/trustledger/fixtures/bank.malformed.csv +3 -0
  95. package/trustledger/fixtures/bank.noalias.csv +5 -0
  96. package/trustledger/fixtures/bank.ofx +34 -0
  97. package/trustledger/fixtures/bank.real.csv +5 -0
  98. package/trustledger/fixtures/corpus/_shared/prior-close.json +22 -0
  99. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/inputs.json +14 -0
  100. package/trustledger/fixtures/corpus/bank-book-mismatch--benign-twin/meta.json +7 -0
  101. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/inputs.json +14 -0
  102. package/trustledger/fixtures/corpus/bank-book-mismatch--out-of-trust/meta.json +7 -0
  103. package/trustledger/fixtures/corpus/continuity-break--benign-twin/inputs.json +15 -0
  104. package/trustledger/fixtures/corpus/continuity-break--benign-twin/meta.json +7 -0
  105. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/inputs.json +15 -0
  106. package/trustledger/fixtures/corpus/continuity-break--out-of-trust/meta.json +7 -0
  107. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/inputs.json +13 -0
  108. package/trustledger/fixtures/corpus/negative-tenant-ledger--benign-twin/meta.json +7 -0
  109. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/inputs.json +13 -0
  110. package/trustledger/fixtures/corpus/negative-tenant-ledger--out-of-trust/meta.json +7 -0
  111. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/inputs.json +15 -0
  112. package/trustledger/fixtures/corpus/owner-overdraw--benign-twin/meta.json +7 -0
  113. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/inputs.json +15 -0
  114. package/trustledger/fixtures/corpus/owner-overdraw--out-of-trust/meta.json +7 -0
  115. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/inputs.json +16 -0
  116. package/trustledger/fixtures/corpus/security-deposit-segregation--benign-twin/meta.json +7 -0
  117. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/inputs.json +13 -0
  118. package/trustledger/fixtures/corpus/security-deposit-segregation--out-of-trust/meta.json +7 -0
  119. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/inputs.json +13 -0
  120. package/trustledger/fixtures/corpus/subledger-out-of-balance--benign-twin/meta.json +7 -0
  121. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/inputs.json +13 -0
  122. package/trustledger/fixtures/corpus/subledger-out-of-balance--out-of-trust/meta.json +7 -0
  123. package/trustledger/fixtures/e2e/bank.aliased.csv +4 -0
  124. package/trustledger/fixtures/e2e/bank.csv +4 -0
  125. package/trustledger/fixtures/e2e/bank.nsf.csv +4 -0
  126. package/trustledger/fixtures/e2e/quickbooks.csv +6 -0
  127. package/trustledger/fixtures/e2e/quickbooks.nsf.csv +8 -0
  128. package/trustledger/fixtures/e2e/rentroll.csv +6 -0
  129. package/trustledger/fixtures/e2e/rentroll.nsf.csv +8 -0
  130. package/trustledger/fixtures/e2e/rentroll.short.csv +5 -0
  131. package/trustledger/fixtures/plans/baseline.json +25 -0
  132. package/trustledger/fixtures/plans/price-binding.example.json +27 -0
  133. package/trustledger/fixtures/policy/ambiguous-deposit-example.json +12 -0
  134. package/trustledger/fixtures/policy/baseline.json +19 -0
  135. package/trustledger/fixtures/policy/ca-example.json +12 -0
  136. package/trustledger/fixtures/policy/negative-tenant-ledger-example.json +12 -0
  137. package/trustledger/fixtures/policy/owner-overdraw-example.json +12 -0
  138. package/trustledger/fixtures/quickbooks.csv +7 -0
  139. package/trustledger/fixtures/quickbooks.real.csv +5 -0
  140. package/trustledger/fixtures/rentroll.csv +6 -0
  141. package/trustledger/fixtures/rentroll.real.csv +4 -0
  142. package/trustledger/ingest.js +1163 -0
  143. package/trustledger/lib/policy-bundled-loader.js +44 -0
  144. package/trustledger/lib/sha256-vendored.js +227 -0
  145. package/trustledger/license.js +563 -0
  146. package/trustledger/match.js +551 -0
  147. package/trustledger/plans.js +551 -0
  148. package/trustledger/policy.js +398 -0
  149. package/trustledger/public/index.html +512 -0
  150. package/trustledger/reconcile.js +1486 -0
  151. package/trustledger/report.js +887 -0
  152. package/trustledger/seal.js +854 -0
  153. package/trustledger/server.js +391 -0
  154. package/trustledger/valueproof.js +350 -0
@@ -0,0 +1,121 @@
1
+ {
2
+ "_doc": "Loop spend governor. The /loop SUPERVISOR (not the engine) reads & enforces this at each relaunch boundary. A run is atomic once launched; cap is checked BEFORE launching the next. Tune any field freely.",
3
+ "windowDays": 7,
4
+ "windowStartIso": "2026-07-01T17:44:01Z",
5
+ "windowStartEpoch": 1782927841,
6
+ "windowResetIso": "2026-07-08T17:44:01Z",
7
+ "ceilingTokens": 120000000,
8
+ "cooldownSeconds": 7200,
9
+ "atCap": "pause-and-wait",
10
+ "estPerRunTokens": 5308720,
11
+ "spentTokens": 24638756,
12
+ "runs": [
13
+ {
14
+ "runId": "wf_7247856c-81c",
15
+ "tokens": 4337689,
16
+ "endEpoch": 1782332559,
17
+ "endIso": "2026-06-24T20:22:39Z"
18
+ },
19
+ {
20
+ "runId": "wf_231b8535-d68",
21
+ "tokens": 5308720,
22
+ "endEpoch": 1782354321,
23
+ "endIso": "2026-06-25T02:25:21Z"
24
+ },
25
+ {
26
+ "runId": "wf_1e4dd9f7-e36",
27
+ "tokens": 4570608,
28
+ "endEpoch": 1782374753,
29
+ "endIso": "2026-06-25T08:05:53Z"
30
+ },
31
+ {
32
+ "runId": "wf_574564f9-394",
33
+ "tokens": 4611799,
34
+ "endEpoch": 1782394073,
35
+ "endIso": "2026-06-25T13:27:53Z"
36
+ },
37
+ {
38
+ "runId": "wf_5df912fe-b52",
39
+ "tokens": 5395384,
40
+ "endEpoch": 1782418083,
41
+ "endIso": "2026-06-25T20:08:03Z"
42
+ },
43
+ {
44
+ "runId": "wf_4c16c5a7-142",
45
+ "tokens": 4717447,
46
+ "endEpoch": 1782435921,
47
+ "endIso": "2026-06-26T01:05:21Z"
48
+ },
49
+ {
50
+ "runId": "wf_c1d79f30-b27",
51
+ "tokens": 4789932,
52
+ "endEpoch": 1782454664,
53
+ "endIso": "2026-06-26T06:17:44Z"
54
+ },
55
+ {
56
+ "runId": "wf_63748963-3e7",
57
+ "tokens": 7723935,
58
+ "endEpoch": 1782482715,
59
+ "endIso": "2026-06-26T14:05:15Z"
60
+ },
61
+ {
62
+ "runId": "wf_6cd34703-102",
63
+ "tokens": 3913754,
64
+ "endEpoch": 1782502218,
65
+ "endIso": "2026-06-26T19:30:18Z"
66
+ },
67
+ {
68
+ "runId": "wf_eca980df-3d0",
69
+ "tokens": 12141525,
70
+ "tokensEstimated": true,
71
+ "note": "TaskStopped at user request (token-saving) on its 6th task; tokens estimated from transcript (output+input+cache_creation, excl. cache-reads)",
72
+ "endEpoch": 1782526595,
73
+ "endIso": "2026-06-27T02:16:35Z"
74
+ },
75
+ {
76
+ "runId": "wf_7265354b-ac9",
77
+ "tokens": 5660334,
78
+ "endEpoch": 1782890998,
79
+ "endIso": "2026-07-01T07:29:58Z",
80
+ "note": "8 tasks all VERIFIED (SDK-adoption arc T-56.2..T-59.1); Architect promoted engine #22 (usefulness min()->median); tokens authoritative from subagent_tokens"
81
+ },
82
+ {
83
+ "runId": "wf_5b304124-b3d",
84
+ "tokens": 2859078,
85
+ "endEpoch": 1782908347,
86
+ "endIso": "2026-07-01T12:19:07Z",
87
+ "note": "verify-as-a-service line: T-59.2 (serve-verify HTTP) + T-59.3 (CI drop-in) + T-60.1 (integrity-journal CORE) all VERIFIED/committed; T-60.2 (journal CLI verb) built+reviewed but its Critique panel, commit, and the run's Report/Manager/Architect phases FAILED on the subscription WEEKLY LIMIT ('resets 2pm UTC'). Engine unchanged (engineUpgrade none, md5 0bc9bbc9). tokens authoritative from subagent_tokens. endEpoch estimated (notification epoch lost across compaction; cooldown satisfied regardless)."
88
+ },
89
+ {
90
+ "runId": "wf_c9795182-acb",
91
+ "tokens": 6283496,
92
+ "endEpoch": 1782955200,
93
+ "endIso": "2026-07-02T01:20:00Z",
94
+ "note": "FINAL run before intentional loop STOP (user restarting session to enable Fable 5). ranTasks 6: VERIFIED T-60.3, T-61.3, T-62.1, T-62.2 (4); BLOCKED T-61.1 (auto-build x3) + T-63.1 (targeted 33/33 but full suite RED -> reverted, tree confirmed green 3314 passing). Strategist invented EPIC-61/62/63/64; EPIC-64 self-PARKED per qualityStall. engineUpgrade none; Manager/Reporter/Architect died on API 529 Overloaded so METRICS.jsonl line NOT written and these planning files were committed manually. tokens authoritative from subagent_tokens (agent_count 92, ~8.3h). WINDOW ROLLED at 2026-07-01T17:44:01Z: this is the only run in the new window, so spentTokens reset to 6283496; prior-window runs remain above for audit (pre-roll file in git history)."
95
+ },
96
+ {
97
+ "runId": "wf_07ba35e7-d72",
98
+ "tokens": 5192993,
99
+ "endEpoch": 1782981244,
100
+ "endIso": "2026-07-02T08:34:04Z",
101
+ "note": "FIRST FABLE-TRIAL run (engine #23): 8/8 VERIFIED, 0 blocked, ALL on attempt 1 — fableFirstShots 8/8 (100%) vs Opus baseline ~33%; avgUsefulness 3.88 (prior 3.38); converted both previously-BLOCKED tasks (T-61.1, T-63.1); completed EPIC-63 transparency log end-to-end; Strategist (Fable) invented EPIC-65 zero-install browser TrustLedger (T-65.1/2/3 shipped); Manager made 1 team change; Fable Architect shipped engine #24 (dissenter-only re-score = efficiency-audit finding #5), independently re-gated PASS+SMOKE-PASS, model pins intact. CHEAPER (5.19M vs 6.28M tokens) and FASTER (5.2h vs 8.3h) than the prior all-Opus run. 1 agent errored (absorbed). tokens authoritative from subagent_tokens."
102
+ },
103
+ {
104
+ "runId": "wf_1f634aeb-b9f",
105
+ "tokens": 7286045,
106
+ "endEpoch": 1783018585,
107
+ "endIso": "2026-07-02T18:56:25Z",
108
+ "note": "FABLE-TRIAL run 2 (engine #24): 8/8 VERIFIED, 0 blocked, ALL attempt 1 — fableFirstShots 8/8 (100%) AGAIN (cumulative 16/16 vs ~33% Opus baseline); avgUsefulness 4.13 / min 4 (best ever; 3.38->3.88->4.13); rework 4 with 0 reverted; 97/97 agents clean. Shipped: EPIC-66 browser verifier (in-memory seam + offline HTML page w/ 60s challenge, usefulness 5, + funnel wiring), EPIC-67 site-release assembler + drift visibility (--diff/--mark-deployed vs DEPLOYED.json), NEW Strategist-invented EPIC-68 AGENTTRACE (agent-session provenance: pure core + vh agent CLI w/ license-gated paid sign + zero-install verify). Manager: 2 team changes (TrustIntegrity AGENTTRACE clause). engineUpgrade none (md5 88bf8ee0 unchanged). tokens authoritative from subagent_tokens."
109
+ },
110
+ {
111
+ "runId": "wf_72ed879b-35c",
112
+ "tokens": 5876222,
113
+ "endEpoch": 1783054303,
114
+ "endIso": "2026-07-03T04:51:43Z",
115
+ "note": "FINAL run of the session; TaskStopped mid-8th-build by user to save weekly Fable usage (run was ~6.4h, unusually long). 7 tasks VERIFIED/committed (EPIC-69 T-69.1/2/3, EPIC-70 T-70.1/2/3 + one more); abandoned in-flight task git-stashed (recoverable). tokens AUTHORITATIVE from the workflow state json totalTokens (79 agents), NOT an estimate. Architect phase never reached, so NO engine swap this run (live md5 88bf8ee0 unchanged). Loop STOPPED after this per user; no relaunch until explicit go."
116
+ }
117
+ ],
118
+ "note": "Max $200 plan, weekly allowance >> 40M. Cooldown is the primary lever; ceiling is a safe backstop (pause-at-cap, so erring low is harmless). Raise ceilingTokens or lower cooldownSeconds to go faster.",
119
+ "lastRunEndEpoch": 1783054303,
120
+ "lastRunEndIso": "2026-07-03T04:51:43Z"
121
+ }
@@ -0,0 +1,168 @@
1
+ # verifyhash verify-service — the drop-in HTTP verify endpoint (`vh serve-verify`)
2
+
3
+ `vh serve-verify` stands the **verify** half of verifyhash up as a tiny, dependency-free HTTP service so a
4
+ CI pipeline or another microservice can **POST a seal and get an ACCEPT/REJECT** — the *"CI plugin that
5
+ imports rather than shells out"*. It is a **drop-in dependency**: boot it once, POST many seals, read the
6
+ JSON verdict, gate your build on the HTTP status. It reuses the **exact same** verify cores the `vh` CLI and
7
+ the `require("verifyhash")` SDK run (`verifySeal` / `verifySignedSeal`) — no fork, no second implementation.
8
+
9
+ This file is the **canonical reference** for the service's request schema, response fields, status mapping,
10
+ and trust boundary. It is **machine-checked**: `test/verify-service.example.test.js` byte-matches the request
11
+ `kind`s and response fields documented here against the live `cli/serve-verify.js › verifyRequest` core, so
12
+ this doc **cannot silently drift** from the code.
13
+
14
+ > **Booting it (a human deploy step).** The loop only BUILDS + locally TESTS. Exposing this service publicly
15
+ > (behind *your* nginx/Cloudflare, on *your* domain, with TLS) is an explicit **human** deploy step — see
16
+ > **STRATEGY.md P-9**. By default it binds **loopback** (`127.0.0.1`) and is never auto-deployed.
17
+
18
+ ---
19
+
20
+ ## Boot the service
21
+
22
+ ```bash
23
+ vh serve-verify [--port <n>] [--host <h>] [--max-body <bytes>]
24
+ # default: 127.0.0.1:4180 — loopback only, verify-only, Node-core http (ZERO new dependency)
25
+ ```
26
+
27
+ - `POST /verify` → a JSON verdict on a CI-mappable status.
28
+ - `GET /healthz` → `{ ok: true, ... }` (a liveness/readiness probe; holds no key, touches nothing).
29
+
30
+ The server is **verify-only**: it never signs, holds **no** private key, and writes **no** file.
31
+
32
+ ---
33
+
34
+ ## Request schema
35
+
36
+ Every request is a single JSON object with a `kind` field that selects one of the two verify paths. The
37
+ schema envelope version is **`vh.verify-request/1`** (the `schema` field of every response, below).
38
+
39
+ ### `kind: "verify-seal"` — UNSIGNED tamper-evidence
40
+
41
+ ```json
42
+ {
43
+ "kind": "verify-seal",
44
+ "seal": { "...": "a seal object OR its serialized JSON string" },
45
+ "entries": [
46
+ { "relPath": "dist/app.js", "content": "<encoded bytes>", "encoding": "base64" }
47
+ ]
48
+ }
49
+ ```
50
+
51
+ - `seal` — a seal **object** *or* its serialized JSON **string** (both accepted; strictly validated first).
52
+ - `entries[]` — the bytes to re-verify against the seal, each `{ relPath, content, encoding }` where
53
+ `encoding` is one of **`utf8`**, **`base64`**, or **`hex`** (default `utf8`). The service **re-derives**
54
+ the Merkle root from *these* bytes — never the seal's own stored hashes — so a one-byte tamper flips
55
+ `ACCEPTED` → `REJECTED`.
56
+
57
+ ### `kind: "verify-signed-seal"` — SIGNED / vendor-address-pinned
58
+
59
+ ```json
60
+ {
61
+ "kind": "verify-signed-seal",
62
+ "container": { "...": "a signed-seal container OR its JSON string" },
63
+ "expectedSigner": "0x<20-byte address>",
64
+ "entries": [
65
+ { "relPath": "dist/app.js", "content": "<encoded bytes>", "encoding": "base64" }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ - `container` — a signed-seal **object** *or* its JSON **string** (strictly validated first).
71
+ - `expectedSigner` *(optional)* — a `0x` address the recovered signer must equal (the vendor **pin**). A
72
+ genuine signature that recovers to a *different* address is a **REJECTED**, not an error.
73
+ - `entries[]` *(optional)* — when supplied, the canonical seal bytes are recomputed from these entries and
74
+ required byte-identical to the signed payload (a set that does not match is a clean **REJECTED**).
75
+
76
+ ---
77
+
78
+ ## Response schema
79
+
80
+ ### OK response (the request was evaluated)
81
+
82
+ ```json
83
+ {
84
+ "schema": "vh.verify-request/1",
85
+ "service": "vh-serve-verify",
86
+ "verdict": "ACCEPTED",
87
+ "kind": "verify-seal",
88
+ "detail": { "verdict": "ACCEPTED", "accepted": true, "...": "the core verdict, fields unchanged" }
89
+ }
90
+ ```
91
+
92
+ - `schema` — the envelope version, always **`vh.verify-request/1`**.
93
+ - `service` — always **`vh-serve-verify`**.
94
+ - `verdict` — the top-level answer: **`ACCEPTED`** or **`REJECTED`** (copied verbatim from `detail.verdict`,
95
+ never re-derived).
96
+ - `kind` — the request kind that was dispatched.
97
+ - `detail` — the **unchanged** core verdict from `verifySeal` / `verifySignedSeal`. Its fields are the same
98
+ contract the CLI and SDK already ship (e.g. `accepted`, `rootMatches`, `counts`, `changed` for a seal;
99
+ `recoveredSigner`, `checks`, `failedChecks` for a signed container).
100
+
101
+ ### ERROR response (the request could **not** be evaluated)
102
+
103
+ ```json
104
+ {
105
+ "schema": "vh.verify-request/1",
106
+ "service": "vh-serve-verify",
107
+ "verdict": "ERROR",
108
+ "code": "ERR_UNKNOWN_KIND",
109
+ "message": "a human-readable, non-sensitive reason"
110
+ }
111
+ ```
112
+
113
+ An `ERROR` is **fail-closed**: a malformed / oversized / unknown request is **never** a silent `ACCEPTED`.
114
+ `verdict` is **`ERROR`**, `code` is a stable machine-readable string, and `message` is a human reason. The
115
+ stable error `code`s are: `ERR_BODY_NOT_OBJECT`, `ERR_BODY_TOO_LARGE`, `ERR_UNKNOWN_KIND`,
116
+ `ERR_MISSING_SEAL`, `ERR_BAD_SEAL`, `ERR_BAD_ENTRIES`, `ERR_MISSING_CONTAINER`, `ERR_BAD_CONTAINER`,
117
+ `ERR_BAD_EXPECTED_SIGNER`, `ERR_INTERNAL`.
118
+
119
+ ---
120
+
121
+ ## Status mapping (CI-mappable — gate on the code alone)
122
+
123
+ | Verdict / condition | HTTP status | Meaning for a CI gate |
124
+ | -------------------------------------------- | ----------- | ---------------------------------------------- |
125
+ | `ACCEPTED` | **200** | the seal/container verified → **pass** |
126
+ | `REJECTED` | **422** | well-formed request that did NOT verify → fail |
127
+ | `ERROR` (malformed / unknown request) | **400** | the request itself is bad → fail |
128
+ | body over `--max-body` | **413** | payload too large → fail |
129
+ | wrong path / wrong method | **404/405** | routing error → fail |
130
+
131
+ A build should gate on **HTTP 200** exactly: anything else is a non-pass. The shipped
132
+ [`verifier/ci/verify-service.generic.sh`](../verifier/ci/verify-service.generic.sh) and
133
+ [`verifier/ci/verify-service.github-actions.yml`](../verifier/ci/verify-service.github-actions.yml) do
134
+ exactly this (curl POST → map the HTTP status to a non-zero exit on anything but 200).
135
+
136
+ ---
137
+
138
+ ## Trust boundary (the service will not let you overclaim)
139
+
140
+ A **200 ACCEPTED** for a seal means **tamper-evidence**: *these exact bytes re-derive the sealed Merkle
141
+ root.* A valid **signature** (`verify-signed-seal`) additionally proves **who vouched** — the holder of the
142
+ pinned address's key — for those bytes. Neither of these:
143
+
144
+ - is a **trusted timestamp** ("sealed / signed since date *T*") — that rides the **human-owned** signing /
145
+ timestamp / anchor trust-root (`needs-human`; **STRATEGY.md P-3**);
146
+ - is a **legal opinion**.
147
+
148
+ The service is **verify-only**: it never signs, holds **no** private key, and writes **no** file. It binds
149
+ **loopback** (`127.0.0.1`) by default; exposing it publicly is a **human** deploy step (your nginx /
150
+ Cloudflare / domain / TLS) — **never** auto-deployed. See [`docs/TRUST-BOUNDARIES.md`](./TRUST-BOUNDARIES.md).
151
+
152
+ ---
153
+
154
+ ## Drop it into your pipeline
155
+
156
+ - **Any shell CI** (GitLab, CircleCI, Jenkins, Makefile): boot the service, then run
157
+ [`verifier/ci/verify-service.generic.sh`](../verifier/ci/verify-service.generic.sh) with
158
+ `VH_VERIFY_URL` + `VH_REQUEST` (the path to a prepared request-body JSON).
159
+ - **GitHub Actions**: copy
160
+ [`verifier/ci/verify-service.github-actions.yml`](../verifier/ci/verify-service.github-actions.yml) — it
161
+ boots `vh serve-verify`, builds a request body with the SDK, POSTs it, and fails the job on anything but
162
+ 200.
163
+ - **A tiny dependency-free client**: [`examples/verify-service-client.js`](../examples/verify-service-client.js)
164
+ boots the service, POSTs a clean seal (ACCEPT) then a tampered one (REJECT), and exits 0 — copy it as your
165
+ in-process integration. It imports **only** `require("verifyhash")`, the `vh` command, and Node built-ins.
166
+
167
+ This doc, the example, and the CI scripts are all test-gated by `test/verify-service.example.test.js` on
168
+ every `npx hardhat test`, so they can never silently rot.
package/index.js ADDED
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+
3
+ // index.js — the SINGLE, documented, semver-guarded PUBLIC API entrypoint for verifyhash (T-57.1).
4
+ //
5
+ // WHAT THIS IS
6
+ // A THIN re-export of the already-built, already-tested core so a downstream program can `require`
7
+ // verifyhash as a library and get the EXACT SAME functions the `vh` CLI runs — no fork, no second
8
+ // implementation, no new crypto or mechanism introduced here. Every symbol below is the SAME function
9
+ // object exported by its `cli/…` source module (an identity re-export, asserted by the tests). If the
10
+ // CLI's behavior changes, this API changes with it automatically, because it IS the CLI's code.
11
+ //
12
+ // The embedded ("SDK") path and the CLI path are therefore the same code path: a seal built via
13
+ // `sdk.buildSeal(...)` verifies (ACCEPT) with `sdk.verifySeal(...)`, and a one-byte tamper is REJECTED
14
+ // — identical to `vh evidence seal` / `vh evidence verify`. The SAME holds for the SIGNED / vendor-pinned
15
+ // path: a seal signed via `sdk.signSealWith(...)` verifies (ACCEPT) with `sdk.verifySignedSeal(...)` under
16
+ // the matching expected signer, and a WRONG expected signer or a one-byte-tampered container is REJECTED
17
+ // — byte-identical to `vh evidence verify-signed`. So an embedder can verify a SIGNED, address-pinned
18
+ // packet in-process, with no shell-out to the `vh` binary.
19
+ //
20
+ // TRUST BOUNDARY (unchanged from the CLI — this wrapper adds nothing)
21
+ // A seal proves TAMPER-EVIDENCE + OFFLINE RE-COMPUTE ("these exact bytes are what was sealed"), NOT a
22
+ // trusted timestamp and NOT who authored the bytes. Signing/timestamping still ride the human-owned
23
+ // trust-root. `verifySeal` RE-DERIVES the Merkle root from the bytes YOU supply — never the seal's own
24
+ // stored hashes. See docs/TRUST-BOUNDARIES.md.
25
+ //
26
+ // STABILITY / SEMVER
27
+ // This module is the package's stability contract. `apiVersion` mirrors `package.json`'s version and
28
+ // is the semver-guarded surface: anything re-exported here is a PUBLIC symbol whose removal or breaking
29
+ // change requires a semver-major bump; symbols NOT re-exported here (deep `cli/…` internals) carry no
30
+ // stability guarantee. Add to this surface deliberately.
31
+
32
+ const pkg = require("./package.json");
33
+
34
+ // ---- Source modules (the already-built core; NOT re-implemented here) -------------------------------
35
+ // The seal SDK is bound to a REAL product config (`vh evidence`) over the generic packetseal core, so
36
+ // the embedded path is byte-identical to the CLI seal path.
37
+ const evidence = require("./cli/evidence");
38
+ const receipt = require("./cli/receipt");
39
+ const packetseal = require("./cli/core/packetseal");
40
+ const attestation = require("./cli/core/attestation");
41
+ const hash = require("./cli/hash");
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // THE PUBLIC SURFACE — each value is the SAME function object as its `cli/…` source (identity re-export).
45
+ // Grouped for humans; also spread flat at the top level for convenience.
46
+ // ---------------------------------------------------------------------------
47
+
48
+ // (1) Seal SDK — build / verify a tamper-evident evidence seal from a flat { relPath, bytes } entry list.
49
+ // buildSeal(entries) -> seal object
50
+ // validateSeal(seal) -> throws on structural / root-mismatch problems
51
+ // serializeSeal(seal) -> canonical, byte-deterministic JSON (newline-terminated)
52
+ // readSeal(jsonOrObject) -> parsed + strictly validated seal
53
+ // verifySeal(seal, entries) -> { verdict: "ACCEPTED" | "REJECTED", accepted, ... } (RE-DERIVES root)
54
+ const seal = Object.freeze({
55
+ KIND: evidence.SEAL_KIND,
56
+ SCHEMA_VERSION: evidence.SEAL_SCHEMA_VERSION,
57
+ TRUST_NOTE: evidence.EVIDENCE_TRUST_NOTE,
58
+ buildSeal: evidence.buildSeal,
59
+ validateSeal: evidence.validateSeal,
60
+ serializeSeal: evidence.serializeSeal,
61
+ readSeal: evidence.readSeal,
62
+ verifySeal: evidence.verifySeal,
63
+ // The generic, product-agnostic seal core the above are bound to (advanced / custom products).
64
+ PacketSealError: packetseal.PacketSealError,
65
+ });
66
+
67
+ // (1b) SIGNED / vendor-pinned verify SDK — the embedded twin of `vh evidence verify-signed`. This is the
68
+ // SAME code the CLI runs (identity re-exports of the already-green, already-CLI-shipped functions),
69
+ // so a signed packet a buyer verifies in-process is byte-identical to the `vh evidence verify-signed`
70
+ // path — no shelling out to the binary to verify a SIGNED, vendor-address-pinned seal.
71
+ //
72
+ // signSealWith(seal, signer) -> a signed-seal container (WRAPS the canonical seal bytes)
73
+ // validateSignedSeal(container) -> strict structural validation of a signed container
74
+ // verifySignedSeal({container,expectedSigner,expectedCanonical})
75
+ // -> the PURE core verdict (recover signer; optional pin/bind)
76
+ // verifySignedSealAttestation({container,expectedSigner,dir})
77
+ // -> the strict signed-verify the CLI runs (--signer / --dir)
78
+ // recoverSigner(container) -> the address the signature recovers to (offline, key-free)
79
+ // verifySignedAttestation(params) -> the generic, product-agnostic signed-attestation verifier
80
+ //
81
+ // TRUST BOUNDARY: a valid signature proves WHO vouched (the holder of `signer`'s key) for THIS sealed
82
+ // packet — NOT a trusted timestamp ("signed since T" rides the human-owned trust-root, STRATEGY.md P-3)
83
+ // and NOT a legal opinion. Verification is OFFLINE / key-free: it recovers a PUBLIC address, holds no
84
+ // private key, and contacts nothing.
85
+ const signed = Object.freeze({
86
+ KIND: evidence.SIGNED_SEAL_KIND,
87
+ TRUST_NOTE: evidence.VERIFY_SIGNED_SEAL_TRUST_NOTE,
88
+ signSealWith: evidence.signSealWith,
89
+ validateSignedSeal: evidence.validateSignedSeal,
90
+ verifySignedSeal: evidence.verifySignedSeal,
91
+ verifySignedSealAttestation: evidence.verifySignedSealAttestation,
92
+ // The generic, product-agnostic signed-attestation core the evidence path is bound to.
93
+ recoverSigner: attestation.recoverSigner,
94
+ verifySignedAttestation: attestation.verifySignedAttestation,
95
+ });
96
+
97
+ // (2) Receipts — the anchor/claim receipt codec + the path-bound manifest diff.
98
+ const receipts = Object.freeze({
99
+ SCHEMA_VERSION: receipt.SCHEMA_VERSION,
100
+ CLAIM_RECEIPT_KIND: receipt.CLAIM_RECEIPT_KIND,
101
+ ANCHOR_RECEIPT_KIND: receipt.ANCHOR_RECEIPT_KIND,
102
+ buildReceipt: receipt.buildReceipt,
103
+ buildAnchorReceipt: receipt.buildAnchorReceipt,
104
+ writeReceipt: receipt.writeReceipt,
105
+ readReceipt: receipt.readReceipt,
106
+ diffManifest: receipt.diffManifest,
107
+ });
108
+
109
+ // (3) Hashing primitives — the SAME keccak/Merkle helpers every seal + receipt is built on.
110
+ const hashing = Object.freeze({
111
+ hashBytes: hash.hashBytes,
112
+ hashFile: hash.hashFile,
113
+ hashEntries: hash.hashEntries,
114
+ hashDir: hash.hashDir,
115
+ hashPath: hash.hashPath,
116
+ buildTree: hash.buildTree,
117
+ });
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Flat top-level export map. Every function property is the identity re-export of its source.
121
+ // ---------------------------------------------------------------------------
122
+ module.exports = Object.freeze({
123
+ // Semver-guarded version of THIS public surface (mirrors package.json).
124
+ apiVersion: pkg.version,
125
+
126
+ // Grouped namespaces.
127
+ seal,
128
+ signed,
129
+ receipts,
130
+ hashing,
131
+
132
+ // Flat convenience re-exports (identity with the grouped + source symbols).
133
+ // --- seal SDK ---
134
+ buildSeal: evidence.buildSeal,
135
+ validateSeal: evidence.validateSeal,
136
+ serializeSeal: evidence.serializeSeal,
137
+ readSeal: evidence.readSeal,
138
+ verifySeal: evidence.verifySeal,
139
+ PacketSealError: packetseal.PacketSealError,
140
+ // --- signed / vendor-pinned verify SDK (the embedded twin of `vh evidence verify-signed`) ---
141
+ signSealWith: evidence.signSealWith,
142
+ validateSignedSeal: evidence.validateSignedSeal,
143
+ verifySignedSeal: evidence.verifySignedSeal,
144
+ verifySignedSealAttestation: evidence.verifySignedSealAttestation,
145
+ recoverSigner: attestation.recoverSigner,
146
+ verifySignedAttestation: attestation.verifySignedAttestation,
147
+ // --- receipts ---
148
+ buildReceipt: receipt.buildReceipt,
149
+ buildAnchorReceipt: receipt.buildAnchorReceipt,
150
+ writeReceipt: receipt.writeReceipt,
151
+ readReceipt: receipt.readReceipt,
152
+ diffManifest: receipt.diffManifest,
153
+ // --- hashing ---
154
+ hashBytes: hash.hashBytes,
155
+ hashFile: hash.hashFile,
156
+ hashEntries: hash.hashEntries,
157
+ hashDir: hash.hashDir,
158
+ hashPath: hash.hashPath,
159
+ buildTree: hash.buildTree,
160
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "verifyhash",
3
+ "version": "0.1.0",
4
+ "description": "Tamper-evident, permissionless on-chain registry of code-contribution hashes, plus an offline data-provenance toolkit (the `vh` CLI).",
5
+ "license": "Apache-2.0",
6
+ "main": "index.js",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "bin": {
12
+ "vh": "cli/vh.js"
13
+ },
14
+ "files": [
15
+ "index.js",
16
+ "cli/",
17
+ "trustledger/",
18
+ "README.md",
19
+ "docs/"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "scripts": {
25
+ "build": "hardhat compile",
26
+ "test": "hardhat test",
27
+ "go-live": "node scripts/go-live-check.js",
28
+ "sdk:surface": "node scripts/gen-sdk-surface.cjs",
29
+ "node": "hardhat node",
30
+ "deploy:amoy": "hardhat run scripts/deploy.js --network amoy"
31
+ },
32
+ "dependencies": {
33
+ "ethers": "^6.17.0",
34
+ "js-sha3": "^0.8.0"
35
+ },
36
+ "devDependencies": {
37
+ "@nomicfoundation/hardhat-toolbox": "^5.0.0",
38
+ "hardhat": "^2.22.0",
39
+ "dotenv": "^16.4.5"
40
+ }
41
+ }