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.
- 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/ANCHORING.md +43 -22
- 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 +26 -3
- package/verifier/README.md +584 -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 +5123 -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 +3376 -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
package/examples/run.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// =================================================================================================
|
|
5
|
+
// verifyhash — runnable, self-checking, OFFLINE end-to-end EXAMPLE (T-21.2)
|
|
6
|
+
//
|
|
7
|
+
// WHAT THIS IS
|
|
8
|
+
// A single script a buyer/evaluator (and CI) can run with ZERO setup — no key, no TSA, no RPC, no
|
|
9
|
+
// network — to watch the real DataLedger + ProofParcel buyer pipeline work against the committed
|
|
10
|
+
// sample data in examples/. It reuses the EXACT module entrypoints the `vh` CLI dispatches to
|
|
11
|
+
// (cli/dataset.js, cli/parcel.js) — it is NOT a brittle shell pipeline of string parsing.
|
|
12
|
+
//
|
|
13
|
+
// DataLedger : dataset build -> check --policy (PASS + FAIL) -> verify (MATCH + TAMPER) -> report -> attest
|
|
14
|
+
// ProofParcel: parcel build -> verify (MATCH + TAMPER) -> attest
|
|
15
|
+
//
|
|
16
|
+
// FILESYSTEM HYGIENE (load-bearing)
|
|
17
|
+
// The committed sample data under examples/ is READ-ONLY here. Everything this script produces —
|
|
18
|
+
// manifests, the report, the unsigned attestation bytes, and the working copies it deliberately
|
|
19
|
+
// tampers — is written to a fresh OS temp workspace (or VH_EXAMPLE_OUT if you set it). NOTHING is
|
|
20
|
+
// ever scattered into the repo working tree. Set VH_EXAMPLE_KEEP=1 to keep the temp dir for inspection.
|
|
21
|
+
//
|
|
22
|
+
// TRUST POSTURE (read this; the script will not let you forget it)
|
|
23
|
+
// This example demonstrates TAMPER-EVIDENCE (any edit/rename/add/remove flips the Merkle root) and
|
|
24
|
+
// emits the canonical UNSIGNED attestation bytes a human trust-root would sign. It does NOT, and
|
|
25
|
+
// cannot, prove "unaltered since date T": that standing claim rides the HUMAN-OWNED signing/timestamp/
|
|
26
|
+
// anchor trust-root (needs-human, P-3 in STRATEGY.md). This script references — but never executes —
|
|
27
|
+
// those `sign` / `timestamp` / anchor steps, and says exactly where the human handoff is.
|
|
28
|
+
// =================================================================================================
|
|
29
|
+
|
|
30
|
+
const fs = require("fs");
|
|
31
|
+
const os = require("os");
|
|
32
|
+
const path = require("path");
|
|
33
|
+
|
|
34
|
+
const dataset = require("../cli/dataset");
|
|
35
|
+
const parcel = require("../cli/parcel");
|
|
36
|
+
|
|
37
|
+
const EX_DIR = __dirname;
|
|
38
|
+
const SAMPLE_DATASET = path.join(EX_DIR, "sample-dataset");
|
|
39
|
+
const SAMPLE_DATASET_HINTS = path.join(EX_DIR, "sample-dataset.hints.json");
|
|
40
|
+
const POLICY_LENIENT = path.join(EX_DIR, "policy.lenient.json");
|
|
41
|
+
const POLICY_STRICT = path.join(EX_DIR, "policy.strict.json");
|
|
42
|
+
const SAMPLE_PARCEL = path.join(EX_DIR, "sample-parcel");
|
|
43
|
+
|
|
44
|
+
// ---- tiny output + check helpers -----------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
const out = (s) => process.stdout.write(s + "\n");
|
|
47
|
+
const hr = () => out("-".repeat(88));
|
|
48
|
+
|
|
49
|
+
const checks = []; // { ok, label } — the script's verdict is the AND of every one of these.
|
|
50
|
+
function check(label, ok, detail) {
|
|
51
|
+
checks.push({ ok: !!ok, label });
|
|
52
|
+
out(` [${ok ? "PASS" : "FAIL"}] ${label}${detail ? " — " + detail : ""}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Recursively copy a directory tree (the sample is tiny). Used so the TAMPER step mutates a working
|
|
56
|
+
// COPY in the temp workspace, never the committed sample under examples/.
|
|
57
|
+
function copyDir(src, dst) {
|
|
58
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
59
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
60
|
+
const s = path.join(src, entry.name);
|
|
61
|
+
const d = path.join(dst, entry.name);
|
|
62
|
+
if (entry.isDirectory()) copyDir(s, d);
|
|
63
|
+
else fs.copyFileSync(s, d);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// A stdout sink that swallows the command's own human output — the example prints its OWN narration so
|
|
68
|
+
// the PASS/FAIL summary stays readable. (The commands still run for real; we just don't echo their block.)
|
|
69
|
+
const quiet = () => {};
|
|
70
|
+
|
|
71
|
+
// =================================================================================================
|
|
72
|
+
|
|
73
|
+
function main() {
|
|
74
|
+
// LEAD with the standing trust note so the example can never overclaim.
|
|
75
|
+
hr();
|
|
76
|
+
out("verifyhash — OFFLINE end-to-end example (DataLedger + ProofParcel)");
|
|
77
|
+
hr();
|
|
78
|
+
out("TRUST NOTE (DataLedger): " + dataset.TRUST_NOTE);
|
|
79
|
+
out("TRUST NOTE (ProofParcel): " + parcel.TRUST_NOTE);
|
|
80
|
+
out("");
|
|
81
|
+
out("This example proves TAMPER-EVIDENCE and emits the canonical UNSIGNED attestation bytes.");
|
|
82
|
+
out('It does NOT prove "unaltered since date T" — that rides the HUMAN-OWNED signing/timestamp/');
|
|
83
|
+
out("anchor trust-root (needs-human, P-3). Those steps are referenced below but NEVER executed.");
|
|
84
|
+
hr();
|
|
85
|
+
|
|
86
|
+
// Fresh, isolated output workspace. Default: an OS temp dir; override via VH_EXAMPLE_OUT.
|
|
87
|
+
const work =
|
|
88
|
+
process.env.VH_EXAMPLE_OUT && process.env.VH_EXAMPLE_OUT.trim()
|
|
89
|
+
? path.resolve(process.env.VH_EXAMPLE_OUT.trim())
|
|
90
|
+
: fs.mkdtempSync(path.join(os.tmpdir(), "vh-example-"));
|
|
91
|
+
fs.mkdirSync(work, { recursive: true });
|
|
92
|
+
out(`output workspace (gitignored / OS temp — never the repo): ${work}`);
|
|
93
|
+
out("");
|
|
94
|
+
|
|
95
|
+
const artifacts = {};
|
|
96
|
+
|
|
97
|
+
// ===============================================================================================
|
|
98
|
+
// PART 1 — DataLedger: the AI training-data provenance pipeline.
|
|
99
|
+
// ===============================================================================================
|
|
100
|
+
out("== DataLedger ==");
|
|
101
|
+
|
|
102
|
+
const dsManifest = path.join(work, "dataset.manifest.json");
|
|
103
|
+
const hints = JSON.parse(fs.readFileSync(SAMPLE_DATASET_HINTS, "utf8"));
|
|
104
|
+
|
|
105
|
+
// build — tamper-evident manifest (Merkle root + per-file leaves) with the UNTRUSTED hints attached.
|
|
106
|
+
const dsBuild = dataset.runDatasetBuild({
|
|
107
|
+
dir: SAMPLE_DATASET,
|
|
108
|
+
out: dsManifest,
|
|
109
|
+
hints,
|
|
110
|
+
stdout: quiet,
|
|
111
|
+
});
|
|
112
|
+
artifacts.datasetManifest = dsManifest;
|
|
113
|
+
check(
|
|
114
|
+
"dataset build produced a manifest with a Merkle root over every file",
|
|
115
|
+
/^0x[0-9a-fA-F]{64}$/.test(dsBuild.root) && dsBuild.fileCount === 5,
|
|
116
|
+
`root=${dsBuild.root.slice(0, 14)}… files=${dsBuild.fileCount}`
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// check --policy (LENIENT) — the org-policy gate should PASS (CI exit 0).
|
|
120
|
+
const lenient = dataset.runDatasetCheck({
|
|
121
|
+
manifest: dsManifest,
|
|
122
|
+
policy: POLICY_LENIENT,
|
|
123
|
+
stdout: quiet,
|
|
124
|
+
});
|
|
125
|
+
check(
|
|
126
|
+
"dataset check --policy (lenient) PASSes → CLI exit 0",
|
|
127
|
+
lenient.verdict === "PASS",
|
|
128
|
+
`verdict=${lenient.verdict}`
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// check --policy (STRICT) — the DELIBERATE violations in the sample (a GPL-3.0 file + a file with no
|
|
132
|
+
// license hint under requireLicense) must be FLAGGED (CI exit 3). This is the core "the gate works" proof.
|
|
133
|
+
const strict = dataset.runDatasetCheck({
|
|
134
|
+
manifest: dsManifest,
|
|
135
|
+
policy: POLICY_STRICT,
|
|
136
|
+
stdout: quiet,
|
|
137
|
+
});
|
|
138
|
+
const flaggedGpl = strict.violations.some(
|
|
139
|
+
(v) => v.rule === "denyLicenses" && v.value === "GPL-3.0"
|
|
140
|
+
);
|
|
141
|
+
const flaggedMissing = strict.violations.some((v) => v.rule === "requireLicense");
|
|
142
|
+
check(
|
|
143
|
+
"dataset check --policy (strict) FLAGS the deliberate violations → CLI exit 3",
|
|
144
|
+
strict.verdict === "FAIL" && flaggedGpl && flaggedMissing,
|
|
145
|
+
`verdict=${strict.verdict} violations=${strict.violations.length} (GPL-3.0 + missing-license)`
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// verify (MATCH) — re-derive the root from the UNTOUCHED committed sample; must MATCH (CI exit 0).
|
|
149
|
+
const dsVerifyOk = dataset.runDatasetVerify({
|
|
150
|
+
dir: SAMPLE_DATASET,
|
|
151
|
+
manifest: dsManifest,
|
|
152
|
+
stdout: quiet,
|
|
153
|
+
});
|
|
154
|
+
check(
|
|
155
|
+
"dataset verify against the untouched sample → MATCH (CLI exit 0)",
|
|
156
|
+
dsVerifyOk.status === "MATCH",
|
|
157
|
+
`status=${dsVerifyOk.status}`
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// verify (TAMPER) — copy the sample, mutate ONE file, and confirm verify catches it as MISMATCH and
|
|
161
|
+
// localizes the CHANGED file. The mutation happens on the temp COPY, never the committed sample.
|
|
162
|
+
const dsTamperDir = path.join(work, "tampered-dataset");
|
|
163
|
+
copyDir(SAMPLE_DATASET, dsTamperDir);
|
|
164
|
+
const tamperedFile = path.join(dsTamperDir, "corpus", "mit-notes.txt");
|
|
165
|
+
fs.appendFileSync(tamperedFile, "\nTAMPER: one extra byte changes the file's bytes.\n");
|
|
166
|
+
const dsVerifyTamper = dataset.runDatasetVerify({
|
|
167
|
+
dir: dsTamperDir,
|
|
168
|
+
manifest: dsManifest,
|
|
169
|
+
stdout: quiet,
|
|
170
|
+
});
|
|
171
|
+
const caughtChanged = dsVerifyTamper.diff.changed.some(
|
|
172
|
+
(c) => c.path === "corpus/mit-notes.txt"
|
|
173
|
+
);
|
|
174
|
+
check(
|
|
175
|
+
"dataset verify catches a one-byte TAMPER → MISMATCH, localized to the CHANGED file (CLI exit 3)",
|
|
176
|
+
dsVerifyTamper.status === "MISMATCH" && caughtChanged,
|
|
177
|
+
`status=${dsVerifyTamper.status} changed=[${dsVerifyTamper.diff.changed
|
|
178
|
+
.map((c) => c.path)
|
|
179
|
+
.join(", ")}]`
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// report — ONE filed evidence document (identity + license roll-up + embedded verify + policy verdict).
|
|
183
|
+
// Run with BOTH gates against the strict policy: the report is the combined CI gate and reports FAIL.
|
|
184
|
+
const dsReport = path.join(work, "dataset.evidence.md");
|
|
185
|
+
const reportRes = dataset.runDatasetReport({
|
|
186
|
+
manifest: dsManifest,
|
|
187
|
+
verifyDir: SAMPLE_DATASET,
|
|
188
|
+
policy: POLICY_STRICT,
|
|
189
|
+
out: dsReport,
|
|
190
|
+
stdout: quiet,
|
|
191
|
+
});
|
|
192
|
+
artifacts.datasetReport = dsReport;
|
|
193
|
+
check(
|
|
194
|
+
"dataset report wrote ONE evidence document (verify=MATCH embedded, policy=FAIL embedded)",
|
|
195
|
+
fs.existsSync(dsReport) &&
|
|
196
|
+
reportRes.verifyStatus === "MATCH" &&
|
|
197
|
+
reportRes.policyVerdict === "FAIL",
|
|
198
|
+
`verify=${reportRes.verifyStatus} policy=${reportRes.policyVerdict}`
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// attest — emit the canonical UNSIGNED attestation bytes the human trust-root would sign.
|
|
202
|
+
const dsAttest = path.join(work, "dataset.attestation.json");
|
|
203
|
+
const attestRes = dataset.runDatasetAttest({
|
|
204
|
+
manifest: dsManifest,
|
|
205
|
+
out: dsAttest,
|
|
206
|
+
stdout: quiet,
|
|
207
|
+
});
|
|
208
|
+
artifacts.datasetAttestation = dsAttest;
|
|
209
|
+
check(
|
|
210
|
+
"dataset attest emitted canonical UNSIGNED bytes (signed:false) for the human trust-root to sign",
|
|
211
|
+
fs.existsSync(dsAttest) &&
|
|
212
|
+
attestRes.envelope.signed === false &&
|
|
213
|
+
typeof attestRes.canonical === "string" &&
|
|
214
|
+
attestRes.canonical.length > 0,
|
|
215
|
+
`signed=${attestRes.envelope.signed}`
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
out("");
|
|
219
|
+
|
|
220
|
+
// ===============================================================================================
|
|
221
|
+
// PART 2 — ProofParcel: the B2B data-delivery receipt pipeline.
|
|
222
|
+
// ===============================================================================================
|
|
223
|
+
out("== ProofParcel ==");
|
|
224
|
+
|
|
225
|
+
const pManifest = path.join(work, "parcel.manifest.json");
|
|
226
|
+
|
|
227
|
+
// build — tamper-evident delivery receipt (root + per-file leaves + UNTRUSTED parcel metadata).
|
|
228
|
+
const pBuild = parcel.runParcelBuild({
|
|
229
|
+
dir: SAMPLE_PARCEL,
|
|
230
|
+
out: pManifest,
|
|
231
|
+
parcel: {
|
|
232
|
+
parcelId: "EX-0001",
|
|
233
|
+
sender: "acme-data-co",
|
|
234
|
+
recipient: "globex-ml",
|
|
235
|
+
},
|
|
236
|
+
stdout: quiet,
|
|
237
|
+
});
|
|
238
|
+
artifacts.parcelManifest = pManifest;
|
|
239
|
+
check(
|
|
240
|
+
"parcel build produced a delivery receipt with a Merkle root over every delivered file",
|
|
241
|
+
/^0x[0-9a-fA-F]{64}$/.test(pBuild.root) && pBuild.fileCount === 3,
|
|
242
|
+
`root=${pBuild.root.slice(0, 14)}… files=${pBuild.fileCount}`
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// verify (MATCH) — the recipient re-derives the root from the delivered bytes; must MATCH (CI exit 0).
|
|
246
|
+
const pVerifyOk = parcel.runParcelVerify({
|
|
247
|
+
dir: SAMPLE_PARCEL,
|
|
248
|
+
manifest: pManifest,
|
|
249
|
+
stdout: quiet,
|
|
250
|
+
});
|
|
251
|
+
check(
|
|
252
|
+
"parcel verify against the delivered files → MATCH (CLI exit 0)",
|
|
253
|
+
pVerifyOk.status === "MATCH",
|
|
254
|
+
`status=${pVerifyOk.status}`
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// verify (TAMPER) — mutate ONE delivered file on a temp copy; verify must catch it as MISMATCH.
|
|
258
|
+
const pTamperDir = path.join(work, "tampered-parcel");
|
|
259
|
+
copyDir(SAMPLE_PARCEL, pTamperDir);
|
|
260
|
+
fs.appendFileSync(path.join(pTamperDir, "data", "records.csv"), "4,delta,400\n");
|
|
261
|
+
const pVerifyTamper = parcel.runParcelVerify({
|
|
262
|
+
dir: pTamperDir,
|
|
263
|
+
manifest: pManifest,
|
|
264
|
+
stdout: quiet,
|
|
265
|
+
});
|
|
266
|
+
const pCaughtChanged = pVerifyTamper.diff.changed.some((c) => c.path === "data/records.csv");
|
|
267
|
+
check(
|
|
268
|
+
"parcel verify catches a TAMPER → MISMATCH, localized to the CHANGED file (CLI exit 3)",
|
|
269
|
+
pVerifyTamper.status === "MISMATCH" && pCaughtChanged,
|
|
270
|
+
`status=${pVerifyTamper.status} changed=[${pVerifyTamper.diff.changed
|
|
271
|
+
.map((c) => c.path)
|
|
272
|
+
.join(", ")}]`
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// attest — emit the canonical UNSIGNED parcel-attestation bytes the human trust-root would sign.
|
|
276
|
+
const pAttest = path.join(work, "parcel.attestation.json");
|
|
277
|
+
const pAttestRes = parcel.runParcelAttest({
|
|
278
|
+
manifest: pManifest,
|
|
279
|
+
out: pAttest,
|
|
280
|
+
stdout: quiet,
|
|
281
|
+
});
|
|
282
|
+
artifacts.parcelAttestation = pAttest;
|
|
283
|
+
check(
|
|
284
|
+
"parcel attest emitted canonical UNSIGNED bytes (signed:false) for the human trust-root to sign",
|
|
285
|
+
fs.existsSync(pAttest) &&
|
|
286
|
+
pAttestRes.envelope.signed === false &&
|
|
287
|
+
typeof pAttestRes.canonical === "string" &&
|
|
288
|
+
pAttestRes.canonical.length > 0,
|
|
289
|
+
`signed=${pAttestRes.envelope.signed}`
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// ===============================================================================================
|
|
293
|
+
// Summary + the human-gated trust-root handoff (referenced, NOT executed).
|
|
294
|
+
// ===============================================================================================
|
|
295
|
+
out("");
|
|
296
|
+
hr();
|
|
297
|
+
out("Produced artifacts (all under the temp workspace — none in the repo):");
|
|
298
|
+
for (const [k, v] of Object.entries(artifacts)) out(` ${k}: ${v}`);
|
|
299
|
+
out("");
|
|
300
|
+
out("NEXT — the HUMAN-OWNED trust-root steps (NOT run here; they need a key / a TSA / an RPC):");
|
|
301
|
+
out(
|
|
302
|
+
" vh dataset sign " +
|
|
303
|
+
dsAttest +
|
|
304
|
+
" --key-file <YOUR-KEY> # sign the UNSIGNED bytes with a key YOU provisioned"
|
|
305
|
+
);
|
|
306
|
+
out(
|
|
307
|
+
" vh dataset timestamp-request " +
|
|
308
|
+
dsManifest +
|
|
309
|
+
" # then stamp the digest at an RFC-3161 TSA"
|
|
310
|
+
);
|
|
311
|
+
out(
|
|
312
|
+
" vh parcel sign " +
|
|
313
|
+
pAttest +
|
|
314
|
+
" --key-file <YOUR-KEY> # same handoff for the parcel attestation"
|
|
315
|
+
);
|
|
316
|
+
out(
|
|
317
|
+
" (on-chain anchor is a deploy + real funds — also needs-human, P-2 in STRATEGY.md)"
|
|
318
|
+
);
|
|
319
|
+
out(
|
|
320
|
+
' These convert "the bytes are these" into "signed/stamped by a trusted party at time T". The'
|
|
321
|
+
);
|
|
322
|
+
out(" example deliberately stops at the unsigned bytes — that boundary is the product's honesty line.");
|
|
323
|
+
hr();
|
|
324
|
+
|
|
325
|
+
const failed = checks.filter((c) => !c.ok);
|
|
326
|
+
if (failed.length === 0) {
|
|
327
|
+
out(`RESULT: PASS — all ${checks.length} pipeline checks passed.`);
|
|
328
|
+
} else {
|
|
329
|
+
out(`RESULT: FAIL — ${failed.length} of ${checks.length} checks failed:`);
|
|
330
|
+
for (const c of failed) out(` - ${c.label}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
code: failed.length === 0 ? 0 : 1,
|
|
335
|
+
work,
|
|
336
|
+
artifacts,
|
|
337
|
+
checks: checks.slice(),
|
|
338
|
+
// A temp workspace is OURS to delete on success; a caller-chosen VH_EXAMPLE_OUT is theirs to keep.
|
|
339
|
+
ownsWorkspace: !(process.env.VH_EXAMPLE_OUT && process.env.VH_EXAMPLE_OUT.trim()),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Run, then clean up the temp workspace (unless the caller asked to keep it or chose their own --out).
|
|
344
|
+
if (require.main === module) {
|
|
345
|
+
let result;
|
|
346
|
+
try {
|
|
347
|
+
result = main();
|
|
348
|
+
} catch (e) {
|
|
349
|
+
process.stderr.write("example crashed: " + (e && e.stack ? e.stack : e) + "\n");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const keep = process.env.VH_EXAMPLE_KEEP === "1";
|
|
354
|
+
if (result.work && result.ownsWorkspace && !keep) {
|
|
355
|
+
try {
|
|
356
|
+
fs.rmSync(result.work, { recursive: true, force: true });
|
|
357
|
+
} catch {
|
|
358
|
+
/* best-effort cleanup; never fail the example over it */
|
|
359
|
+
}
|
|
360
|
+
} else if (result.work) {
|
|
361
|
+
out(`(kept workspace: ${result.work})`);
|
|
362
|
+
}
|
|
363
|
+
process.exit(result.code);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
module.exports = { main };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Sample training dataset for the verifyhash / DataLedger end-to-end example.
|
|
2
|
+
|
|
3
|
+
This directory stands in for a real AI training dataset. It is intentionally tiny
|
|
4
|
+
(a handful of small text files, no secrets, no large blobs) so the example runs in
|
|
5
|
+
well under a second and the whole pipeline is auditable by eye.
|
|
6
|
+
|
|
7
|
+
The per-file source/license hints live in ../sample-dataset.hints.json. Those hints
|
|
8
|
+
are UNTRUSTED, self-asserted metadata: they are NOT bound into the Merkle root and
|
|
9
|
+
prove nothing on their own. The example's policy check is a gate on those CLAIMS,
|
|
10
|
+
not a verification that any license is genuinely correct.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
A vendored snippet whose license hint is GPL-3.0.
|
|
2
|
+
|
|
3
|
+
This file exists ON PURPOSE so the example's strict policy (which denies copyleft
|
|
4
|
+
licenses) has a real violation to flag. A proprietary-product org policy that lists
|
|
5
|
+
GPL-3.0 in denyLicenses will FAIL the dataset on this file.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"README.txt": { "source": "verifyhash-examples", "license": "MIT" },
|
|
3
|
+
"corpus/mit-notes.txt": { "source": "verifyhash-examples", "license": "MIT" },
|
|
4
|
+
"corpus/cc-by-poem.txt": { "source": "verifyhash-examples", "license": "CC-BY-4.0" },
|
|
5
|
+
"vendored/gpl-snippet.txt": { "source": "third-party-vendor", "license": "GPL-3.0" },
|
|
6
|
+
"data/unlabeled.txt": { "source": "verifyhash-examples" }
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Contents of this delivery:
|
|
2
|
+
- delivery-note.txt human-readable cover note
|
|
3
|
+
- data/records.csv the delivered records
|
|
4
|
+
- data/manifest-of-contents.txt this file
|
|
5
|
+
|
|
6
|
+
ProofParcel's tamper-evident manifest is the machine-checkable counterpart to this
|
|
7
|
+
human note: it commits to the exact bytes of every file above.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Sample B2B data-delivery parcel for the verifyhash / ProofParcel end-to-end example.
|
|
2
|
+
|
|
3
|
+
This directory stands in for a real data hand-off between two parties (a sender and a
|
|
4
|
+
recipient). ProofParcel pins exactly which files (names AND bytes) were delivered, so a
|
|
5
|
+
later "you never sent X" / "the file you sent was altered" dispute is resolved by
|
|
6
|
+
re-deriving the root from the delivered bytes.
|
|
7
|
+
|
|
8
|
+
The parcel metadata (parcelId, sender, recipient) is UNTRUSTED, self-asserted, and is
|
|
9
|
+
NOT bound into the Merkle root. Only the file set (names + bytes) is bound.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verifyhash",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Tamper-evident, permissionless on-chain registry of code-contribution hashes, plus an offline data-provenance toolkit (the `vh` CLI).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,14 +9,37 @@
|
|
|
9
9
|
"./package.json": "./package.json"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"vh": "cli/vh.js"
|
|
12
|
+
"vh": "cli/vh.js",
|
|
13
|
+
"vh-agent-hook": "cli/agent-hook.js"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"index.js",
|
|
16
17
|
"cli/",
|
|
17
18
|
"trustledger/",
|
|
19
|
+
"verifier/",
|
|
20
|
+
"examples/README.md",
|
|
21
|
+
"examples/run.js",
|
|
22
|
+
"examples/sample-dataset/",
|
|
23
|
+
"examples/sample-dataset.hints.json",
|
|
24
|
+
"examples/policy.lenient.json",
|
|
25
|
+
"examples/policy.strict.json",
|
|
26
|
+
"examples/sample-parcel/",
|
|
18
27
|
"README.md",
|
|
19
|
-
"docs/"
|
|
28
|
+
"docs/",
|
|
29
|
+
"!docs/METRICS.jsonl",
|
|
30
|
+
"!docs/USAGE-BUDGET.json",
|
|
31
|
+
"!docs/ENGINE-LEDGER.json",
|
|
32
|
+
"!docs/LOOP-AUDIT-*.json",
|
|
33
|
+
"!docs/LOOP-HARDENING-PLAN.md",
|
|
34
|
+
"!docs/SUPERVISOR-RUNBOOK.md",
|
|
35
|
+
"!docs/DECISIONS-PENDING.md",
|
|
36
|
+
"!docs/STRATEGY-ARCHIVE.md",
|
|
37
|
+
"!docs/MORNING.md",
|
|
38
|
+
"!docs/ADOPTION.json",
|
|
39
|
+
"!docs/DECIDE.md",
|
|
40
|
+
"!docs/DEPLOY-PUBLIC-SITE.md",
|
|
41
|
+
"!docs/AUDIT.md",
|
|
42
|
+
"!docs/VENDOR-PROVENANCE.md"
|
|
20
43
|
],
|
|
21
44
|
"engines": {
|
|
22
45
|
"node": ">=18"
|