deadbyte-mcp 0.15.39 → 0.15.42
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/DEADBYTE-MCP.cmd +8 -4
- package/MANIFEST.SHA256 +99 -45
- package/PROVENANCE.json +90 -20
- package/README.txt +39 -18
- package/RELEASE.json +85 -20
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +134 -34
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +132 -12
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R64-EVIDENCE.json +95 -0
- package/docs/R64-REFERENCE-GAP-MATRIX.json +98 -0
- package/docs/R64-SCOPE.json +41 -0
- package/docs/R65-AUTHORITY-CONTINUITY-DESIGN.json +27 -0
- package/docs/R65-REFERENCE-GAP-MATRIX.json +22 -0
- package/package.json +7 -4
- package/scripts/deploy-r48.mjs +123 -7
- package/scripts/gate-windows-r63-prepublish.ps1 +45 -0
- package/scripts/gate-windows-r64-prepublish.ps1 +68 -0
- package/scripts/gate-windows-r65-prepublish.ps1 +65 -0
- package/scripts/npm-publication-r65.mjs +139 -0
- package/scripts/npm-release-r48.mjs +62 -37
- package/scripts/r64-predecessor-reproducer.mjs +32 -0
- package/scripts/r64-read-benchmark.mjs +84 -0
- package/scripts/r65-canonical-full.mjs +45 -0
- package/scripts/r65-focused.mjs +37 -0
- package/scripts/verify-r65-parent-source.mjs +75 -0
- package/src/activation-continuity-engine-r65.mjs +225 -0
- package/src/activation-lock-r65.mjs +155 -0
- package/src/activation-preflight-r65.mjs +197 -0
- package/src/authority-continuity-r65.mjs +541 -0
- package/src/authority-epoch-cli-r65.mjs +10 -0
- package/src/autonomous-policy.mjs +7 -3
- package/src/coding-plane.mjs +12 -3
- package/src/compact-gateway-mcp-tools.mjs +1 -1
- package/src/deadbyte-cli.mjs +16 -2
- package/src/desktop-policy-r40.mjs +6 -3
- package/src/doctor-transactions-r58.mjs +1 -1
- package/src/host-plane.mjs +9 -3
- package/src/machine-fs-mcp-tools.mjs +1 -1
- package/src/machine-fs.mjs +7 -4
- package/src/process-policy.mjs +7 -4
- package/src/remote-authority.mjs +20 -1
- package/src/runtime-update.mjs +15 -5
- package/src/version.mjs +1 -1
- package/test/audit-context.test.mjs +15 -4
- package/test/autonomous-local-supervisor.test.mjs +7 -6
- package/test/autonomous-supervisor-integration.test.mjs +8 -7
- package/test/capability-resolver.test.mjs +11 -5
- package/test/codex-autonomous-r44.test.mjs +7 -6
- package/test/coding-observation.test.mjs +4 -3
- package/test/commander-real-parity-r47.test.mjs +5 -4
- package/test/commander-torture-r47.test.mjs +5 -4
- package/test/desktop-policy-r40.test.mjs +6 -4
- package/test/desktop-runtime-r40.test.mjs +8 -3
- package/test/host-inline-r45.test.mjs +4 -3
- package/test/machine-fs-runtime.test.mjs +4 -3
- package/test/process-longrun-r36.test.mjs +3 -2
- package/test/process-policy.test.mjs +10 -6
- package/test/process-runtime.test.mjs +9 -6
- package/test/r52-host-revocation.test.mjs +1 -0
- package/test/r59-tool-surface-compat.test.mjs +14 -4
- package/test/r62-release-contract.test.mjs +2 -1
- package/test/r63-one-shot-coherence.test.mjs +38 -0
- package/test/r63-release-contract.test.mjs +59 -0
- package/test/r64-machine-fs-large-read.test.mjs +136 -0
- package/test/r64-release-contract.test.mjs +52 -0
- package/test/r65-activation-lock-recovery.test.mjs +72 -0
- package/test/r65-activation-lock.test.mjs +59 -0
- package/test/r65-activation-orchestration.test.mjs +179 -0
- package/test/r65-activation-preflight.test.mjs +35 -0
- package/test/r65-authority-continuity-decision.test.mjs +58 -0
- package/test/r65-authority-durable-policy-binding.test.mjs +34 -0
- package/test/r65-authority-epoch-cli.test.mjs +125 -0
- package/test/r65-authority-epoch-races.test.mjs +87 -0
- package/test/r65-authority-epoch-recovery.test.mjs +82 -0
- package/test/r65-authority-epoch-store.test.mjs +94 -0
- package/test/r65-authority-fixture.mjs +41 -0
- package/test/r65-authority-grant-capture.test.mjs +37 -0
- package/test/r65-authority-observation-epoch.test.mjs +25 -0
- package/test/r65-authority-policy-envelope.test.mjs +46 -0
- package/test/r65-authority-restored-safe.test.mjs +75 -0
- package/test/r65-authority-runtime-epoch.test.mjs +115 -0
- package/test/r65-authority-runtime-policy-binding.test.mjs +29 -0
- package/test/r65-authority-test-trust.mjs +20 -0
- package/test/r65-cli-invocation.test.mjs +38 -0
- package/test/r65-controller-authority-epoch-live.test.mjs +100 -0
- package/test/r65-controller-authority-epoch.test.mjs +37 -0
- package/test/r65-deploy-preflight.test.mjs +55 -0
- package/test/r65-epoch-native-activation-integration.test.mjs +141 -0
- package/test/r65-epoch-native-continuity-engine.test.mjs +112 -0
- package/test/r65-epoch-native-cross-plane.test.mjs +95 -0
- package/test/r65-legacy-authority-migration.test.mjs +63 -0
- package/test/r65-legacy-auto-restore-rejection.test.mjs +17 -0
- package/test/r65-npm-publication.test.mjs +114 -0
- package/test/r65-parent-source-verifier.test.mjs +63 -0
- package/test/r65-release-contract.test.mjs +74 -0
- package/test/r65-transaction-precondition.test.mjs +36 -0
- package/test/remote-session-cli.test.mjs +13 -11
- package/test/standing-grants.test.mjs +20 -4
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { reconcileNpmPublication } from './npm-publication-r65.mjs';
|
|
4
|
+
import { mkdtemp, readFile, rm, unlink, mkdir, rename } from 'node:fs/promises';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
4
7
|
import path from 'node:path';
|
|
5
8
|
import os from 'node:os';
|
|
6
9
|
import { fileURLToPath } from 'node:url';
|
|
@@ -10,7 +13,7 @@ const node = process.execPath;
|
|
|
10
13
|
const npmCli = process.env.npm_execpath || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
11
14
|
|
|
12
15
|
function run(command, args, options = {}) {
|
|
13
|
-
const { cwd = root, timeout = 180000 } = options;
|
|
16
|
+
const { cwd = root, timeout = 180000, env=process.env } = options;
|
|
14
17
|
const r = spawnSync(command, args, {
|
|
15
18
|
cwd,
|
|
16
19
|
encoding: 'utf8',
|
|
@@ -18,6 +21,7 @@ function run(command, args, options = {}) {
|
|
|
18
21
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
19
22
|
shell: false,
|
|
20
23
|
windowsHide: true,
|
|
24
|
+
env,
|
|
21
25
|
});
|
|
22
26
|
if (r.error) throw r.error;
|
|
23
27
|
if (r.status !== 0) {
|
|
@@ -94,30 +98,34 @@ async function main() {
|
|
|
94
98
|
const existingText = (existing.stdout || '').trim();
|
|
95
99
|
const isAlreadyPublished = existing.status === 0 && existingText && existingText !== 'null';
|
|
96
100
|
|
|
97
|
-
if (isAlreadyPublished) {
|
|
98
|
-
if (!isVerifyOnly) {
|
|
99
|
-
throw new Error(`deadbyte-mcp@${version} is already published on registry: ${existingText}`);
|
|
100
|
-
} else {
|
|
101
|
-
console.log(`Notice: deadbyte-mcp@${version} is already published on registry (${existingText}), proceeding with pre-publish verification only.`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
101
|
+
if (isAlreadyPublished) console.log(`Notice: deadbyte-mcp@${version} exists on registry (${existingText}); exact-byte reconciliation is required.`);
|
|
104
102
|
|
|
105
103
|
console.log('[2/7] Pre-pack gates: verifying working tree manifest and doctor...');
|
|
106
104
|
run(node, [path.join(root, 'scripts', 'release-manifest.mjs'), 'verify']);
|
|
107
|
-
|
|
105
|
+
const doctorState=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-release-doctor-'));
|
|
106
|
+
try{run(node,[path.join(root,'src','deadbyte-cli.mjs'),'doctor'],{env:{...process.env,DEADBYTE_STATE_ROOT:doctorState}});}
|
|
107
|
+
finally{await rm(doctorState,{recursive:true,force:true});}
|
|
108
108
|
|
|
109
109
|
console.log('[3/7] Packing artifact with npm pack...');
|
|
110
|
-
const
|
|
111
|
-
const
|
|
110
|
+
const publicationDir=isVerifyOnly?null:path.join(resolveStateRoot(),'evidence','npm-publication',version);
|
|
111
|
+
const preservedTarball=publicationDir?path.join(publicationDir,`deadbyte-mcp-${version}.tgz`):null;
|
|
112
|
+
const resuming=Boolean(preservedTarball&&existsSync(path.join(publicationDir,'claim.json')));
|
|
113
|
+
if(publicationDir)await mkdir(publicationDir,{recursive:true});
|
|
114
|
+
if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
|
|
115
|
+
if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
|
|
116
|
+
const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
|
|
117
|
+
const packRecord=resuming?{filename:path.basename(preservedTarball)}:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
|
|
112
118
|
const filename = packRecord?.filename;
|
|
113
119
|
if (!filename) throw new Error('npm pack did not return a tarball filename');
|
|
114
|
-
const tarball = path.join(root, filename);
|
|
120
|
+
const tarball = resuming?preservedTarball:path.join(root, filename);
|
|
115
121
|
let releaseSucceeded = false;
|
|
116
122
|
let publishAttempted = false;
|
|
123
|
+
let releasePending = false;
|
|
117
124
|
|
|
118
125
|
try {
|
|
119
126
|
console.log('[4/7] Verifying packed artifact file surface against MANIFEST.SHA256 and packaging contract...');
|
|
120
127
|
const manifestText = await readFile(path.join(root, 'MANIFEST.SHA256'), 'utf8');
|
|
128
|
+
const manifestSha256=createHash('sha256').update(manifestText).digest('hex');
|
|
121
129
|
const manifestEntries = new Set();
|
|
122
130
|
for (const line of manifestText.split(/\r?\n/)) {
|
|
123
131
|
if (!line) continue;
|
|
@@ -168,7 +176,7 @@ async function main() {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
run(node, [path.join(extractedPackage, 'src', 'deadbyte-cli.mjs'), 'doctor'], {
|
|
171
|
-
cwd: extractedPackage,
|
|
179
|
+
cwd: extractedPackage,env:{...process.env,DEADBYTE_STATE_ROOT:path.join(sandbox,'state')},
|
|
172
180
|
});
|
|
173
181
|
|
|
174
182
|
console.log('Artifact isolation gate PASS (clean doctor and version check on unpacked tarball in sandbox)');
|
|
@@ -177,36 +185,48 @@ async function main() {
|
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
if (isVerifyOnly) {
|
|
188
|
+
const packedBytes = await readFile(tarball);
|
|
189
|
+
const sha256 = createHash('sha256').update(packedBytes).digest('hex');
|
|
190
|
+
const sha512Integrity = `sha512-${createHash('sha512').update(packedBytes).digest('base64')}`;
|
|
191
|
+
if (packRecord.integrity !== sha512Integrity) throw new Error('npm pack integrity differs from packed tarball bytes');
|
|
192
|
+
console.log('VERIFIED_TARBALL ' + JSON.stringify({filename,bytes:packedBytes.length,sha256,integrity:sha512Integrity,shasum:packRecord.shasum}));
|
|
180
193
|
console.log('[6/7] [VERIFY-ONLY] Skipping npm publish.');
|
|
181
194
|
console.log('[7/7] [VERIFY-ONLY] Skipping external registry smoke test.');
|
|
182
195
|
console.log('\nVERIFY-ONLY PIPELINE PASS: All pre-pack, artifact surface, and isolated sandbox gates passed successfully.');
|
|
183
196
|
return;
|
|
184
197
|
}
|
|
185
198
|
|
|
186
|
-
|
|
199
|
+
if(!resuming)await rename(tarball,preservedTarball);
|
|
200
|
+
const publishTarball=preservedTarball;
|
|
201
|
+
const localBytes = await readFile(publishTarball);
|
|
202
|
+
const localIntegrity=`sha512-${createHash('sha512').update(localBytes).digest('base64')}`;
|
|
203
|
+
if(packRecord.integrity&&packRecord.integrity!==localIntegrity)throw new Error('npm packed artifact integrity changed before publication');
|
|
187
204
|
|
|
188
205
|
console.log('[6/7] Publishing artifact to npm registry...');
|
|
189
206
|
publishAttempted = true;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
207
|
+
const reconciliation=await reconcileNpmPublication({recordPath:publicationDir,version,manifestSha256,localBytes,
|
|
208
|
+
publish:async()=>runNpmInteractive(['publish',publishTarball,'--access','public','--auth-type=web'],{timeout:300000}),
|
|
209
|
+
lookup:async()=>{
|
|
210
|
+
try{
|
|
211
|
+
const viewed=parseJson(runNpm(['view',`deadbyte-mcp@${version}`,'version','dist.integrity','dist.shasum','dist.tarball','--json'],{timeout:120000}));
|
|
212
|
+
return Array.isArray(viewed)?viewed[0]:viewed;
|
|
213
|
+
}catch(error){if(/E404|404 Not Found/.test(String(error?.message)))return null;throw error;}
|
|
214
|
+
},
|
|
215
|
+
tags:async()=>parseJson(runNpm(['view','deadbyte-mcp','dist-tags','--json'],{timeout:120000})),
|
|
216
|
+
fetchTarball:async published=>{
|
|
217
|
+
const response=await fetch(published?.dist?.tarball,{signal:AbortSignal.timeout(120000)});
|
|
218
|
+
if(!response.ok)throw Object.assign(new Error(`registry tarball fetch failed status=${response.status}`),{status:response.status});
|
|
219
|
+
return Buffer.from(await response.arrayBuffer());
|
|
220
|
+
}});
|
|
221
|
+
if(reconciliation.state!=='REGISTRY_IDENTITY_VERIFIED'){
|
|
222
|
+
console.log('RELEASE_PENDING '+JSON.stringify({state:reconciliation.state,version,tarball:publishTarball,record_path:publicationDir,reason:reconciliation.reason??null}));
|
|
223
|
+
releasePending=true;
|
|
224
|
+
process.exitCode=2;return;
|
|
200
225
|
}
|
|
201
|
-
const
|
|
202
|
-
const published
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
const registryResponse = await fetch(dist.tarball,{signal:AbortSignal.timeout(120000)});
|
|
206
|
-
if(!registryResponse.ok) throw new Error(`registry tarball fetch failed status=${registryResponse.status}`);
|
|
207
|
-
const registryBytes = Buffer.from(await registryResponse.arrayBuffer());
|
|
208
|
-
const tags = parseJson(runNpm(['view', 'deadbyte-mcp', 'dist-tags', '--json'], { timeout: 120000 }));
|
|
209
|
-
const identity=verifyPublishedTarballIdentity({version,localBytes,registryBytes,published,tags});
|
|
226
|
+
const identity=reconciliation.identity;
|
|
227
|
+
const published={version,dist:{integrity:identity.npm_integrity,shasum:identity.shasum}};
|
|
228
|
+
const tags={latest:version};
|
|
229
|
+
const publishOut='registry identity verified by durable reconciliation';
|
|
210
230
|
|
|
211
231
|
console.log('[7/7] Post-publish external registry smoke test (executed outside repo in temp)...');
|
|
212
232
|
const tempDir = os.tmpdir();
|
|
@@ -221,6 +241,7 @@ async function main() {
|
|
|
221
241
|
const externalDoctor = runNpm(['exec', '--yes', `--package=deadbyte-mcp@${version}`, '--', 'deadbyte-mcp', 'doctor'], {
|
|
222
242
|
cwd: tempDir,
|
|
223
243
|
timeout: 300000,
|
|
244
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:path.join(publicationDir,'external-doctor-state')},
|
|
224
245
|
});
|
|
225
246
|
|
|
226
247
|
if(!publishOnly){
|
|
@@ -247,10 +268,14 @@ async function main() {
|
|
|
247
268
|
}, null, 2));
|
|
248
269
|
|
|
249
270
|
} finally {
|
|
250
|
-
if (
|
|
271
|
+
if (isVerifyOnly || (!publishAttempted&&!publicationDir)) {
|
|
251
272
|
await unlink(tarball).catch(() => {});
|
|
273
|
+
} else if(releasePending){
|
|
274
|
+
console.log('Publication processing or reconciliation pending; exact tarball preserved: '+preservedTarball);
|
|
275
|
+
} else if(releaseSucceeded){
|
|
276
|
+
console.log('Verified release tarball retained for audit: '+preservedTarball);
|
|
252
277
|
} else {
|
|
253
|
-
console.error('Release artifact preserved
|
|
278
|
+
console.error('Release artifact preserved for reconciliation: ' + preservedTarball);
|
|
254
279
|
}
|
|
255
280
|
}
|
|
256
281
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {mkdtemp,mkdir,writeFile,rm} from 'node:fs/promises';
|
|
2
|
+
import {tmpdir} from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {pathToFileURL} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const sourceRoot=process.argv[2];
|
|
7
|
+
if(!sourceRoot||!path.isAbsolute(sourceRoot))throw new Error('absolute predecessor root required');
|
|
8
|
+
const load=name=>import(pathToFileURL(path.join(sourceRoot,name)).href);
|
|
9
|
+
const [{loadCodingPolicy},{createMachineFsRuntime},{generateTrustKeypair},{machineFsGatewayInputSchema},{registerMachineFsTools}]=await Promise.all([
|
|
10
|
+
load('src/coding-plane.mjs'),load('src/machine-fs.mjs'),load('src/trust.mjs'),
|
|
11
|
+
load('src/compact-gateway-mcp-tools.mjs'),load('src/machine-fs-mcp-tools.mjs')
|
|
12
|
+
]);
|
|
13
|
+
const root=await mkdtemp(path.join(tmpdir(),'deadbyte-r64-red-'));
|
|
14
|
+
try{
|
|
15
|
+
const project=path.join(root,'project');await mkdir(project);
|
|
16
|
+
await writeFile(path.join(project,'large.bin'),Buffer.alloc(4*1024*1024+64,0x52));
|
|
17
|
+
const policyPath=path.join(root,'policy.json');
|
|
18
|
+
await writeFile(policyPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:path.join(root,'grant.json'),evidence_dir:path.join(root,'evidence'),roots:{project:{path:project,read:true,write:false,exec:false,deny_paths:[]}},commands:{},max_read_bytes:4*1024*1024,max_write_bytes:65536}));
|
|
19
|
+
const policy=await loadCodingPolicy(policyPath);
|
|
20
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
21
|
+
const runtime=createMachineFsRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
22
|
+
const first=await runtime.read({rootId:'project',relativePath:'large.bin',offset:2,maxBytes:4*1024*1024});
|
|
23
|
+
const args={root_id:'project',path:'large.bin',offset:first.next_offset,max_bytes:64,hash_file:false,timeout_ms:30000};
|
|
24
|
+
const server={registerTool(name,definition){if(name==='machine_fs_read')this.read=definition;}};
|
|
25
|
+
registerMachineFsTools(server,{runtime:{}});
|
|
26
|
+
let runtimeError=null;
|
|
27
|
+
try{await runtime.read({rootId:'project',relativePath:'large.bin',offset:first.next_offset,maxBytes:64});}
|
|
28
|
+
catch(error){runtimeError=String(error?.message||error);}
|
|
29
|
+
const result={source_root:sourceRoot,page_two_offset:first.next_offset,full_schema_accepts:server.read.inputSchema.safeParse(args).success,compact_schema_accepts:machineFsGatewayInputSchema.safeParse({op:'read',...args}).success,runtime_error:runtimeError};
|
|
30
|
+
console.log(JSON.stringify(result));
|
|
31
|
+
if(result.full_schema_accepts||result.compact_schema_accepts||!runtimeError)process.exitCode=1;
|
|
32
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {mkdtemp,mkdir,readFile,rm,writeFile} from 'node:fs/promises';
|
|
3
|
+
import {performance} from 'node:perf_hooks';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {pathToFileURL} from 'node:url';
|
|
6
|
+
|
|
7
|
+
const [beforeRoot,afterRoot,scratchRoot,outputPath]=process.argv.slice(2);
|
|
8
|
+
if([beforeRoot,afterRoot,scratchRoot,outputPath].some(value=>!value||!path.isAbsolute(value)))throw new Error('four absolute paths required: R63 R64 scratch output');
|
|
9
|
+
const rounds=7,operations=400,concurrency=4;
|
|
10
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
const percentile=(values,p)=>[...values].sort((a,b)=>a-b)[Math.ceil(values.length*p)-1];
|
|
12
|
+
await mkdir(scratchRoot,{recursive:true});
|
|
13
|
+
const scratch=await mkdtemp(path.join(scratchRoot,'read-bench-'));
|
|
14
|
+
try{
|
|
15
|
+
const project=path.join(scratch,'project');
|
|
16
|
+
await mkdir(project);
|
|
17
|
+
const file=path.join(project,'fixture.bin');
|
|
18
|
+
const content=Buffer.alloc(4*1024*1024+131072,0x52);
|
|
19
|
+
await writeFile(file,content);
|
|
20
|
+
const policyPath=path.join(scratch,'policy.json');
|
|
21
|
+
await writeFile(policyPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
|
|
22
|
+
lease_file:path.join(scratch,'grant.json'),evidence_dir:path.join(scratch,'evidence'),
|
|
23
|
+
roots:{project:{path:project,read:true,write:false,exec:false,deny_paths:[]}},
|
|
24
|
+
commands:{},max_read_bytes:4*1024*1024,max_write_bytes:65536}));
|
|
25
|
+
const rows=[];
|
|
26
|
+
for(let round=0;round<rounds;round++){
|
|
27
|
+
for(const [label,root] of (round%2?[['after',afterRoot],['before',beforeRoot]]:[['before',beforeRoot],['after',afterRoot]])){
|
|
28
|
+
const url=name=>pathToFileURL(path.join(root,name)).href;
|
|
29
|
+
const [{loadCodingPolicy},{createMachineFsRuntime},{generateTrustKeypair},{createBridgeGovernor}]=await Promise.all([
|
|
30
|
+
import(url('src/coding-plane.mjs')),import(url('src/machine-fs.mjs')),
|
|
31
|
+
import(url('src/trust.mjs')),import(url('bridge/src/r40-governor.mjs'))
|
|
32
|
+
]);
|
|
33
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(scratch,`private-${label}-${round}.pem`),publicKeyPath:path.join(scratch,`public-${label}-${round}.pem`)});
|
|
34
|
+
const policy=await loadCodingPolicy(policyPath);
|
|
35
|
+
const runtime=createMachineFsRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
36
|
+
const governor=createBridgeGovernor();
|
|
37
|
+
const latencies=[],control=[],errors=[];
|
|
38
|
+
let next=0,peakRss=process.memoryUsage().rss;
|
|
39
|
+
const started=performance.now();
|
|
40
|
+
async function bulkWorker(){
|
|
41
|
+
for(;;){
|
|
42
|
+
const index=next++;
|
|
43
|
+
if(index>=operations)return;
|
|
44
|
+
const mark=performance.now();
|
|
45
|
+
const ticket=await governor.acquire('machine_fs',{op:'read'});
|
|
46
|
+
try{
|
|
47
|
+
const result=await runtime.read({rootId:'project',relativePath:'fixture.bin',offset:(index%32)*65536,maxBytes:65536});
|
|
48
|
+
if(result.status!=='ok'||Buffer.from(result.data_base64,'base64').length!==65536)throw new Error('read oracle failed');
|
|
49
|
+
}catch(error){errors.push(String(error));}
|
|
50
|
+
finally{ticket.release();latencies.push(performance.now()-mark);peakRss=Math.max(peakRss,process.memoryUsage().rss);}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function controlWorker(){
|
|
54
|
+
for(let index=0;index<operations;index++){
|
|
55
|
+
const mark=performance.now();
|
|
56
|
+
const ticket=await governor.acquire(index%2?'machine_status':'machine_ping');
|
|
57
|
+
try{if(index%2)await readFile(policyPath);else sha(Buffer.from(`ping-${index}`));}
|
|
58
|
+
finally{ticket.release();control.push(performance.now()-mark);}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await Promise.all([...Array.from({length:concurrency},()=>bulkWorker()),controlWorker()]);
|
|
62
|
+
const elapsed=performance.now()-started;
|
|
63
|
+
await governor.close();
|
|
64
|
+
const boundaryStart=performance.now();
|
|
65
|
+
let boundary={status:'unknown'};
|
|
66
|
+
try{
|
|
67
|
+
const first=await runtime.read({rootId:'project',relativePath:'fixture.bin',offset:65536,maxBytes:4*1024*1024});
|
|
68
|
+
const second=await runtime.read({rootId:'project',relativePath:'fixture.bin',offset:first.next_offset,maxBytes:65536});
|
|
69
|
+
if(first.next_offset<=4*1024*1024||second.eof!==true||Buffer.from(second.data_base64,'base64').length!==65536)throw new Error('cross-boundary oracle failed');
|
|
70
|
+
boundary={status:'pass',page_two_offset:first.next_offset,elapsed_ms:performance.now()-boundaryStart};
|
|
71
|
+
}catch(error){boundary={status:'rejected',reason:String(error?.message||error),elapsed_ms:performance.now()-boundaryStart};}
|
|
72
|
+
if(label==='before'&&boundary.status!=='rejected')errors.push('R63 unexpectedly accepted page two');
|
|
73
|
+
if(label==='after'&&boundary.status!=='pass')errors.push('R64 cross-boundary paging failed');
|
|
74
|
+
rows.push({round,label,source_sha256:sha(await readFile(path.join(root,'src/machine-fs.mjs'))),
|
|
75
|
+
ops:latencies.length,ops_per_second:operations*1000/elapsed,p50_ms:percentile(latencies,.5),p95_ms:percentile(latencies,.95),
|
|
76
|
+
control_p95_ms:percentile(control,.95),peak_rss_bytes:peakRss,boundary,errors});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const report={schema:'deadbyte.r64.read-benchmark.v1',node:process.version,rounds,operations_per_round:operations,concurrency,
|
|
80
|
+
source_roots:{before:beforeRoot,after:afterRoot},rows,
|
|
81
|
+
limitations:['same-host sequential alternating runs; OS scheduling noise remains','comparable throughput rows use inherited sub-4-MiB reads; boundary timings are separate because R63 cannot execute page two','control probe uses governor and local read/hash, not public MCP machine_ping or machine_status']};
|
|
82
|
+
await writeFile(outputPath,JSON.stringify(report,null,2)+'\n',{flag:'wx'});
|
|
83
|
+
console.log(JSON.stringify({output_path:outputPath,rows:rows.length,errors:rows.reduce((sum,row)=>sum+row.errors.length,0)}));
|
|
84
|
+
}finally{await rm(scratch,{recursive:true,force:true});}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {mkdirSync,readdirSync,writeFileSync} from 'node:fs';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const runId=process.argv[2]??'manual';
|
|
7
|
+
if(!/^[A-Za-z0-9._-]{1,96}$/.test(runId))throw new Error('invalid run id');
|
|
8
|
+
const testDir=path.join(process.cwd(),'test');
|
|
9
|
+
const tests=readdirSync(testDir,{withFileTypes:true})
|
|
10
|
+
.filter(entry=>entry.isFile()&&entry.name.endsWith('.test.mjs'))
|
|
11
|
+
.map(entry=>path.join('test',entry.name))
|
|
12
|
+
.sort((a,b)=>a.localeCompare(b,'en'));
|
|
13
|
+
const startedAt=new Date().toISOString();
|
|
14
|
+
const run=spawnSync(process.execPath,['--test','--test-concurrency=4',...tests],{
|
|
15
|
+
cwd:process.cwd(),
|
|
16
|
+
encoding:'utf8',
|
|
17
|
+
maxBuffer:128*1024*1024,
|
|
18
|
+
windowsHide:true,
|
|
19
|
+
shell:false
|
|
20
|
+
});
|
|
21
|
+
const stdout=run.stdout||'',stderr=run.stderr||'';
|
|
22
|
+
const evidenceDir=path.resolve(process.cwd(),'..','..','testing','0.15.42-r65','full-runs',runId);
|
|
23
|
+
mkdirSync(evidenceDir,{recursive:true});
|
|
24
|
+
writeFileSync(path.join(evidenceDir,'stdout.log'),stdout,'utf8');
|
|
25
|
+
writeFileSync(path.join(evidenceDir,'stderr.log'),stderr,'utf8');
|
|
26
|
+
const grab=(name)=>{const m=stdout.match(new RegExp('(?:ℹ|#)\\s*'+name+'\\s+(\\d+)','i'));return m?Number(m[1]):null;};
|
|
27
|
+
const result={
|
|
28
|
+
schema:'deadbyte.r65-full-run.v1',
|
|
29
|
+
run_id:runId,
|
|
30
|
+
started_at_utc:startedAt,
|
|
31
|
+
finished_at_utc:new Date().toISOString(),
|
|
32
|
+
command:[process.execPath,'--test','--test-concurrency=4',...tests],
|
|
33
|
+
test_files:tests.length,
|
|
34
|
+
exit_code:run.status??null,
|
|
35
|
+
signal:run.signal??null,
|
|
36
|
+
error:run.error?String(run.error.stack||run.error):null,
|
|
37
|
+
tests:grab('tests'),pass:grab('pass'),fail:grab('fail'),skipped:grab('skipped'),
|
|
38
|
+
stdout_sha256:createHash('sha256').update(stdout).digest('hex'),
|
|
39
|
+
stderr_sha256:createHash('sha256').update(stderr).digest('hex')
|
|
40
|
+
};
|
|
41
|
+
writeFileSync(path.join(evidenceDir,'result.json'),JSON.stringify(result,null,2)+'\n','utf8');
|
|
42
|
+
process.stdout.write(stdout);
|
|
43
|
+
process.stderr.write(stderr);
|
|
44
|
+
if(run.error)process.stderr.write(String(run.error.stack||run.error)+'\n');
|
|
45
|
+
process.exit(run.status??1);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
const tests=[
|
|
4
|
+
'test/r65-activation-lock.test.mjs',
|
|
5
|
+
'test/r65-activation-lock-recovery.test.mjs',
|
|
6
|
+
'test/r65-activation-orchestration.test.mjs',
|
|
7
|
+
'test/r65-activation-preflight.test.mjs',
|
|
8
|
+
'test/r65-authority-continuity-decision.test.mjs',
|
|
9
|
+
'test/r65-authority-durable-policy-binding.test.mjs',
|
|
10
|
+
'test/r65-authority-epoch-cli.test.mjs',
|
|
11
|
+
'test/r65-authority-epoch-races.test.mjs',
|
|
12
|
+
'test/r65-authority-epoch-recovery.test.mjs',
|
|
13
|
+
'test/r65-authority-epoch-store.test.mjs',
|
|
14
|
+
'test/r65-authority-grant-capture.test.mjs',
|
|
15
|
+
'test/r65-authority-observation-epoch.test.mjs',
|
|
16
|
+
'test/r65-authority-policy-envelope.test.mjs',
|
|
17
|
+
'test/r65-authority-runtime-epoch.test.mjs',
|
|
18
|
+
'test/r65-authority-runtime-policy-binding.test.mjs',
|
|
19
|
+
'test/r65-authority-restored-safe.test.mjs',
|
|
20
|
+
'test/r65-controller-authority-epoch.test.mjs',
|
|
21
|
+
'test/r65-controller-authority-epoch-live.test.mjs',
|
|
22
|
+
'test/r65-deploy-preflight.test.mjs',
|
|
23
|
+
'test/r65-epoch-native-activation-integration.test.mjs',
|
|
24
|
+
'test/r65-epoch-native-continuity-engine.test.mjs',
|
|
25
|
+
'test/r65-epoch-native-cross-plane.test.mjs',
|
|
26
|
+
'test/r65-legacy-authority-migration.test.mjs',
|
|
27
|
+
'test/r65-legacy-auto-restore-rejection.test.mjs',
|
|
28
|
+
'test/r65-parent-source-verifier.test.mjs',
|
|
29
|
+
'test/r65-release-contract.test.mjs',
|
|
30
|
+
'test/r65-transaction-precondition.test.mjs'
|
|
31
|
+
];
|
|
32
|
+
const run=spawnSync(process.execPath,['--test','--test-concurrency=2',...tests],{
|
|
33
|
+
cwd:process.cwd(),encoding:'utf8',maxBuffer:64*1024*1024,windowsHide:true
|
|
34
|
+
});
|
|
35
|
+
process.stdout.write(run.stdout||'');
|
|
36
|
+
process.stderr.write(run.stderr||'');
|
|
37
|
+
process.exit(run.status??1);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {lstat,readFile,realpath} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
7
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
8
|
+
|
|
9
|
+
function parseArgs(argv){
|
|
10
|
+
const out={root:null,expectedManifestSha256:null};
|
|
11
|
+
for(let i=0;i<argv.length;i++){
|
|
12
|
+
const arg=argv[i];
|
|
13
|
+
if(arg==='--root')out.root=argv[++i]??null;
|
|
14
|
+
else if(arg==='--expected-manifest-sha256')out.expectedManifestSha256=argv[++i]??null;
|
|
15
|
+
else throw new Error('unknown argument '+arg);
|
|
16
|
+
}
|
|
17
|
+
if(typeof out.root!=='string'||out.root.length<1)throw new Error('--root required');
|
|
18
|
+
if(!HEX64.test(String(out.expectedManifestSha256)))throw new Error('--expected-manifest-sha256 must be lowercase hex');
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseManifest(text){
|
|
23
|
+
const entries=[];
|
|
24
|
+
const seen=new Set();
|
|
25
|
+
for(const raw of text.split(/\r?\n/)){
|
|
26
|
+
if(raw==='')continue;
|
|
27
|
+
const match=/^([0-9a-f]{64}) (.+)$/.exec(raw);
|
|
28
|
+
if(!match)throw new Error('manifest line invalid');
|
|
29
|
+
const rel=match[2];
|
|
30
|
+
if(rel.includes('\\')||path.posix.isAbsolute(rel)||rel==='.'||rel.split('/').includes('..')||path.posix.normalize(rel)!==rel)
|
|
31
|
+
throw new Error('manifest path invalid: '+rel);
|
|
32
|
+
if(seen.has(rel))throw new Error('manifest duplicate path: '+rel);
|
|
33
|
+
seen.add(rel);
|
|
34
|
+
entries.push({sha256:match[1],rel});
|
|
35
|
+
}
|
|
36
|
+
if(entries.length<1)throw new Error('manifest has no entries');
|
|
37
|
+
return entries;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function verifyDeclaredParentSource({root,expectedManifestSha256}){
|
|
41
|
+
const absolute=path.resolve(root);
|
|
42
|
+
const rootReal=await realpath(absolute);
|
|
43
|
+
const manifestPath=path.join(absolute,'MANIFEST.SHA256');
|
|
44
|
+
const manifestBytes=await readFile(manifestPath);
|
|
45
|
+
const actualManifestSha=sha(manifestBytes);
|
|
46
|
+
if(actualManifestSha!==expectedManifestSha256)
|
|
47
|
+
throw new Error(`manifest SHA-256 mismatch expected=${expectedManifestSha256} actual=${actualManifestSha}`);
|
|
48
|
+
const entries=parseManifest(manifestBytes.toString('utf8'));
|
|
49
|
+
for(const entry of entries){
|
|
50
|
+
const target=path.resolve(absolute,...entry.rel.split('/'));
|
|
51
|
+
const info=await lstat(target).catch(error=>{
|
|
52
|
+
if(error?.code==='ENOENT')throw new Error('listed file missing: '+entry.rel);
|
|
53
|
+
throw error;
|
|
54
|
+
});
|
|
55
|
+
if(!info.isFile()||info.isSymbolicLink())throw new Error('listed path is not a regular file: '+entry.rel);
|
|
56
|
+
const targetReal=await realpath(target);
|
|
57
|
+
const prefix=rootReal.endsWith(path.sep)?rootReal:rootReal+path.sep;
|
|
58
|
+
if(targetReal!==rootReal&&!targetReal.startsWith(prefix))throw new Error('listed path escapes root: '+entry.rel);
|
|
59
|
+
const actual=sha(await readFile(target));
|
|
60
|
+
if(actual!==entry.sha256)throw new Error(`hash mismatch ${entry.rel} expected=${entry.sha256} actual=${actual}`);
|
|
61
|
+
}
|
|
62
|
+
return Object.freeze({files:entries.length,manifest_sha256:actualManifestSha,root:rootReal});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===path.resolve(fileURLToPath(import.meta.url));
|
|
66
|
+
if(invoked){
|
|
67
|
+
try{
|
|
68
|
+
const opts=parseArgs(process.argv.slice(2));
|
|
69
|
+
const result=await verifyDeclaredParentSource(opts);
|
|
70
|
+
console.log(`R65_PARENT_SOURCE_VERIFY_OK files=${result.files} manifest_sha256=${result.manifest_sha256}`);
|
|
71
|
+
}catch(error){
|
|
72
|
+
console.error('R65_PARENT_SOURCE_VERIFY_FAIL '+String(error?.stack??error));
|
|
73
|
+
process.exitCode=1;
|
|
74
|
+
}
|
|
75
|
+
}
|