verifyhash 0.1.0 → 0.1.1
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/README.md +5 -3
- package/cli/agent-hook.js +431 -0
- package/docs/ADOPT.md +15 -5
- package/docs/AGENT-HOOK.md +111 -0
- package/docs/PUBLISH-VERIFY-VH.md +45 -0
- package/examples/README.md +185 -0
- package/examples/policy.lenient.json +5 -0
- package/examples/policy.strict.json +6 -0
- package/examples/run.js +366 -0
- package/examples/sample-dataset/README.txt +10 -0
- package/examples/sample-dataset/corpus/cc-by-poem.txt +8 -0
- package/examples/sample-dataset/corpus/mit-notes.txt +4 -0
- package/examples/sample-dataset/data/unlabeled.txt +5 -0
- package/examples/sample-dataset/vendored/gpl-snippet.txt +5 -0
- package/examples/sample-dataset.hints.json +7 -0
- package/examples/sample-parcel/data/manifest-of-contents.txt +7 -0
- package/examples/sample-parcel/data/records.csv +4 -0
- package/examples/sample-parcel/delivery-note.txt +9 -0
- package/package.json +25 -3
- package/verifier/README.md +555 -0
- package/verifier/action/README.md +87 -0
- package/verifier/action/action.yml +146 -0
- package/verifier/build-standalone-html.js +1287 -0
- package/verifier/build-standalone.js +989 -0
- package/verifier/ci/journal.generic.sh +96 -0
- package/verifier/ci/journal.github-actions.yml +99 -0
- package/verifier/ci/reproduce-vh.generic.sh +59 -0
- package/verifier/ci/reproduce-vh.github-actions.yml +49 -0
- package/verifier/ci/verify-service.generic.sh +96 -0
- package/verifier/ci/verify-service.github-actions.yml +88 -0
- package/verifier/ci/verify-vh.generic.sh +75 -0
- package/verifier/ci/verify-vh.github-actions.yml +56 -0
- package/verifier/dist/BUILD-PROVENANCE.json +210 -0
- package/verifier/dist/seal-vh-standalone.js +876 -0
- package/verifier/dist/seal-vh-standalone.js.sha256 +1 -0
- package/verifier/dist/verify-vh-standalone.html +3373 -0
- package/verifier/dist/verify-vh-standalone.html.sha256 +1 -0
- package/verifier/dist/verify-vh-standalone.js +4121 -0
- package/verifier/dist/verify-vh-standalone.js.sha256 +1 -0
- package/verifier/lib/canonical.js +141 -0
- package/verifier/lib/keccak.js +30 -0
- package/verifier/lib/keccak256-vendored.js +206 -0
- package/verifier/lib/merkle.js +145 -0
- package/verifier/lib/revocation-core.js +606 -0
- package/verifier/lib/revocation.js +200 -0
- package/verifier/lib/seal-cli.js +374 -0
- package/verifier/lib/seal-evidence.js +237 -0
- package/verifier/lib/secp256k1-recover.js +249 -0
- package/verifier/package.json +39 -0
- package/verifier/verify-vh.js +2374 -0
- package/docs/ADOPTION.json +0 -11
- package/docs/AUDIT.md +0 -55
- package/docs/DECIDE.md +0 -47
- package/docs/DECISIONS-PENDING.md +0 -27
- package/docs/DEPLOY-PUBLIC-SITE.md +0 -301
- package/docs/ENGINE-LEDGER.json +0 -12
- package/docs/LOOP-AUDIT-2026-07-03.json +0 -580
- package/docs/LOOP-HARDENING-PLAN.md +0 -44
- package/docs/METRICS.jsonl +0 -31
- package/docs/MORNING.md +0 -204
- package/docs/STRATEGY-ARCHIVE.md +0 -5055
- package/docs/SUPERVISOR-RUNBOOK.md +0 -52
- 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"
|