ruvnet-brain 4.2.2-dev → 4.3.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.
Files changed (73) hide show
  1. package/README.md +15 -14
  2. package/bin/install.mjs +11 -2
  3. package/console/architecture.html +12 -14
  4. package/console/install-mockup.html +3 -3
  5. package/kb/zip-extract.mjs +8 -2
  6. package/package.json +7 -1
  7. package/plugin/.claude-plugin/plugin.json +1 -1
  8. package/plugin/.codex-plugin/plugin.json +1 -1
  9. package/plugin/hooks/codex-hooks.json +1 -1
  10. package/plugin/hooks/hooks.json +10 -0
  11. package/plugin/host-adapters/claude.json +11 -0
  12. package/plugin/host-adapters/codex.json +11 -0
  13. package/plugin/mcp/managed-cli-interface.mjs +77 -10
  14. package/plugin/mcp/server.mjs +2 -1
  15. package/plugin/scripts/anticipate.sh +30 -1
  16. package/plugin/scripts/capability-claim-evidence.mjs +431 -0
  17. package/plugin/scripts/capability-inventory-receipt.mjs +225 -0
  18. package/plugin/scripts/capability-registry.mjs +6 -0
  19. package/plugin/scripts/capability-routing.mjs +70 -0
  20. package/plugin/scripts/continuation-gate.mjs +68 -2
  21. package/plugin/scripts/coverage-integrity.mjs +575 -0
  22. package/plugin/scripts/ground-ruvnet.sh +2 -2
  23. package/plugin/scripts/nightly-controller.mjs +19 -2
  24. package/plugin/scripts/project-progression-contract.mjs +409 -0
  25. package/plugin/scripts/project-progression-hook.mjs +196 -0
  26. package/plugin/scripts/project-progression-outbox.mjs +95 -0
  27. package/plugin/scripts/project-progression-session-start.mjs +183 -0
  28. package/plugin/scripts/project-progression-store.mjs +248 -0
  29. package/plugin/scripts/project-store-resolver.mjs +106 -0
  30. package/plugin/scripts/session-snapshot-hook.mjs +29 -1
  31. package/plugin/scripts/session-start-core.mjs +22 -18
  32. package/plugin/scripts/spend-guard.mjs +6 -1
  33. package/scripts/adr-072-completion.mjs +98 -0
  34. package/scripts/behavioral-l1-l4.mjs +11 -8
  35. package/scripts/brain-score.mjs +7 -2
  36. package/scripts/build-bundle.mjs +82 -6
  37. package/scripts/card-from-source.mjs +32 -6
  38. package/scripts/corpus-aggregates.mjs +194 -0
  39. package/scripts/corpus-candidate.mjs +56 -36
  40. package/scripts/corpus-reconcile.mjs +295 -28
  41. package/scripts/corpus-seed-publish.mjs +3 -3
  42. package/scripts/coverage-integrity.mjs +3 -0
  43. package/scripts/gist-receipts.mjs +183 -0
  44. package/scripts/git-hooks/pre-push +21 -0
  45. package/scripts/host-registry.mjs +118 -0
  46. package/scripts/independent-review-receipt.mjs +499 -0
  47. package/scripts/ingest-gists.mjs +34 -5
  48. package/scripts/ingest-new-repos.mjs +5 -0
  49. package/scripts/metaharness-gate.mjs +58 -0
  50. package/scripts/nightly-gists.sh +3 -3
  51. package/scripts/nightly-wrapper.sh +75 -69
  52. package/scripts/product-integrity-contract.mjs +156 -0
  53. package/scripts/public-inventory.mjs +4 -0
  54. package/scripts/public-verification-aggregate.mjs +342 -0
  55. package/scripts/public-verification-finalizer.mjs +59 -0
  56. package/scripts/public-verification-inputs.mjs +473 -0
  57. package/scripts/public-verification-lane.mjs +244 -0
  58. package/scripts/publication-receipt.mjs +50 -6
  59. package/scripts/rebuild-gists-from-receipts.mjs +50 -5
  60. package/scripts/release-projection.mjs +86 -0
  61. package/scripts/release-transaction-provider.mjs +43 -102
  62. package/scripts/release-transaction.mjs +139 -51
  63. package/scripts/release.mjs +15 -7
  64. package/scripts/restore-local-ingests.mjs +2 -2
  65. package/scripts/retrieval-canary.mjs +533 -0
  66. package/scripts/routing-flywheel.mjs +12 -1
  67. package/scripts/rvf-generation.mjs +9 -3
  68. package/scripts/self-update.mjs +19 -44
  69. package/scripts/source-coverage.mjs +415 -0
  70. package/scripts/source-scope-receipt.mjs +84 -0
  71. package/scripts/sync-census.mjs +0 -0
  72. package/scripts/wired-check.mjs +114 -24
  73. package/scripts/worktree-integrity.mjs +123 -0
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env node
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { canonicalJson, digest } from './coverage-integrity.mjs';
7
+ import { retrievalOracleExpectationFromPlan, validateRetrievalOracleReview } from './independent-review-receipt.mjs';
8
+ import { validateRetrievalCanaryPlan, validateRetrievalCanaryReceipt } from './retrieval-canary.mjs';
9
+
10
+ export const PUBLIC_VERIFICATION_OS = Object.freeze(['linux', 'macos', 'windows']);
11
+ export const PUBLIC_VERIFICATION_MODES = Object.freeze(['claude', 'codex', 'dual']);
12
+ export const REQUIRED_REVIEW_MODELS = Object.freeze(['claude-fable-5', 'gpt-5.6-sol']);
13
+
14
+ const HEX40 = /^[a-f0-9]{40}$/;
15
+ const HEX64 = /^[a-f0-9]{64}$/;
16
+
17
+ function unsignedLeaf(leaf) {
18
+ const { leafSha256: _leafSha256, ...payload } = leaf;
19
+ return payload;
20
+ }
21
+
22
+ export function validatePublicVerificationLeaf(leaf) {
23
+ if (leaf?.schemaVersion !== 1 || leaf?.kind !== 'ruvnet-brain-public-verification-leaf'
24
+ || !PUBLIC_VERIFICATION_OS.includes(leaf.os) || !PUBLIC_VERIFICATION_MODES.includes(leaf.mode)
25
+ || !HEX40.test(String(leaf.sourceSha || '')) || !HEX64.test(String(leaf.artifactSha256 || ''))
26
+ || !HEX64.test(String(leaf.bundleSha256 || '')) || !HEX64.test(String(leaf.payloadId || ''))
27
+ || !HEX64.test(String(leaf.hostRegistryDigest || '')) || !HEX64.test(String(leaf.coverageGeneration || ''))
28
+ || !HEX64.test(String(leaf.canaryPlanSha256 || '')) || !HEX64.test(String(leaf.releaseTransactionId || ''))
29
+ || leaf.tag !== `v${leaf.version}` || leaf.status !== 'completed' || leaf.verdict !== 'PASS') {
30
+ throw new Error('public verification leaf identity or verdict is invalid');
31
+ }
32
+ if (digest(unsignedLeaf(leaf)) !== leaf.leafSha256) throw new Error('public verification leaf digest mismatch');
33
+ if (leaf.publicBytes?.npmExact !== true || leaf.publicBytes?.githubExact !== true
34
+ || leaf.publicBytes?.bundleExact !== true || leaf.installed?.version !== leaf.version
35
+ || leaf.installed?.loaderVerified !== true) throw new Error(`${leaf.os}/${leaf.mode} public bytes or loader are unverified`);
36
+ if (leaf.coverage?.verified !== true || leaf.coverage.eligibleCurrent !== leaf.coverage.eligibleTotal
37
+ || leaf.coverage.gistCurrent !== leaf.coverage.gistTotal) throw new Error(`${leaf.os}/${leaf.mode} coverage is incomplete`);
38
+ if (!Array.isArray(leaf.untested) || leaf.untested.length || leaf.skipped !== 0 || leaf.unknown !== 0) {
39
+ throw new Error(`${leaf.os}/${leaf.mode} has untested, skipped, or unknown evidence`);
40
+ }
41
+ const retrievalPlan = validateRetrievalCanaryPlan(leaf.retrievalPlan);
42
+ if (retrievalPlan.planSha256 !== leaf.canaryPlanSha256
43
+ || retrievalPlan.candidate.archiveSha256 !== leaf.bundleSha256) {
44
+ throw new Error(`${leaf.os}/${leaf.mode} retrieval plan identity differs`);
45
+ }
46
+ const retrieval = validateRetrievalCanaryReceipt(leaf.retrieval, { plan: retrievalPlan });
47
+ if (retrieval.sourceSha !== leaf.sourceSha || retrieval.artifactSha256 !== leaf.artifactSha256
48
+ || retrieval.candidateArchiveSha256 !== leaf.bundleSha256
49
+ || retrieval.coverageGeneration !== leaf.coverageGeneration || retrieval.planSha256 !== leaf.canaryPlanSha256) {
50
+ throw new Error(`${leaf.os}/${leaf.mode} retrieval identity differs`);
51
+ }
52
+ return leaf;
53
+ }
54
+
55
+ export function createPublicVerificationLeaf(input) {
56
+ const payload = { schemaVersion: 1, kind: 'ruvnet-brain-public-verification-leaf', ...input };
57
+ return validatePublicVerificationLeaf({ ...payload, leafSha256: digest(payload) });
58
+ }
59
+
60
+ function aggregatePayload(aggregate) {
61
+ const { aggregateSha256: _aggregateSha256, signature: _signature, ...payload } = aggregate;
62
+ return payload;
63
+ }
64
+
65
+ function reviewPayload(review) {
66
+ const { receiptSha256: _receiptSha256, ...payload } = review;
67
+ return payload;
68
+ }
69
+
70
+ export function validateIndependentReviewReceipt(review) {
71
+ if (review?.schemaVersion !== 2 || review?.kind !== 'ruvnet-brain-independent-review'
72
+ || !REQUIRED_REVIEW_MODELS.includes(review.model) || review.id !== review.model
73
+ || review.independent !== true || review.verdict !== 'PASS' || !Number.isInteger(review.score) || review.score < 95
74
+ || !HEX40.test(String(review.sourceSha || '')) || !HEX64.test(String(review.artifactSha256 || ''))
75
+ || !HEX64.test(String(review.payloadId || '')) || !HEX64.test(String(review.productContractSha256 || ''))
76
+ || !HEX64.test(String(review.rubricSha256 || '')) || !Array.isArray(review.deductions)
77
+ || !Array.isArray(review.untested) || review.untested.length || !review.execution?.subscriptionAuthenticated
78
+ || typeof review.execution?.invocationDigest !== 'string' || !HEX64.test(review.execution.invocationDigest)) {
79
+ throw new Error('independent review receipt is malformed, below 95, or incomplete');
80
+ }
81
+ if (review.model === 'claude-fable-5' && review.provider !== 'firstParty') {
82
+ throw new Error('Fable 5 review did not use the verified first-party subscription path');
83
+ }
84
+ if (review.model === 'gpt-5.6-sol' && (review.provider !== 'openai' || typeof review.execution.threadId !== 'string'
85
+ || !review.execution.threadId || !HEX64.test(String(review.execution.catalogRowSha256 || '')))) {
86
+ throw new Error('GPT-5.6-Sol review lacks live catalog and thread evidence');
87
+ }
88
+ const semantic = validateRetrievalOracleReview(review.retrievalOracleReview, { requireAcceptance: true });
89
+ if (canonicalJson(semantic) !== canonicalJson(review.retrievalOracleReview)) {
90
+ throw new Error('retrieval oracle semantic review is not canonical');
91
+ }
92
+ if (digest(reviewPayload(review)) !== review.receiptSha256) throw new Error('independent review receipt digest mismatch');
93
+ return review;
94
+ }
95
+
96
+ export function createIndependentReviewReceipt(input) {
97
+ const semantic = validateRetrievalOracleReview(input?.retrievalOracleReview, { requireAcceptance: true });
98
+ const payload = { schemaVersion: 2, kind: 'ruvnet-brain-independent-review', ...input,
99
+ retrievalOracleReview: semantic };
100
+ return validateIndependentReviewReceipt({ ...payload, receiptSha256: digest(payload) });
101
+ }
102
+
103
+ function validateReviewOracleAgainstPlan(review, retrievalPlan) {
104
+ const expected = retrievalOracleExpectationFromPlan(retrievalPlan);
105
+ const semantic = validateRetrievalOracleReview(review.retrievalOracleReview, { requireAcceptance: true });
106
+ const stores = semantic.records.map(({ store }) => store);
107
+ if (semantic.oracleReceiptSha256 !== expected.oracleReceiptSha256
108
+ || semantic.queryStoreSetSha256 !== expected.queryStoreSetSha256
109
+ || canonicalJson(stores) !== canonicalJson(expected.stores)) {
110
+ throw new Error(`${review.model} semantic review does not cover the complete oracle store set or exact oracle identity`);
111
+ }
112
+ const oracleQueries = retrievalPlan.oracle?.evidence?.queries;
113
+ if (!oracleQueries || typeof oracleQueries !== 'object' || Array.isArray(oracleQueries)
114
+ || canonicalJson(Object.keys(oracleQueries).sort()) !== canonicalJson(expected.stores)) {
115
+ throw new Error('retrieval plan lacks complete sealed oracle query evidence');
116
+ }
117
+ if (semantic.records.some((row) => oracleQueries[row.store]?.recordSha256 !== row.oracleRecordSha256)) {
118
+ throw new Error(`${review.model} semantic review record differs from the retrieval plan`);
119
+ }
120
+ return semantic;
121
+ }
122
+
123
+ const identityOf = (leaf) => ({
124
+ sourceSha: leaf.sourceSha,
125
+ version: leaf.version,
126
+ tag: leaf.tag,
127
+ artifactSha256: leaf.artifactSha256,
128
+ bundleSha256: leaf.bundleSha256,
129
+ payloadId: leaf.payloadId,
130
+ hostRegistryDigest: leaf.hostRegistryDigest,
131
+ coverageGeneration: leaf.coverageGeneration,
132
+ canaryPlanSha256: leaf.canaryPlanSha256,
133
+ releaseTransactionId: leaf.releaseTransactionId,
134
+ });
135
+
136
+ export function buildPublicVerificationAggregate(leaves, reviews) {
137
+ if (!Array.isArray(leaves) || leaves.length !== PUBLIC_VERIFICATION_OS.length * PUBLIC_VERIFICATION_MODES.length) {
138
+ throw new Error('public verification requires exactly nine leaves');
139
+ }
140
+ leaves.forEach(validatePublicVerificationLeaf);
141
+ const byLane = new Map(leaves.map((leaf) => [`${leaf.os}/${leaf.mode}`, leaf]));
142
+ const required = PUBLIC_VERIFICATION_OS.flatMap((os) => PUBLIC_VERIFICATION_MODES.map((mode) => `${os}/${mode}`));
143
+ if (byLane.size !== required.length || required.some((lane) => !byLane.has(lane))) {
144
+ throw new Error('public verification lanes are missing or duplicated');
145
+ }
146
+ const first = byLane.get(required[0]);
147
+ const identity = identityOf(first);
148
+ for (const lane of required) {
149
+ const leaf = byLane.get(lane);
150
+ if (canonicalJson(identityOf(leaf)) !== canonicalJson(identity)) throw new Error(`${lane} public verification identity differs`);
151
+ if (canonicalJson(leaf.coverage) !== canonicalJson(first.coverage)) throw new Error(`${lane} coverage projection differs`);
152
+ }
153
+ const retrievalCases = leaves.flatMap((leaf) => leaf.retrieval.cases);
154
+ const delta = retrievalCases.filter(({ cohort }) => cohort === 'delta');
155
+ const metrics = {
156
+ leaves: leaves.length,
157
+ retrievalCases: retrievalCases.length,
158
+ retrievalHits: retrievalCases.filter(({ retrievalHit }) => retrievalHit === true).length,
159
+ deltaCases: delta.length,
160
+ deltaHits: delta.filter(({ retrievalHit }) => retrievalHit === true).length,
161
+ recallAt10: retrievalCases.filter(({ retrievalHit }) => retrievalHit === true).length / retrievalCases.length,
162
+ deltaCitationRate: delta.filter(({ retrievalHit, citationResolved }) => retrievalHit === true && citationResolved === true).length / delta.length,
163
+ skipped: leaves.reduce((sum, leaf) => sum + leaf.skipped + leaf.retrieval.metrics.skipped, 0),
164
+ unknown: leaves.reduce((sum, leaf) => sum + leaf.unknown + leaf.retrieval.metrics.unknown, 0),
165
+ };
166
+ if (metrics.recallAt10 < 0.98 || metrics.deltaCitationRate !== 1 || metrics.skipped || metrics.unknown) {
167
+ throw new Error('public verification aggregate retrieval acceptance failed');
168
+ }
169
+ if (!Array.isArray(reviews) || reviews.length !== REQUIRED_REVIEW_MODELS.length) {
170
+ throw new Error('public verification aggregate requires exactly two independent reviews');
171
+ }
172
+ reviews.forEach(validateIndependentReviewReceipt);
173
+ const byModel = new Map(reviews.map((review) => [review.model, review]));
174
+ if (byModel.size !== REQUIRED_REVIEW_MODELS.length || REQUIRED_REVIEW_MODELS.some((model) => !byModel.has(model))) {
175
+ throw new Error('required independent review models are missing or duplicated');
176
+ }
177
+ const firstReview = byModel.get(REQUIRED_REVIEW_MODELS[0]);
178
+ const semanticIdentity = validateReviewOracleAgainstPlan(firstReview, first.retrievalPlan);
179
+ for (const model of REQUIRED_REVIEW_MODELS) {
180
+ const review = byModel.get(model);
181
+ const semantic = validateReviewOracleAgainstPlan(review, first.retrievalPlan);
182
+ if (review.sourceSha !== identity.sourceSha || review.artifactSha256 !== identity.artifactSha256
183
+ || review.payloadId !== identity.payloadId || review.productContractSha256 !== firstReview.productContractSha256
184
+ || review.rubricSha256 !== firstReview.rubricSha256
185
+ || semantic.recordSetSha256 !== semanticIdentity.recordSetSha256) {
186
+ throw new Error(`${model} review identity, rubric, or oracle identity differs`);
187
+ }
188
+ }
189
+ return {
190
+ schemaVersion: 1,
191
+ kind: 'ruvnet-brain-public-verification-aggregate',
192
+ identity,
193
+ coverage: first.coverage,
194
+ lanes: required.map((lane) => ({ lane, leafSha256: byLane.get(lane).leafSha256 })),
195
+ reviews: REQUIRED_REVIEW_MODELS.map((model) => ({ model, receiptSha256: byModel.get(model).receiptSha256,
196
+ score: byModel.get(model).score })),
197
+ evidence: { leaves: required.map((lane) => structuredClone(byLane.get(lane))),
198
+ reviews: REQUIRED_REVIEW_MODELS.map((model) => structuredClone(byModel.get(model))) },
199
+ productContractSha256: firstReview.productContractSha256,
200
+ rubricSha256: firstReview.rubricSha256,
201
+ retrievalOracle: {
202
+ oracleReceiptSha256: semanticIdentity.oracleReceiptSha256,
203
+ queryStoreSetSha256: semanticIdentity.queryStoreSetSha256,
204
+ recordCount: semanticIdentity.recordCount,
205
+ recordSetSha256: semanticIdentity.recordSetSha256,
206
+ },
207
+ metrics,
208
+ verdict: 'PASS',
209
+ untested: [],
210
+ };
211
+ }
212
+
213
+ export function signPublicVerificationAggregate({ leaves, reviews }, privateKey) {
214
+ const payload = buildPublicVerificationAggregate(leaves, reviews);
215
+ const aggregateSha256 = digest(payload);
216
+ const signed = { ...payload, aggregateSha256 };
217
+ return { ...signed, signature: crypto.sign(null, Buffer.from(canonicalJson(signed)), privateKey).toString('base64') };
218
+ }
219
+
220
+ export function verifyPublicVerificationAggregate(aggregate, publicKey, expectedIdentity = null) {
221
+ if (aggregate?.schemaVersion !== 1 || aggregate?.kind !== 'ruvnet-brain-public-verification-aggregate'
222
+ || aggregate.verdict !== 'PASS' || !Array.isArray(aggregate.untested) || aggregate.untested.length
223
+ || !HEX64.test(String(aggregate.aggregateSha256 || '')) || typeof aggregate.signature !== 'string') {
224
+ throw new Error('public verification aggregate is malformed');
225
+ }
226
+ const payload = aggregatePayload(aggregate);
227
+ if (digest(payload) !== aggregate.aggregateSha256) throw new Error('public verification aggregate digest mismatch');
228
+ if (!Array.isArray(aggregate.evidence?.leaves) || !Array.isArray(aggregate.evidence?.reviews)) {
229
+ throw new Error('public verification aggregate lacks raw leaf or review evidence');
230
+ }
231
+ const rebuilt = buildPublicVerificationAggregate(aggregate.evidence.leaves, aggregate.evidence.reviews);
232
+ if (canonicalJson(rebuilt) !== canonicalJson(payload)) {
233
+ throw new Error('public verification aggregate differs from rebuilt raw evidence');
234
+ }
235
+ const signed = { ...payload, aggregateSha256: aggregate.aggregateSha256 };
236
+ if (!crypto.verify(null, Buffer.from(canonicalJson(signed)), publicKey, Buffer.from(aggregate.signature, 'base64'))) {
237
+ throw new Error('public verification aggregate signature mismatch');
238
+ }
239
+ const required = PUBLIC_VERIFICATION_OS.flatMap((os) => PUBLIC_VERIFICATION_MODES.map((mode) => `${os}/${mode}`));
240
+ if (aggregate.lanes?.length !== required.length || new Set(aggregate.lanes.map(({ lane }) => lane)).size !== required.length
241
+ || required.some((lane) => !aggregate.lanes.some((row) => row.lane === lane && HEX64.test(String(row.leafSha256 || ''))))) {
242
+ throw new Error('public verification aggregate lanes are incomplete');
243
+ }
244
+ if (aggregate.metrics?.leaves !== 9 || aggregate.metrics?.recallAt10 < 0.98
245
+ || aggregate.metrics?.deltaCitationRate !== 1 || aggregate.metrics?.skipped !== 0 || aggregate.metrics?.unknown !== 0
246
+ || aggregate.coverage?.verified !== true || aggregate.coverage.eligibleCurrent !== aggregate.coverage.eligibleTotal
247
+ || aggregate.coverage.gistCurrent !== aggregate.coverage.gistTotal) {
248
+ throw new Error('public verification aggregate evidence is incomplete');
249
+ }
250
+ if (aggregate.reviews?.length !== REQUIRED_REVIEW_MODELS.length
251
+ || REQUIRED_REVIEW_MODELS.some((model) => !aggregate.reviews.some((row) => row.model === model
252
+ && Number.isInteger(row.score) && row.score >= 95 && HEX64.test(String(row.receiptSha256 || ''))))
253
+ || !HEX64.test(String(aggregate.productContractSha256 || '')) || !HEX64.test(String(aggregate.rubricSha256 || ''))) {
254
+ throw new Error('public verification aggregate reviews are incomplete');
255
+ }
256
+ if (aggregate.retrievalOracle?.oracleReceiptSha256 !== aggregate.evidence.leaves[0].retrievalPlan.oracle.receiptSha256
257
+ || aggregate.retrievalOracle?.queryStoreSetSha256 !== aggregate.evidence.leaves[0].retrievalPlan.oracle.queryStoreSetSha256
258
+ || aggregate.retrievalOracle?.recordCount !== aggregate.evidence.leaves[0].retrievalPlan.denominator.eligibleStores.length
259
+ || !HEX64.test(String(aggregate.retrievalOracle?.recordSetSha256 || ''))) {
260
+ throw new Error('public verification aggregate retrieval oracle review is incomplete');
261
+ }
262
+ if (expectedIdentity && canonicalJson(aggregate.identity) !== canonicalJson(expectedIdentity)) {
263
+ throw new Error('public verification aggregate identity differs from the release transaction');
264
+ }
265
+ return aggregate;
266
+ }
267
+
268
+ function parseCliArgs(argv) {
269
+ const parsed = {};
270
+ for (let index = 0; index < argv.length; index += 2) {
271
+ const flag = argv[index];
272
+ const value = argv[index + 1];
273
+ if (!['--lanes', '--reviews', '--out'].includes(flag) || !value) {
274
+ throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --reviews <dir> --out <file>');
275
+ }
276
+ parsed[flag.slice(2)] = value;
277
+ }
278
+ if (!parsed.lanes || !parsed.reviews || !parsed.out || Object.keys(parsed).length !== 3) {
279
+ throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --reviews <dir> --out <file>');
280
+ }
281
+ return parsed;
282
+ }
283
+
284
+ function readExactJsonFiles(directory, count, label) {
285
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
286
+ if (entries.length !== count || entries.some((entry) => !entry.isFile() || !entry.name.endsWith('.json'))) {
287
+ throw new Error(`${label} directory must contain exactly ${count} JSON files`);
288
+ }
289
+ return entries.sort((left, right) => left.name.localeCompare(right.name))
290
+ .map((entry) => JSON.parse(fs.readFileSync(path.join(directory, entry.name), 'utf8')));
291
+ }
292
+
293
+ function readLaneLeaves(directory) {
294
+ const wrappers = readExactJsonFiles(directory, PUBLIC_VERIFICATION_OS.length, 'lane');
295
+ const byOs = new Map();
296
+ for (const wrapper of wrappers) {
297
+ const payload = { schemaVersion: wrapper?.schemaVersion, kind: wrapper?.kind, os: wrapper?.os,
298
+ leaves: wrapper?.leaves };
299
+ if (payload.schemaVersion !== 1 || payload.kind !== 'ruvnet-brain-public-verification-os-lane'
300
+ || !PUBLIC_VERIFICATION_OS.includes(payload.os) || byOs.has(payload.os)
301
+ || !Array.isArray(payload.leaves) || payload.leaves.length !== PUBLIC_VERIFICATION_MODES.length
302
+ || payload.leaves.some((leaf) => leaf?.os !== payload.os)
303
+ || digest(payload) !== wrapper.laneSha256) {
304
+ throw new Error('public verification OS lane wrapper is malformed, duplicated, or tampered');
305
+ }
306
+ payload.leaves.forEach(validatePublicVerificationLeaf);
307
+ byOs.set(payload.os, payload.leaves);
308
+ }
309
+ if (PUBLIC_VERIFICATION_OS.some((osName) => !byOs.has(osName))) {
310
+ throw new Error('public verification OS lane wrapper is missing');
311
+ }
312
+ return PUBLIC_VERIFICATION_OS.flatMap((osName) => byOs.get(osName));
313
+ }
314
+
315
+ export function generatePublicVerificationAggregate({ lanesDirectory, reviewsDirectory, outputFile, privateKey }) {
316
+ if (!privateKey) throw new Error('RUVNET_SIGNING_KEY is required');
317
+ if (fs.existsSync(outputFile)) throw new Error(`refusing to overwrite existing aggregate: ${outputFile}`);
318
+ const leaves = readLaneLeaves(lanesDirectory);
319
+ const reviews = readExactJsonFiles(reviewsDirectory, REQUIRED_REVIEW_MODELS.length, 'review');
320
+ const aggregate = signPublicVerificationAggregate({ leaves, reviews }, privateKey);
321
+ fs.writeFileSync(outputFile, `${JSON.stringify(aggregate, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
322
+ return aggregate;
323
+ }
324
+
325
+ async function main() {
326
+ const options = parseCliArgs(process.argv.slice(2));
327
+ const aggregate = generatePublicVerificationAggregate({
328
+ lanesDirectory: options.lanes,
329
+ reviewsDirectory: options.reviews,
330
+ outputFile: options.out,
331
+ privateKey: process.env.RUVNET_SIGNING_KEY,
332
+ });
333
+ process.stdout.write(`${JSON.stringify({ ok: true, aggregateSha256: aggregate.aggregateSha256,
334
+ leaves: aggregate.metrics.leaves, reviews: aggregate.reviews.length })}\n`);
335
+ }
336
+
337
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
338
+ main().catch((error) => {
339
+ process.stderr.write(`${error.message}\n`);
340
+ process.exitCode = 1;
341
+ });
342
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { finalizeReleaseTransaction } from './release-transaction.mjs';
6
+ import { liveReleaseProvider } from './release-transaction-provider.mjs';
7
+
8
+ const argument = (args, name) => {
9
+ const index = args.indexOf(name);
10
+ return index >= 0 ? args[index + 1] : null;
11
+ };
12
+
13
+ function regularJson(file, label) {
14
+ const absolute = path.resolve(file || '');
15
+ let stat;
16
+ try { stat = fs.lstatSync(absolute); } catch { throw new Error(`${label} is missing`); }
17
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`${label} is not a trusted regular file`);
18
+ return JSON.parse(fs.readFileSync(absolute, 'utf8'));
19
+ }
20
+
21
+ export async function finalizePublicVerification({
22
+ identityFile,
23
+ aggregateFile,
24
+ outputFile,
25
+ privatePem = process.env.RUVNET_SIGNING_KEY,
26
+ publicKeyFile = 'keys/ruvnet-brain-signing.pub.pem',
27
+ adapter = liveReleaseProvider({ root: process.cwd() }),
28
+ } = {}) {
29
+ if (!identityFile || !aggregateFile || !outputFile) throw new Error('--identity, --aggregate, and --out are required');
30
+ const output = path.resolve(outputFile);
31
+ if (fs.existsSync(output)) throw new Error(`refusing to overwrite install-verified receipt: ${output}`);
32
+ if (!privatePem) throw new Error('RUVNET_SIGNING_KEY is required');
33
+ const identity = regularJson(identityFile, 'release identity');
34
+ const aggregate = regularJson(aggregateFile, 'public verification aggregate');
35
+ const publicKey = crypto.createPublicKey(fs.readFileSync(path.resolve(publicKeyFile), 'utf8'));
36
+ const receipt = await finalizeReleaseTransaction({ identity, aggregate, adapter,
37
+ privateKey: crypto.createPrivateKey(privatePem), publicKey, aggregatePublicKey: publicKey });
38
+ fs.mkdirSync(path.dirname(output), { recursive: true });
39
+ fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
40
+ return receipt;
41
+ }
42
+
43
+ export async function main(args = process.argv.slice(2)) {
44
+ try {
45
+ const receipt = await finalizePublicVerification({
46
+ identityFile: argument(args, '--identity'),
47
+ aggregateFile: argument(args, '--aggregate'),
48
+ outputFile: argument(args, '--out'),
49
+ });
50
+ process.stdout.write(`${JSON.stringify({ state: receipt.state, transactionId: receipt.transactionId,
51
+ receiptDigest: receipt.receiptDigest }, null, 2)}\n`);
52
+ return 0;
53
+ } catch (error) {
54
+ console.error(`public-verification-finalizer: ${error.message}`);
55
+ return 1;
56
+ }
57
+ }
58
+
59
+ if (path.resolve(process.argv[1] || '') === path.resolve(new URL(import.meta.url).pathname)) process.exitCode = await main();