verifyhash 0.1.0 → 0.1.2

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 (64) hide show
  1. package/README.md +5 -3
  2. package/cli/agent-hook.js +431 -0
  3. package/docs/ADOPT.md +15 -5
  4. package/docs/AGENT-HOOK.md +111 -0
  5. package/docs/ANCHORING.md +43 -22
  6. package/docs/PUBLISH-VERIFY-VH.md +45 -0
  7. package/examples/README.md +185 -0
  8. package/examples/policy.lenient.json +5 -0
  9. package/examples/policy.strict.json +6 -0
  10. package/examples/run.js +366 -0
  11. package/examples/sample-dataset/README.txt +10 -0
  12. package/examples/sample-dataset/corpus/cc-by-poem.txt +8 -0
  13. package/examples/sample-dataset/corpus/mit-notes.txt +4 -0
  14. package/examples/sample-dataset/data/unlabeled.txt +5 -0
  15. package/examples/sample-dataset/vendored/gpl-snippet.txt +5 -0
  16. package/examples/sample-dataset.hints.json +7 -0
  17. package/examples/sample-parcel/data/manifest-of-contents.txt +7 -0
  18. package/examples/sample-parcel/data/records.csv +4 -0
  19. package/examples/sample-parcel/delivery-note.txt +9 -0
  20. package/package.json +26 -3
  21. package/verifier/README.md +584 -0
  22. package/verifier/action/README.md +87 -0
  23. package/verifier/action/action.yml +146 -0
  24. package/verifier/build-standalone-html.js +1287 -0
  25. package/verifier/build-standalone.js +989 -0
  26. package/verifier/ci/journal.generic.sh +96 -0
  27. package/verifier/ci/journal.github-actions.yml +99 -0
  28. package/verifier/ci/reproduce-vh.generic.sh +59 -0
  29. package/verifier/ci/reproduce-vh.github-actions.yml +49 -0
  30. package/verifier/ci/verify-service.generic.sh +96 -0
  31. package/verifier/ci/verify-service.github-actions.yml +88 -0
  32. package/verifier/ci/verify-vh.generic.sh +75 -0
  33. package/verifier/ci/verify-vh.github-actions.yml +56 -0
  34. package/verifier/dist/BUILD-PROVENANCE.json +210 -0
  35. package/verifier/dist/seal-vh-standalone.js +876 -0
  36. package/verifier/dist/seal-vh-standalone.js.sha256 +1 -0
  37. package/verifier/dist/verify-vh-standalone.html +3373 -0
  38. package/verifier/dist/verify-vh-standalone.html.sha256 +1 -0
  39. package/verifier/dist/verify-vh-standalone.js +5123 -0
  40. package/verifier/dist/verify-vh-standalone.js.sha256 +1 -0
  41. package/verifier/lib/canonical.js +141 -0
  42. package/verifier/lib/keccak.js +30 -0
  43. package/verifier/lib/keccak256-vendored.js +206 -0
  44. package/verifier/lib/merkle.js +145 -0
  45. package/verifier/lib/revocation-core.js +606 -0
  46. package/verifier/lib/revocation.js +200 -0
  47. package/verifier/lib/seal-cli.js +374 -0
  48. package/verifier/lib/seal-evidence.js +237 -0
  49. package/verifier/lib/secp256k1-recover.js +249 -0
  50. package/verifier/package.json +39 -0
  51. package/verifier/verify-vh.js +3376 -0
  52. package/docs/ADOPTION.json +0 -11
  53. package/docs/AUDIT.md +0 -55
  54. package/docs/DECIDE.md +0 -47
  55. package/docs/DECISIONS-PENDING.md +0 -27
  56. package/docs/DEPLOY-PUBLIC-SITE.md +0 -301
  57. package/docs/ENGINE-LEDGER.json +0 -12
  58. package/docs/LOOP-AUDIT-2026-07-03.json +0 -580
  59. package/docs/LOOP-HARDENING-PLAN.md +0 -44
  60. package/docs/METRICS.jsonl +0 -31
  61. package/docs/MORNING.md +0 -204
  62. package/docs/STRATEGY-ARCHIVE.md +0 -5055
  63. package/docs/SUPERVISOR-RUNBOOK.md +0 -52
  64. package/docs/USAGE-BUDGET.json +0 -121
@@ -0,0 +1,87 @@
1
+ # verify-vh — sealed-artifact merge gate (GitHub Action)
2
+
3
+ A composite GitHub Action that **fails your build the instant a sealed verifyhash artifact is tampered,
4
+ forged, or signed by the wrong key.** It installs ONLY the standalone, offline `verify-vh` verifier
5
+ (`js-sha3` — no ethers, no hardhat, no network) and runs it over your release artifact(s).
6
+
7
+ **What a green check means depends on `vendor:`:**
8
+
9
+ - With **`vendor:` set** — a green check means *"every sealed artifact still matches the bytes the key
10
+ you pinned signed."* An artifact signed by any other key is **REJECTED** (exit 3).
11
+ - With **`vendor:` omitted** — a green check means **tamper-evidence ONLY**: the bytes match the seal,
12
+ but it does **NOT** prove **WHO** signed. See the security warning below before omitting `vendor:`.
13
+
14
+ > ⚠️ **Pin `vendor:` for any SIGNED release.** A signed artifact gated **without** `vendor:` is accepted
15
+ > as long as its signature recovers to the signer the packet **self-asserts** — so an attacker who
16
+ > re-signs a tampered release with **their own key** passes this gate. `vendor:` is what makes the green
17
+ > check mean "signed by the producer **I** pinned" rather than "signed by **whoever** built this packet."
18
+ > Leave `vendor:` empty **only** for genuinely unsigned evidence seals (where there is no signer to pin).
19
+
20
+ Adoption is **one line**:
21
+
22
+ ```yaml
23
+ # .github/workflows/verify-vh.yml
24
+ name: verify-vh merge gate
25
+ on: [push, pull_request]
26
+ jobs:
27
+ verify-vh:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: verifyhash/verifyhash/verifier/action@17696eff5d910b496b8935052ff42ee2e7c6a85a
32
+ with:
33
+ vendor: "0xYOUR_PRODUCER_SIGNER_ADDRESS" # the key that must have signed (omit to check tamper only)
34
+ manifest: "release.manifest" # OR set `artifacts:` instead
35
+ ```
36
+
37
+ > The `uses:` line is pre-pinned to this repository's real slug (`verifyhash/verifyhash`) and a full
38
+ > 40-hex commit SHA reachable from `main` — it works exactly as pasted. Supply-chain hygiene: re-pin
39
+ > `@<sha>` to a commit SHA **you** have audited and trust (keep the full-SHA form; a mutable ref like
40
+ > `@main` can change under you). GitHub fetches this action's own tree when you reference it with
41
+ > `uses:`, and the action resolves the bundled `verifier/` via `${{ github.action_path }}` at run time
42
+ > (it does NOT run from your `$GITHUB_WORKSPACE`). So you do **not** need to vendor `verifier/` into your
43
+ > repo — your `actions/checkout` brings only your artifacts, not the verifier.
44
+
45
+ ## Inputs
46
+
47
+ | input | required | default | what it is |
48
+ |----------------|----------|-----------------------------|------------|
49
+ | `vendor` | no | `""` | The producer's signer address (`0x` + 20 bytes), obtained out-of-band, that **every signed artifact must verify against**. When set, an artifact signed by any other key is **REJECTED** (exit 3). Leave empty to verify tamper-evidence only (e.g. an unsigned evidence seal). **⚠️ Leaving this empty on a SIGNED artifact accepts an attacker-re-signed release — pin it for any signed release** (see warning above). |
50
+ | `manifest` | no | `""` | Path to a release manifest (newline list or JSON array of artifact paths, each entry may carry its own `--vendor`/`--dir`) that gates the **whole release in one invocation**. Set this **OR** `artifacts`. |
51
+ | `artifacts` | no | `""` | Space-separated artifact path(s) when no `manifest` is given (e.g. `"dist/a.vhevidence.json dist/b.vhseal"`). Set this **OR** `manifest`. |
52
+ | `dir` | no | `""` | Directory holding the files the artifact(s) reference (the sealed packet). Defaults to each artifact's own directory (sibling resolution). |
53
+ | `verify-vh` | no | _(action's bundled tree)_ | Path to the standalone `verify-vh.js` (advanced/testing override). Defaults to the verifier tree **bundled with this action**, resolved at run time via `${{ github.action_path }}` — so you do **not** vendor `verifier/` into your repo. |
54
+ | `node-version` | no | `"20"` | Node.js version to set up for the verifier (`>= 18`). |
55
+
56
+ ## Exit-code contract (the job status is meaningful)
57
+
58
+ The action propagates `verify-vh`'s own exit code, so any non-zero verdict fails the job and **blocks the merge**:
59
+
60
+ | exit | meaning | gate result |
61
+ |------|--------------|-------------|
62
+ | `0` | OK | every artifact verified — allow the merge |
63
+ | `3` | REJECTED | an artifact was tampered/forged/wrong-issuer — **block** (the report names which) |
64
+ | `2` | USAGE | misconfiguration (no `manifest`/`artifacts`, bad flag/address) |
65
+ | `1` | IO | an artifact or the manifest could not be read — never reported as "passed" |
66
+
67
+ ## Single source of truth — no drift
68
+
69
+ The verifier-invocation the gate step runs is **byte-identical** to the one shipped in
70
+ [`verifier/ci/verify-vh.generic.sh`](../ci/verify-vh.generic.sh) (the portable shell gate for any CI).
71
+ A test (`test/verifier.action.test.js`) parses this `action.yml`, extracts the gate `run:` block, runs
72
+ it over the committed sample sealed packet (asserting exit `0`) and over a one-byte-tampered copy
73
+ (asserting exit `3`), and asserts that invocation has not drifted from `verify-vh.generic.sh`. So this
74
+ Action, the generic shell gate, and the GitHub Actions YAML example all run the **same** gate.
75
+
76
+ ## What it installs (and what it does NOT)
77
+
78
+ The install step runs `npm ci --omit=dev || npm install --omit=dev` inside `verifier/`, whose
79
+ `package.json` declares exactly one runtime dependency — `js-sha3`. It pulls **no** `ethers`, **no**
80
+ `hardhat`, **no** `@nomicfoundation`, and opens no network beyond the registry fetch. The verifier is
81
+ read-only: it holds no key and writes nothing.
82
+
83
+ ## Related
84
+
85
+ - [`verifier/ci/verify-vh.generic.sh`](../ci/verify-vh.generic.sh) — the same gate as a portable `set -e` shell snippet (GitLab CI / Makefile / git hook / cron box), configured by `VH_*` env vars.
86
+ - [`verifier/ci/verify-vh.github-actions.yml`](../ci/verify-vh.github-actions.yml) — a hand-rolled GitHub Actions **workflow** example (this Action is the one-line replacement for it).
87
+ - [`verifier/README.md`](../README.md) — the standalone verifier itself: how to vendor or `npm install` just this tree and audit it in an afternoon.
@@ -0,0 +1,146 @@
1
+ name: "verify-vh — sealed-artifact merge gate"
2
+ description: >-
3
+ Fail the build the instant a sealed verifyhash artifact is tampered, forged, or signed by the wrong
4
+ key. This composite action installs ONLY the standalone, offline verify-vh verifier (js-sha3 — no
5
+ ethers, no hardhat, no network) and runs it over your release artifact(s). With `vendor:` set, a green
6
+ check means "every sealed artifact still matches the bytes the key you pinned signed"; with `vendor:`
7
+ omitted it is tamper-evidence ONLY and does NOT prove WHO signed. Adoption is one line:
8
+ `uses: verifyhash/verifyhash/verifier/action@17696eff5d910b496b8935052ff42ee2e7c6a85a` (re-pin to a
9
+ full-40-hex commit SHA you trust).
10
+ author: "verifyhash"
11
+
12
+ # Marketplace branding (shown on the GitHub Marketplace listing).
13
+ branding:
14
+ icon: "shield"
15
+ color: "green"
16
+
17
+ # ---------------------------------------------------------------------------------------------------
18
+ # INPUTS — the three knobs a consumer sets. `vendor` pins WHO must have signed (omit for an unsigned
19
+ # evidence seal, which is checked for tamper-evidence only). Provide EITHER `manifest` (gate a whole
20
+ # release in one shot) OR `artifacts` (a space-separated list); `dir` resolves the referenced files.
21
+ # ---------------------------------------------------------------------------------------------------
22
+ inputs:
23
+ vendor:
24
+ description: >-
25
+ The producer's signer address (0x + 20 bytes), obtained out-of-band, that EVERY signed artifact
26
+ must verify against. Leave empty to verify tamper-evidence only (e.g. an unsigned evidence seal);
27
+ when set, an artifact signed by any other key is REJECTED (exit 3). SECURITY: gating a SIGNED
28
+ release artifact WITHOUT `vendor:` accepts an attacker who re-signed a tampered release with their
29
+ OWN key — a green check then proves only that the bytes match WHATEVER signer the packet
30
+ self-asserts, NOT who. ALWAYS pin `vendor:` for any signed release.
31
+ required: false
32
+ default: ""
33
+ manifest:
34
+ description: >-
35
+ Path to a release manifest (a newline list or JSON array of artifact paths, each entry may carry
36
+ its own --vendor/--dir) that gates the WHOLE release in one invocation. Set this OR `artifacts`.
37
+ required: false
38
+ default: ""
39
+ artifacts:
40
+ description: >-
41
+ Space-separated artifact path(s) to verify when no `manifest` is given (e.g.
42
+ "dist/a.vhevidence.json dist/b.vhseal"). Set this OR `manifest`.
43
+ required: false
44
+ default: ""
45
+ dir:
46
+ description: >-
47
+ Directory holding the files the artifact(s) reference (the sealed packet). Optional; defaults to
48
+ each artifact's own directory (sibling resolution).
49
+ required: false
50
+ default: ""
51
+ verify-vh:
52
+ description: >-
53
+ Path to the standalone verify-vh.js (advanced/testing override). Defaults to the verifier tree
54
+ BUNDLED WITH this action — resolved at run time via ${{ github.action_path }}, so the consumer
55
+ NEVER has to vendor `verifier/` into their own repo. Leave empty to use the bundled verifier.
56
+ required: false
57
+ default: ""
58
+ node-version:
59
+ description: "Node.js version to set up for the offline verifier (>= 18)."
60
+ required: false
61
+ default: "20"
62
+
63
+ # ---------------------------------------------------------------------------------------------------
64
+ # COMPOSITE STEPS — ordered: set up Node, install ONLY the standalone verifier (js-sha3), then run the
65
+ # gate. The action fetches/installs the verifier itself; the consumer NEVER has to vendor the tree.
66
+ #
67
+ # PATH RESOLUTION (load-bearing for a published action): a composite action's `run:` steps execute with
68
+ # the CONSUMER's $GITHUB_WORKSPACE as the working directory, NOT the action's own checkout. So every
69
+ # reference to the bundled verifier tree is resolved against ${{ github.action_path }} (the directory of
70
+ # THIS action, i.e. verifier/action/) — its parent ${{ github.action_path }}/.. is the verifier/ tree.
71
+ # Without this, `working-directory: verifier` and `./verifier/verify-vh.js` would resolve against the
72
+ # consumer's repo (which has no verifier/ tree) and the action would fail to even start the verifier.
73
+ # ---------------------------------------------------------------------------------------------------
74
+ runs:
75
+ using: "composite"
76
+ steps:
77
+ # 1. Node for the offline verifier (no producer toolchain).
78
+ - name: Set up Node.js
79
+ uses: actions/setup-node@v4
80
+ with:
81
+ node-version: ${{ inputs.node-version }}
82
+
83
+ # 2. Install ONLY the standalone verifier — pulls js-sha3 and NOTHING else (no ethers, no hardhat,
84
+ # no @nomicfoundation, no network beyond the registry fetch of js-sha3). Runs in the action's OWN
85
+ # bundled verifier tree (${{ github.action_path }}/.. == verifier/), NOT the consumer's workspace.
86
+ - name: Install standalone verify-vh (js-sha3 only)
87
+ shell: bash
88
+ working-directory: ${{ github.action_path }}/..
89
+ run: npm ci --omit=dev || npm install --omit=dev
90
+
91
+ # 3. THE GATE. One invocation, one exit code. The verifier-invocation line below is BYTE-IDENTICAL
92
+ # to the one in verifier/ci/verify-vh.generic.sh (the single source of truth — a test asserts
93
+ # no drift). verify-vh exits 0 only if EVERY artifact verifies; 3 if any is tampered/forged/
94
+ # wrong-issuer; 2 on misconfig; 1 on IO. We propagate that exit code so GitHub Actions fails the
95
+ # job on any non-zero — blocking the merge.
96
+ #
97
+ # VERIFY_VH defaults to the action's OWN bundled verify-vh.js, resolved via ${{ github.action_path }}
98
+ # so it points at the verifier tree this action shipped — NOT a path in the consumer's workspace.
99
+ - name: Verify sealed artifacts (fails the build on a bad seal)
100
+ shell: bash
101
+ env:
102
+ VERIFY_VH: ${{ inputs.verify-vh != '' && inputs.verify-vh || format('{0}/../verify-vh.js', github.action_path) }}
103
+ VH_VENDOR: ${{ inputs.vendor }}
104
+ VH_MANIFEST: ${{ inputs.manifest }}
105
+ VH_ARTIFACTS: ${{ inputs.artifacts }}
106
+ VH_DIR: ${{ inputs.dir }}
107
+ run: |
108
+ set -euo pipefail
109
+ VERIFY_VH="${VERIFY_VH:-./verifier/verify-vh.js}"
110
+
111
+ # Build the verify-vh argument list. A MANIFEST gates the whole release in one invocation;
112
+ # otherwise we pass the artifact list positionally. Either way it is ONE call -> ONE exit code.
113
+ set -- # reset positional params we will hand to verify-vh
114
+ if [ -n "${VH_MANIFEST:-}" ]; then
115
+ set -- --manifest "$VH_MANIFEST"
116
+ else
117
+ if [ -z "${VH_ARTIFACTS:-}" ]; then
118
+ echo "verify-vh gate: set 'manifest' or 'artifacts' (the artifact(s) to verify)." >&2
119
+ exit 2
120
+ fi
121
+ # shellcheck disable=SC2086 # word-splitting VH_ARTIFACTS into separate args is intentional.
122
+ set -- $VH_ARTIFACTS
123
+ fi
124
+
125
+ # `vendor` is OPTIONAL: pin the signer only when one is supplied (an unsigned evidence seal has
126
+ # no signer to pin, so verifying tamper-evidence alone must NOT force a vendor). SECURITY NOTE:
127
+ # without --vendor a SIGNED artifact is accepted on its OWN self-claimed signer — pin the
128
+ # producer's signer for any signed release (see the README warning + the `vendor` input doc).
129
+ if [ -n "${VH_VENDOR:-}" ]; then
130
+ set -- "$@" --vendor "$VH_VENDOR"
131
+ fi
132
+ if [ -n "${VH_DIR:-}" ]; then
133
+ set -- "$@" --dir "$VH_DIR"
134
+ fi
135
+
136
+ # Run the gate. `set -e` would abort on the non-zero exit before we could echo a clear message, so we
137
+ # capture the code explicitly and pass it through verbatim — preserving the 0/3/2/1 contract for CI.
138
+ set +e
139
+ node "$VERIFY_VH" "$@"
140
+ code=$?
141
+ set -e
142
+
143
+ if [ "$code" -ne 0 ]; then
144
+ echo "verify-vh gate: FAILED (exit $code) — blocking the merge." >&2
145
+ fi
146
+ exit "$code"