deadbyte-mcp 0.9.0 → 0.10.0
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/MANIFEST.SHA256 +119 -91
- package/README.txt +16 -11
- package/bench/fixtures/corpus.json +32 -13
- package/bench/fixtures/r36/case-map.json +8 -0
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +8 -6
- package/controller/deadbyte-controller.ps1 +48 -2
- package/controller/deadbyte-process-policy.json +5 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/R36-CAPABILITY-EVIDENCE.md +43 -0
- package/docs/superpowers/plans/2026-09-18-r36-zero-loss-autonomy.md +739 -0
- package/docs/superpowers/specs/2026-09-18-r36-zero-loss-autonomy-design.md +640 -0
- package/package.json +1 -1
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/deferred-slot-operation.mjs +53 -15
- package/scripts/final-closure-verify.mjs +88 -39
- package/scripts/final-closure.mjs +63 -56
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-tests.ps1 +9 -0
- package/scripts/release-parity.mjs +149 -139
- package/scripts/windows-gate-evidence.mjs +5 -5
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-mcp-tools.mjs +5 -1
- package/src/autonomous-output-contracts.mjs +4 -4
- package/src/autonomous-planner-contracts.mjs +36 -1
- package/src/autonomous-planner.mjs +25 -3
- package/src/autonomous-runtime.mjs +191 -21
- package/src/capability-benchmark-runner.mjs +86 -1
- package/src/capability-benchmark.mjs +45 -0
- package/src/capability-ledger.mjs +158 -0
- package/src/coding-mcp-tools.mjs +31 -17
- package/src/coding-output-contracts.mjs +48 -10
- package/src/coding-plane.mjs +45 -9
- package/src/coding-search.mjs +130 -47
- package/src/compact-gateway-mcp-tools.mjs +128 -0
- package/src/compact-gateway-output-contracts.mjs +16 -0
- package/src/deadbyte-cli.mjs +51 -28
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +51 -8
- package/src/mcp-server.mjs +32 -9
- package/src/native-diff-v2.mjs +337 -0
- package/src/native-diff.mjs +50 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +7 -1
- package/src/process-runtime.mjs +98 -11
- package/src/process-smoke-client.mjs +11 -3
- package/src/r36-compact-disarmed-probe.mjs +50 -0
- package/src/r36-full-surface-probe.mjs +56 -0
- package/src/remote-live-log.mjs +76 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +7 -3
- package/src/remote-mcp-coding-smoke-client.mjs +1 -1
- package/src/remote-mcp-r36-parity-client.mjs +175 -0
- package/src/remote-mcp-smoke-client.mjs +112 -56
- package/src/runtime-update.mjs +51 -1
- package/src/search-cursor.mjs +45 -0
- package/src/semantic-code.mjs +72 -5
- package/src/semantic-lsp-backend.mjs +39 -3
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +151 -0
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-benchmark-r36.test.mjs +107 -0
- package/test/capability-benchmark-semantic-r36.test.mjs +52 -0
- package/test/capability-claims-r36.test.mjs +23 -0
- package/test/capability-ledger-r36.test.mjs +102 -0
- package/test/coding-catalog-consistency.test.mjs +2 -2
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +16 -0
- package/test/coding-search-r36.test.mjs +90 -0
- package/test/compact-catalog-r36.test.mjs +37 -0
- package/test/compact-gateway-r36.test.mjs +66 -0
- package/test/contained.test.mjs +1 -1
- package/test/core.test.mjs +1 -1
- package/test/deferred-slot-operation.test.mjs +9 -0
- package/test/fixtures/lsp-framed-server.mjs +2 -0
- package/test/helpers/autonomous-r34-fixture.mjs +5 -3
- package/test/multi-file-read.test.mjs +3 -0
- package/test/native-diff-topology-r36.test.mjs +105 -0
- package/test/package-boundary.test.mjs +6 -0
- package/test/process-longrun-r36.test.mjs +82 -0
- package/test/process-release-gate.test.mjs +9 -0
- package/test/production-docs.test.mjs +3 -3
- package/test/prompts-resources.test.mjs +7 -1
- package/test/r33-closeout-regression.test.mjs +4 -3
- package/test/r33-finalization.test.mjs +14 -9
- package/test/r36-finalization.test.mjs +104 -0
- package/test/r36-release-identity.test.mjs +52 -0
- package/test/release-version.test.mjs +18 -15
- package/test/remote-live-log-r36.test.mjs +37 -0
- package/test/remote-live-log-recovery-r36.test.mjs +41 -0
- package/test/remote-r24-catalog.test.mjs +13 -0
- package/test/remote-session-cli.test.mjs +40 -30
- package/test/runtime-self-update.test.mjs +58 -1
- package/test/semantic-lsp-backend.test.mjs +9 -0
- package/test/semantic-r36-rename.test.mjs +82 -0
- package/deadbyte-mcp-0.8.9.tgz +0 -0
|
@@ -39,7 +39,7 @@ deadbyte-process-host.cpp
|
|
|
39
39
|
exit_code=0
|
|
40
40
|
==== probe-dependencies ====
|
|
41
41
|
|
|
42
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
42
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\containment-probe.exe
|
|
43
43
|
|
|
44
44
|
File Type: EXECUTABLE IMAGE
|
|
45
45
|
|
|
@@ -59,7 +59,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
59
59
|
exit_code=0
|
|
60
60
|
==== contained-launcher-dependencies ====
|
|
61
61
|
|
|
62
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
62
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\deadbyte-exec.exe
|
|
63
63
|
|
|
64
64
|
File Type: EXECUTABLE IMAGE
|
|
65
65
|
|
|
@@ -83,7 +83,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
83
83
|
exit_code=0
|
|
84
84
|
==== contained-worker-dependencies ====
|
|
85
85
|
|
|
86
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
86
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\contained-transform-worker.exe
|
|
87
87
|
|
|
88
88
|
File Type: EXECUTABLE IMAGE
|
|
89
89
|
|
|
@@ -104,7 +104,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
104
104
|
exit_code=0
|
|
105
105
|
==== contained-reverse-worker-dependencies ====
|
|
106
106
|
|
|
107
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
107
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\contained-reverse-worker.exe
|
|
108
108
|
|
|
109
109
|
File Type: EXECUTABLE IMAGE
|
|
110
110
|
|
|
@@ -125,7 +125,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
125
125
|
exit_code=0
|
|
126
126
|
==== named-tunnel-host-dependencies ====
|
|
127
127
|
|
|
128
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
128
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\deadbyte-tunnel-host.exe
|
|
129
129
|
|
|
130
130
|
File Type: EXECUTABLE IMAGE
|
|
131
131
|
|
|
@@ -144,7 +144,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
144
144
|
exit_code=0
|
|
145
145
|
==== process-host-dependencies ====
|
|
146
146
|
|
|
147
|
-
Dump of file D:\Projects\deadbyte-mcp-
|
|
147
|
+
Dump of file D:\Projects\deadbyte-mcp-r36-dev\bin\deadbyte-process-host.exe
|
|
148
148
|
|
|
149
149
|
File Type: EXECUTABLE IMAGE
|
|
150
150
|
|
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { parseActivePointer } from '../src/release-slot.mjs';
|
|
8
|
-
import { runSlotUpdateCli, verifySlotUpdateEnvelope } from '../src/runtime-update.mjs';
|
|
8
|
+
import { loadPowerShellDataFile, runSlotUpdateCli, verifySlotUpdateEnvelope } from '../src/runtime-update.mjs';
|
|
9
9
|
|
|
10
10
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
11
11
|
const RECEIPT_ID=/^r27-[A-Za-z0-9-]+$/;
|
|
@@ -22,11 +22,34 @@ const atomicJson=async(file,value)=>{
|
|
|
22
22
|
};
|
|
23
23
|
function activationArgs(manifestSha){
|
|
24
24
|
if(!HEX64.test(manifestSha??'')) throw new Error('activate requires one lowercase manifest SHA-256');
|
|
25
|
-
return ['--stage',packageRoot,'--development-root',packageRoot,'--expected-version','0.
|
|
25
|
+
return ['--stage',packageRoot,'--development-root',packageRoot,'--expected-version','0.10.0',
|
|
26
26
|
'--expected-manifest',manifestSha,'--process-policy',path.join(packageRoot,'controller','deadbyte-process-policy.json'),
|
|
27
27
|
'--process-policy-sha256',null,'--semantic-lsp-config',path.join(packageRoot,'controller','deadbyte-semantic-lsp-config.json'),
|
|
28
28
|
'--semantic-lsp-config-sha256',null,'--enable-machine-fs','--enable-observation','--enable-autonomous-supervisor'];
|
|
29
29
|
}
|
|
30
|
+
async function bridgeBootstrapArgs(explicitBridgeRoot=null,explicitBridgeSha256=null){
|
|
31
|
+
const hasRoot=typeof explicitBridgeRoot==='string'&&explicitBridgeRoot.length>0;
|
|
32
|
+
const hasSha=typeof explicitBridgeSha256==='string'&&explicitBridgeSha256.length>0;
|
|
33
|
+
if(hasRoot!==hasSha) throw new Error('bridge override requires exact root + SHA pair');
|
|
34
|
+
let bridgeRootRaw,expectedSha256=null;
|
|
35
|
+
if(hasRoot){
|
|
36
|
+
if(!path.isAbsolute(explicitBridgeRoot)||!HEX64.test(explicitBridgeSha256)) throw new Error('bridge override requires exact root + SHA pair');
|
|
37
|
+
bridgeRootRaw=explicitBridgeRoot;expectedSha256=explicitBridgeSha256;
|
|
38
|
+
}else{
|
|
39
|
+
const pointer=parseActivePointer(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
40
|
+
const config=await loadPowerShellDataFile(pointer.config_path);
|
|
41
|
+
bridgeRootRaw=config?.Bridge?.WorkingDirectory;
|
|
42
|
+
if(typeof bridgeRootRaw!=='string'||!path.isAbsolute(bridgeRootRaw)) throw new Error('active controller Bridge.WorkingDirectory must be absolute');
|
|
43
|
+
}
|
|
44
|
+
const bridgeRoot=await realpath(path.resolve(bridgeRootRaw));
|
|
45
|
+
const packageFile=path.join(bridgeRoot,'package.json');
|
|
46
|
+
const bridgeScript=path.join(bridgeRoot,'src','bridge.mjs');
|
|
47
|
+
const pkg=JSON.parse(await readFile(packageFile,'utf8'));
|
|
48
|
+
if(pkg?.name!=='deadbyte-chatgpt-bridge') throw new Error('active Bridge package identity mismatch');
|
|
49
|
+
const scriptSha256=sha256(await readFile(bridgeScript));
|
|
50
|
+
if(expectedSha256&&scriptSha256!==expectedSha256) throw new Error('bridge override SHA-256 mismatch');
|
|
51
|
+
return ['--bridge-root',bridgeRoot,'--bridge-script-sha256',scriptSha256];
|
|
52
|
+
}
|
|
30
53
|
async function rollbackArgs(receiptPath){
|
|
31
54
|
const resolved=await realpath(path.resolve(receiptPath));
|
|
32
55
|
const allowed=path.join(stateRoot,'evidence','updates')+path.sep;
|
|
@@ -42,45 +65,60 @@ async function rollbackArgs(receiptPath){
|
|
|
42
65
|
}
|
|
43
66
|
return ['--rollback-receipt',resolved];
|
|
44
67
|
}
|
|
45
|
-
async function normalizedArgs(mode,value){
|
|
68
|
+
async function normalizedArgs(mode,value,{explicitBridgeRoot=null,explicitBridgeSha256=null}={}){
|
|
46
69
|
if(mode==='activate'){
|
|
47
70
|
const args=activationArgs(value);
|
|
48
71
|
args[args.indexOf('--process-policy-sha256')+1]=sha256(await readFile(path.join(packageRoot,'controller','deadbyte-process-policy.json')));
|
|
49
72
|
args[args.indexOf('--semantic-lsp-config-sha256')+1]=sha256(await readFile(path.join(packageRoot,'controller','deadbyte-semantic-lsp-config.json')));
|
|
73
|
+
args.push(...await bridgeBootstrapArgs(explicitBridgeRoot,explicitBridgeSha256));
|
|
50
74
|
return args;
|
|
51
75
|
}
|
|
52
|
-
if(mode==='rollback')
|
|
76
|
+
if(mode==='rollback'){
|
|
77
|
+
if(explicitBridgeRoot||explicitBridgeSha256) throw new Error('rollback does not accept bridge override');
|
|
78
|
+
return rollbackArgs(value);
|
|
79
|
+
}
|
|
53
80
|
throw new Error("operation must be exactly 'activate' or 'rollback'");
|
|
54
81
|
}
|
|
55
82
|
const rawArgs=process.argv.slice(2);
|
|
56
|
-
const [
|
|
57
|
-
if(!value||!['activate','rollback'].includes(mode)
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
const mode=rawArgs[0],value=rawArgs[1];
|
|
84
|
+
if(!value||!['activate','rollback'].includes(mode)) throw new Error('usage: deferred-slot-operation.mjs <activate manifest_sha256|rollback activation_receipt> [--bridge-root ABS --bridge-script-sha256 HEX64]');
|
|
85
|
+
let cursor=2,explicitBridgeRoot=null,explicitBridgeSha256=null;
|
|
86
|
+
if(rawArgs[cursor]==='--bridge-root'){
|
|
87
|
+
explicitBridgeRoot=rawArgs[cursor+1]??null;
|
|
88
|
+
if(rawArgs[cursor+2]!=='--bridge-script-sha256') throw new Error('bridge override requires exact root + SHA pair');
|
|
89
|
+
explicitBridgeSha256=rawArgs[cursor+3]??null;cursor+=4;
|
|
60
90
|
}
|
|
61
|
-
|
|
91
|
+
let workerFlag=null,operationIdArg=null;
|
|
92
|
+
if(rawArgs[cursor]==='--worker'){workerFlag='--worker';operationIdArg=rawArgs[cursor+1]??null;cursor+=2;}
|
|
93
|
+
if(cursor!==rawArgs.length) throw new Error('deferred slot operation arguments invalid');
|
|
94
|
+
if((explicitBridgeRoot===null)!==(explicitBridgeSha256===null)) throw new Error('bridge override requires exact root + SHA pair');
|
|
95
|
+
if(mode==='rollback'&&(explicitBridgeRoot||explicitBridgeSha256)) throw new Error('rollback does not accept bridge override');
|
|
62
96
|
if(workerFlag==='--worker'){
|
|
63
97
|
if(!/^[0-9a-f]{32}$/.test(operationIdArg??'')) throw new Error('deferred worker operation id invalid');
|
|
64
98
|
const statePath=path.join(evidenceRoot,`${operationIdArg}.json`);
|
|
65
99
|
await new Promise(resolve=>setTimeout(resolve,4000));
|
|
66
|
-
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'running',started_at_utc:new Date().toISOString()});
|
|
100
|
+
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'running',started_at_utc:new Date().toISOString(),bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
67
101
|
try{
|
|
102
|
+
const args=await normalizedArgs(mode,value,{explicitBridgeRoot,explicitBridgeSha256});
|
|
68
103
|
const result=await runSlotUpdateCli(args,{stateRoot});
|
|
69
104
|
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'completed',
|
|
70
105
|
completed_at_utc:new Date().toISOString(),receipt_path:result.receipt_path,receipt_sha256:result.receipt_sha256,
|
|
71
|
-
active_release:result.pointer.release_id,generation_root:result.pointer.generation_root});
|
|
106
|
+
active_release:result.pointer.release_id,generation_root:result.pointer.generation_root,bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
72
107
|
}catch(error){
|
|
73
108
|
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'failed',
|
|
74
|
-
completed_at_utc:new Date().toISOString(),error:String(error?.message??error)});
|
|
109
|
+
completed_at_utc:new Date().toISOString(),error:String(error?.message??error),bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
75
110
|
process.exitCode=1;
|
|
76
111
|
}
|
|
77
112
|
}else{
|
|
78
113
|
const operationId=randomBytes(16).toString('hex');
|
|
79
114
|
const statePath=path.join(evidenceRoot,`${operationId}.json`);
|
|
80
|
-
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationId,mode,state:'scheduled',scheduled_at_utc:new Date().toISOString()});
|
|
81
|
-
const
|
|
115
|
+
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationId,mode,state:'scheduled',scheduled_at_utc:new Date().toISOString(),bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
116
|
+
const workerArgs=[fileURLToPath(import.meta.url),mode,value];
|
|
117
|
+
if(explicitBridgeRoot) workerArgs.push('--bridge-root',explicitBridgeRoot,'--bridge-script-sha256',explicitBridgeSha256);
|
|
118
|
+
workerArgs.push('--worker',operationId);
|
|
119
|
+
const child=spawn(process.execPath,workerArgs,{
|
|
82
120
|
cwd:packageRoot,detached:true,stdio:'ignore',windowsHide:true,env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}
|
|
83
121
|
});
|
|
84
122
|
child.unref();
|
|
85
|
-
console.log(JSON.stringify({status:'scheduled',operation_id:operationId,mode,state_path:statePath,execution_provider:'deadbyte_mcp',rdc_operation_count:0}));
|
|
123
|
+
console.log(JSON.stringify({status:'scheduled',operation_id:operationId,mode,state_path:statePath,execution_provider:'deadbyte_mcp',rdc_operation_count:0,bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256}));
|
|
86
124
|
}
|
|
@@ -6,20 +6,32 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
|
|
7
7
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
8
8
|
const UPDATE_DOMAIN=Buffer.from('DEADBYTE-R27-SLOT-UPDATE-V1\0','utf8');
|
|
9
|
-
const CLOSURE_DOMAIN=Buffer.from('DEADBYTE-
|
|
9
|
+
const CLOSURE_DOMAIN=Buffer.from('DEADBYTE-R36-FINAL-CLOSURE-V1\0','utf8');
|
|
10
10
|
const PING_DOMAIN=Buffer.from('DEADBYTE-MACHINE-PING-V1\0','utf8');
|
|
11
|
+
const R35_PREDECESSOR_RELEASE_ID='v0.9.0-b95e1ec1d9a7e26a';
|
|
12
|
+
const R35_PREDECESSOR_MANIFEST='b95e1ec1d9a7e26a96ef492306c1be4df18c883ff03ab3b6015f677d7429ec8d';
|
|
13
|
+
const R36_FULL_TOOL_COUNT=76;
|
|
14
|
+
const R36_COMPACT_TOOL_LIMIT=42;
|
|
15
|
+
const REQUIRED_PARITY_KEYS=['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface'];
|
|
11
16
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
17
|
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
13
18
|
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
14
19
|
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
15
20
|
const same=(a,b)=>canonicalJson(a)===canonicalJson(b);
|
|
16
|
-
function insist(ok,msg){
|
|
21
|
+
function insist(ok,msg){if(!ok) throw new Error(msg);}
|
|
22
|
+
function samePath(a,b){return path.resolve(String(a)).toLowerCase()===path.resolve(String(b)).toLowerCase();}
|
|
23
|
+
function isCandidateIdentity(value,manifest){
|
|
24
|
+
return value?.version==='0.10.0'&&value?.manifest_sha256===manifest&&value?.release_id===`v0.10.0-${manifest.slice(0,16)}`;
|
|
25
|
+
}
|
|
26
|
+
function isR35Identity(value){
|
|
27
|
+
return value?.version==='0.9.0'&&value?.manifest_sha256===R35_PREDECESSOR_MANIFEST&&value?.release_id===R35_PREDECESSOR_RELEASE_ID;
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
function parseManifest(text){
|
|
19
31
|
const out=new Map();
|
|
20
32
|
for(const [i,line] of text.split(/\r?\n/).entries()){
|
|
21
33
|
if(!line) continue;
|
|
22
|
-
const m=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
34
|
+
const m=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);insist(m,`manifest line ${i+1} malformed`);
|
|
23
35
|
insist(!path.posix.isAbsolute(m[2])&&!m[2].split('/').includes('..')&&!out.has(m[2]),`unsafe/duplicate manifest path ${m[2]}`);
|
|
24
36
|
out.set(m[2],m[1]);
|
|
25
37
|
}
|
|
@@ -32,9 +44,9 @@ async function walk(root,rel=''){
|
|
|
32
44
|
for(const name of names){
|
|
33
45
|
const childRel=rel?`${rel}/${name}`:name;
|
|
34
46
|
const full=path.join(root,...childRel.split('/'));
|
|
35
|
-
const st=await lstat(full);
|
|
47
|
+
const st=await lstat(full);insist(!st.isSymbolicLink(),`symlink forbidden: ${childRel}`);
|
|
36
48
|
if(st.isDirectory()) files.push(...await walk(root,childRel));
|
|
37
|
-
else
|
|
49
|
+
else{insist(st.isFile(),`non-file forbidden: ${childRel}`);files.push(childRel);}
|
|
38
50
|
}
|
|
39
51
|
return files;
|
|
40
52
|
}
|
|
@@ -45,13 +57,11 @@ async function verifyTree(root,manifestName,expectedDigest){
|
|
|
45
57
|
const actual=(await walk(root)).filter(rel=>rel!==manifestName).sort((a,b)=>a.localeCompare(b,'en'));
|
|
46
58
|
const expected=[...entries.keys()].sort((a,b)=>a.localeCompare(b,'en'));
|
|
47
59
|
insist(same(actual,expected),`${manifestName} exact tree mismatch`);
|
|
48
|
-
for(const [rel,digest] of entries){
|
|
49
|
-
insist(sha256(await readFile(path.join(root,...rel.split('/'))))===digest,`${manifestName} file hash mismatch: ${rel}`);
|
|
50
|
-
}
|
|
60
|
+
for(const [rel,digest] of entries) insist(sha256(await readFile(path.join(root,...rel.split('/'))))===digest,`${manifestName} file hash mismatch: ${rel}`);
|
|
51
61
|
return entries.size;
|
|
52
62
|
}
|
|
53
63
|
function publicKeyInfo(publicPem){
|
|
54
|
-
const key=createPublicKey(publicPem);
|
|
64
|
+
const key=createPublicKey(publicPem);insist(key.asymmetricKeyType==='ed25519','public key must be Ed25519');
|
|
55
65
|
const der=Buffer.from(key.export({type:'spki',format:'der'}));
|
|
56
66
|
return {key,key_id:sha256(der),spki_base64:der.toString('base64')};
|
|
57
67
|
}
|
|
@@ -71,8 +81,7 @@ function verifyPing(ping,status,pointer){
|
|
|
71
81
|
insist(status?.status==='ok'&&typeof status.public_key_spki_base64==='string','machine status malformed');
|
|
72
82
|
const der=Buffer.from(status.public_key_spki_base64,'base64');
|
|
73
83
|
insist(der.toString('base64')===status.public_key_spki_base64,'machine SPKI base64 non-canonical');
|
|
74
|
-
const machineKey=createPublicKey({key:der,format:'der',type:'spki'});
|
|
75
|
-
insist(machineKey.asymmetricKeyType==='ed25519','machine key must be Ed25519');
|
|
84
|
+
const machineKey=createPublicKey({key:der,format:'der',type:'spki'});insist(machineKey.asymmetricKeyType==='ed25519','machine key must be Ed25519');
|
|
76
85
|
const machineId=sha256(der);
|
|
77
86
|
insist(status.machine_id===machineId&&ping.machine_id===machineId&&ping.attestation?.key_id===machineId,'machine id/key mismatch');
|
|
78
87
|
insist(ping.release_id===pointer.release_id&&ping.release_manifest_sha256===pointer.manifest_sha256,'machine ping release binding mismatch');
|
|
@@ -83,77 +92,117 @@ function verifyPing(ping,status,pointer){
|
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
export async function verifyFinalClosureObject(envelope,{stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
|
|
86
|
-
insist(envelope?.body?.schema==='deadbyte.
|
|
87
|
-
insist(envelope.body.version==='0.
|
|
88
|
-
insist(envelope.body.release_label==='
|
|
95
|
+
insist(envelope?.body?.schema==='deadbyte.r36-final-closure.v1','closure schema mismatch');
|
|
96
|
+
insist(envelope.body.version==='0.10.0','closure version mismatch');
|
|
97
|
+
insist(envelope.body.release_label==='R36 FINAL MACHINE BASELINE','closure release label mismatch');
|
|
89
98
|
const keyInfo=publicKeyInfo(await readFile(path.join(stateRoot,'trust','ed25519-public.pem')));
|
|
90
99
|
const bodyBytes=Buffer.from(canonicalJson(envelope.body),'utf8');
|
|
91
100
|
insist(sha256(bodyBytes)===envelope.body_sha256,'closure body hash mismatch');
|
|
92
101
|
const {closure_sha256,...unsigned}=envelope;
|
|
93
102
|
insist(sha256(Buffer.from(canonicalJson(unsigned),'utf8'))===closure_sha256,'closure envelope hash mismatch');
|
|
94
|
-
insist(envelope.attestation?.algorithm==='ed25519'&&envelope.attestation?.domain==='DEADBYTE-
|
|
103
|
+
insist(envelope.attestation?.algorithm==='ed25519'&&envelope.attestation?.domain==='DEADBYTE-R36-FINAL-CLOSURE-V1','closure attestation contract mismatch');
|
|
95
104
|
insist(envelope.attestation.key_id===keyInfo.key_id,'closure signer mismatch');
|
|
96
105
|
insist(cryptoVerify(null,Buffer.concat([CLOSURE_DOMAIN,bodyBytes]),keyInfo.key,Buffer.from(envelope.attestation.signature_base64,'base64')),'closure signature invalid');
|
|
97
106
|
|
|
98
107
|
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
99
108
|
insist(same(pointer,envelope.body.active_pointer),'active pointer drift');
|
|
100
|
-
insist(pointer.version==='0.
|
|
101
|
-
insist(pointer
|
|
109
|
+
insist(pointer.version==='0.10.0','active pointer version mismatch');
|
|
110
|
+
insist(isCandidateIdentity(pointer,pointer.manifest_sha256),'release id/content address mismatch');
|
|
102
111
|
const releaseFiles=await verifyTree(pointer.release_root,'MANIFEST.SHA256',pointer.manifest_sha256);
|
|
103
112
|
const generationFiles=await verifyTree(pointer.generation_root,'GENERATION.SHA256',pointer.generation_manifest_sha256);
|
|
104
113
|
insist(sha256(await readFile(path.join(stateRoot,'bin','DEADBYTE-MCP.ps1')))===pointer.launcher_sha256,'launcher hash mismatch');
|
|
114
|
+
|
|
115
|
+
const predecessor=envelope.body.predecessor;
|
|
116
|
+
insist(isR35Identity(predecessor),'closure predecessor is not exact sealed R35');
|
|
117
|
+
const immutable=envelope.body.immutable_slot;
|
|
118
|
+
insist(immutable?.protected===true,'closure immutable-slot protection missing');
|
|
119
|
+
insist(samePath(immutable.release_root,pointer.release_root),'closure immutable release_root mismatch');
|
|
120
|
+
insist(!samePath(immutable.development_root,immutable.release_root),'closure development_root aliases immutable release_root');
|
|
121
|
+
|
|
122
|
+
const surfaces=envelope.body.surfaces;
|
|
123
|
+
insist(surfaces?.local_full?.status==='passed'&&surfaces.local_full.tool_count===R36_FULL_TOOL_COUNT&&surfaces.local_full.strict_output_schemas===true,'closure local full 76-tool proof invalid');
|
|
124
|
+
insist(HEX64.test(surfaces.local_full.catalog_sha256??''),'closure local full catalog hash missing');
|
|
125
|
+
insist(surfaces?.public_compact?.status==='passed'&&surfaces.public_compact.tool_count<=R36_COMPACT_TOOL_LIMIT&&surfaces.public_compact.capability_closure===true,'closure public compact capability proof invalid');
|
|
126
|
+
insist(surfaces.public_compact.coding_armed===false&&surfaces.public_compact.autonomous_armed===false,'closure compact authority is not DISARMED');
|
|
127
|
+
insist(HEX64.test(surfaces.public_compact.catalog_sha256??'')&&HEX64.test(surfaces.public_compact.ledger_sha256??''),'closure compact catalog/ledger hash missing');
|
|
128
|
+
|
|
105
129
|
const evidence=envelope.body.evidence;
|
|
106
130
|
insist(evidence&&evidence.parity&&evidence.activation&&evidence.rollback&&evidence.restore&&evidence.windows_gate,'closure evidence set incomplete');
|
|
107
131
|
for(const [name,item] of Object.entries(evidence)){
|
|
108
|
-
|
|
132
|
+
insist(item?.path&&HEX64.test(item.sha256??''),`${name} evidence identity malformed`);
|
|
109
133
|
insist(sha256(await readFile(item.path))===item.sha256,`${name} evidence hash mismatch`);
|
|
110
134
|
}
|
|
135
|
+
|
|
111
136
|
const parity=JSON.parse(await readFile(evidence.parity.path,'utf8'));
|
|
112
|
-
insist(parity.schema==='deadbyte.
|
|
113
|
-
insist(parity.
|
|
114
|
-
insist(parity.
|
|
115
|
-
insist(parity.
|
|
116
|
-
|
|
137
|
+
insist(parity.schema==='deadbyte.r36-parity.v1'&&parity.status==='passed','R36 parity evidence invalid');
|
|
138
|
+
insist(parity.rdc_operation_count===0&&parity.execution_provider==='deadbyte_mcp','R36 parity execution provider mismatch');
|
|
139
|
+
insist(isCandidateIdentity(parity.candidate,pointer.manifest_sha256),'R36 parity candidate binding mismatch');
|
|
140
|
+
insist(isCandidateIdentity(parity.successor,pointer.manifest_sha256),'R36 parity successor binding mismatch');
|
|
141
|
+
insist(isR35Identity(parity.predecessor),'R36 parity predecessor binding mismatch');
|
|
142
|
+
insist(parity.local_full?.status==='passed'&&parity.local_full.tool_count===R36_FULL_TOOL_COUNT&&parity.local_full.strict_output_schemas===true,'R36 parity local full proof invalid');
|
|
143
|
+
insist(parity.local_full.catalog_sha256===surfaces.local_full.catalog_sha256,'R36 local full catalog drift between parity and closure');
|
|
144
|
+
insist(parity.public_compact?.status==='passed'&&parity.public_compact.tool_count<=R36_COMPACT_TOOL_LIMIT&&parity.public_compact.capability_closure===true&&parity.public_compact.gateway_count===5,'R36 parity public compact proof invalid');
|
|
145
|
+
insist(parity.successor_disarmed?.status==='passed'&&parity.successor_disarmed.tool_count<=R36_COMPACT_TOOL_LIMIT&&parity.successor_disarmed.capability_closure===true&&parity.successor_disarmed.coding_armed===false&&parity.successor_disarmed.autonomous_armed===false,'R36 parity successor disarmed proof invalid');
|
|
146
|
+
insist(parity.successor_disarmed.catalog_sha256===surfaces.public_compact.catalog_sha256,'R36 compact catalog drift between restore and closure');
|
|
147
|
+
insist(parity.successor_disarmed.ledger_sha256===surfaces.public_compact.ledger_sha256,'R36 capability ledger drift between restore and closure');
|
|
148
|
+
insist(parity.immutable_slot?.protected===true,'R36 parity immutable-slot proof missing');
|
|
149
|
+
insist(samePath(parity.immutable_slot.release_root,pointer.release_root),'R36 parity immutable release_root mismatch');
|
|
150
|
+
insist(samePath(parity.immutable_slot.development_root,immutable.development_root),'R36 parity/closure development_root mismatch');
|
|
151
|
+
insist(!samePath(parity.immutable_slot.development_root,pointer.release_root),'R36 mutable development_root aliases immutable release slot');
|
|
152
|
+
for(const key of REQUIRED_PARITY_KEYS) insist(parity.requirements?.[key]===true,`R36 parity requirement missing ${key}`);
|
|
117
153
|
|
|
118
154
|
const activation=JSON.parse(await readFile(evidence.activation.path,'utf8'));
|
|
119
155
|
const rollback=JSON.parse(await readFile(evidence.rollback.path,'utf8'));
|
|
120
156
|
const restore=JSON.parse(await readFile(evidence.restore.path,'utf8'));
|
|
121
|
-
verifyUpdateEnvelope(activation,keyInfo);
|
|
122
|
-
insist(activation.body.schema==='deadbyte.r27-slot-update-receipt.v1'&&activation.body.rollback_performed===false,'activation receipt invalid');
|
|
123
|
-
insist(
|
|
124
|
-
insist(
|
|
125
|
-
insist(activation.body.
|
|
126
|
-
insist(
|
|
127
|
-
insist(
|
|
128
|
-
|
|
157
|
+
verifyUpdateEnvelope(activation,keyInfo);verifyUpdateEnvelope(rollback,keyInfo);verifyUpdateEnvelope(restore,keyInfo);
|
|
158
|
+
insist(activation.body.schema==='deadbyte.r27-slot-update-receipt.v1'&&activation.body.rollback_performed===false,'R36 activation receipt invalid');
|
|
159
|
+
insist(activation.body.release_version==='0.10.0'&&activation.body.candidate_manifest_sha256===pointer.manifest_sha256,'R36 activation candidate identity invalid');
|
|
160
|
+
insist(isCandidateIdentity(activation.body.active_pointer,pointer.manifest_sha256),'R36 activation active pointer invalid');
|
|
161
|
+
insist(isR35Identity(activation.body.prior_pointer),'R36 activation predecessor is not exact R35');
|
|
162
|
+
insist(samePath(activation.body.development_root,immutable.development_root),'R36 activation development_root mismatch');
|
|
163
|
+
insist(!samePath(activation.body.development_root,activation.body.active_pointer.release_root),'R36 activation development_root aliases release slot');
|
|
164
|
+
|
|
165
|
+
insist(rollback.body.schema==='deadbyte.r31-slot-rollback-receipt.v1'&&rollback.body.rollback_performed===true,'R36 rollback receipt invalid');
|
|
166
|
+
insist(isCandidateIdentity(rollback.body.from_pointer,pointer.manifest_sha256),'R36 rollback source is not exact candidate');
|
|
167
|
+
insist(isR35Identity(rollback.body.active_pointer),'R36 rollback target is not exact R35 predecessor');
|
|
168
|
+
insist(rollback.body.activation_receipt_sha256===activation.receipt_sha256,'R36 rollback not bound to activation receipt');
|
|
169
|
+
|
|
170
|
+
insist(restore.body.schema==='deadbyte.r27-slot-update-receipt.v1'&&restore.body.rollback_performed===false,'R36 restore receipt invalid');
|
|
171
|
+
insist(restore.body.release_version==='0.10.0'&&restore.body.candidate_manifest_sha256===pointer.manifest_sha256,'R36 restore candidate identity invalid');
|
|
172
|
+
insist(isR35Identity(restore.body.prior_pointer),'R36 restore predecessor is not exact R35');
|
|
173
|
+
insist(isCandidateIdentity(restore.body.active_pointer,pointer.manifest_sha256)&&same(restore.body.active_pointer,pointer),'R36 restore/current pointer mismatch');
|
|
174
|
+
insist(samePath(restore.body.development_root,immutable.development_root),'R36 restore development_root mismatch');
|
|
175
|
+
insist(!samePath(restore.body.development_root,restore.body.active_pointer.release_root),'R36 restore development_root aliases release slot');
|
|
176
|
+
|
|
129
177
|
const windowsGate=JSON.parse(await readFile(evidence.windows_gate.path,'utf8'));
|
|
130
|
-
insist(windowsGate.schema==='deadbyte.
|
|
131
|
-
insist(windowsGate.manifest_sha256===pointer.manifest_sha256,'Windows gate manifest binding mismatch');
|
|
132
|
-
insist(windowsGate.marker==='DEADBYTE V0.
|
|
178
|
+
insist(windowsGate.schema==='deadbyte.r36-windows-gate.v1'&&windowsGate.status==='passed'&&windowsGate.exit_code===0,'R36 Windows gate evidence invalid');
|
|
179
|
+
insist(windowsGate.version==='0.10.0'&&windowsGate.manifest_sha256===pointer.manifest_sha256,'R36 Windows gate manifest/version binding mismatch');
|
|
180
|
+
insist(windowsGate.marker==='DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS','R36 Windows gate PASS marker mismatch');
|
|
133
181
|
|
|
134
182
|
const machine=envelope.body.machine;
|
|
135
|
-
insist(machine?.status?.release_id===pointer.release_id&&machine.status.generation_manifest_sha256===pointer.generation_manifest_sha256,'machine status binding mismatch');
|
|
183
|
+
insist(machine?.status?.release_id===pointer.release_id&&machine.status.release_manifest_sha256===pointer.manifest_sha256&&machine.status.generation_manifest_sha256===pointer.generation_manifest_sha256,'machine status binding mismatch');
|
|
136
184
|
verifyPing(machine.ping,machine.status,pointer);
|
|
137
185
|
insist(machine.ping.nonce===machine.nonce,'machine ping nonce binding mismatch');
|
|
138
186
|
|
|
139
187
|
const grantNames=['process-grant.json','coding-grant.json','autonomous-grant.json','host-grant.json'];
|
|
140
188
|
insist(envelope.body.authority?.all_disarmed===true,'closure authority not disarmed');
|
|
141
189
|
for(const name of grantNames){
|
|
142
|
-
let exists=true;
|
|
190
|
+
let exists=true;try{await lstat(path.join(stateRoot,'runtime',name));}catch(error){if(error?.code==='ENOENT') exists=false;else throw error;}
|
|
143
191
|
insist(!exists,`authority grant still present: ${name}`);
|
|
144
192
|
insist(envelope.body.authority.grants_absent?.[name]===true,`closure grant absence not recorded: ${name}`);
|
|
145
193
|
}
|
|
146
194
|
return {ok:true,status:'passed',release_id:pointer.release_id,manifest_sha256:pointer.manifest_sha256,
|
|
147
195
|
generation_manifest_sha256:pointer.generation_manifest_sha256,release_files:releaseFiles,generation_files:generationFiles,
|
|
148
|
-
machine_id:machine.status.machine_id,parity_sha256:evidence.parity.sha256,closure_sha256:envelope.closure_sha256
|
|
196
|
+
machine_id:machine.status.machine_id,parity_sha256:evidence.parity.sha256,closure_sha256:envelope.closure_sha256,
|
|
197
|
+
full_tool_count:R36_FULL_TOOL_COUNT,compact_tool_count:surfaces.public_compact.tool_count};
|
|
149
198
|
}
|
|
150
199
|
|
|
151
200
|
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
152
201
|
if(invoked){
|
|
153
202
|
const file=process.argv[2];
|
|
154
203
|
if(!file) throw new Error('usage: node scripts/final-closure-verify.mjs <closure.json>');
|
|
155
|
-
try
|
|
204
|
+
try{
|
|
156
205
|
const envelope=JSON.parse(await readFile(path.resolve(file),'utf8'));
|
|
157
206
|
console.log(JSON.stringify(await verifyFinalClosureObject(envelope),null,2));
|
|
158
|
-
}
|
|
207
|
+
}catch(error){console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;}
|
|
159
208
|
}
|
|
@@ -1,96 +1,103 @@
|
|
|
1
|
-
import { createHash, createPrivateKey, createPublicKey,
|
|
1
|
+
import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign } from 'node:crypto';
|
|
2
2
|
import { lstat, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { probeR36FullSurface } from '../src/r36-full-surface-probe.mjs';
|
|
7
|
+
import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
|
|
8
|
+
import {
|
|
9
|
+
validateParityEvidence,
|
|
10
|
+
R35_PREDECESSOR_RELEASE_ID,
|
|
11
|
+
R35_PREDECESSOR_MANIFEST
|
|
12
|
+
} from './release-parity.mjs';
|
|
8
13
|
|
|
9
|
-
const DOMAIN=Buffer.from('DEADBYTE-
|
|
14
|
+
const DOMAIN=Buffer.from('DEADBYTE-R36-FINAL-CLOSURE-V1\0','utf8');
|
|
10
15
|
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
11
16
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
17
|
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
13
18
|
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
14
19
|
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
15
|
-
function insist(ok,msg){
|
|
16
|
-
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
function insist(ok,msg){if(!ok) throw new Error(msg);}
|
|
21
|
+
function samePath(a,b){return path.resolve(String(a)).toLowerCase()===path.resolve(String(b)).toLowerCase();}
|
|
22
|
+
async function fileEvidence(file){const abs=path.resolve(file),bytes=await readFile(abs);return {path:abs,sha256:sha256(bytes)};}
|
|
23
|
+
async function publicUrl(){
|
|
24
|
+
const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();
|
|
25
|
+
const url=new URL(value);insist(url.protocol==='https:','final closure requires HTTPS public endpoint');return value;
|
|
21
26
|
}
|
|
22
|
-
async function publicMachineEvidence(){
|
|
23
|
-
const endpoint=new URL((await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim());
|
|
24
|
-
insist(endpoint.protocol==='https:','final closure requires HTTPS public endpoint');
|
|
25
|
-
const client=new Client({name:'deadbyte-r35-final-closure',version:'0.9.0'},
|
|
26
|
-
{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
27
|
-
const transport=new StreamableHTTPClientTransport(endpoint);
|
|
28
|
-
try{
|
|
29
|
-
await client.connect(transport);
|
|
30
|
-
const listed=await client.listTools();
|
|
31
|
-
const names=listed.tools.map(tool=>tool.name).sort();
|
|
32
|
-
insist(JSON.stringify(names)===JSON.stringify(R34_EXPECTED_TOOLS),'final public catalog is not exact 73');
|
|
33
|
-
for(const tool of listed.tools) insist(tool.outputSchema?.type==='object'&&tool.outputSchema?.additionalProperties===false,`tool ${tool.name} output schema drift`);
|
|
34
|
-
const status=structured(await client.callTool({name:'machine_status',arguments:{}}),'machine_status');
|
|
35
|
-
const nonce=`r35-final-${randomBytes(16).toString('hex')}`;
|
|
36
|
-
const ping=structured(await client.callTool({name:'machine_ping',arguments:{nonce}}),'machine_ping');
|
|
37
|
-
const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding_session_status');
|
|
38
|
-
const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous_session_status');
|
|
39
|
-
insist(coding.armed===false,'coding authority must be DISARMED before closure');
|
|
40
|
-
insist(autonomous.coding_armed===false&&autonomous.autonomous_armed===false,'autonomous authority must be DISARMED before closure');
|
|
41
|
-
return {tool_count:names.length,strict_output_schemas:true,status,ping,nonce};
|
|
42
|
-
} finally { await client.close().catch(()=>{}); }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
27
|
async function grantAbsence(){
|
|
46
28
|
const out={};
|
|
47
29
|
for(const name of ['process-grant.json','coding-grant.json','autonomous-grant.json','host-grant.json']){
|
|
48
|
-
let absent=false;
|
|
49
|
-
|
|
50
|
-
insist(absent,`authority grant still present: ${name}`); out[name]=true;
|
|
30
|
+
let absent=false;try{await lstat(path.join(stateRoot,'runtime',name));}catch(error){if(error?.code==='ENOENT') absent=true;else throw error;}
|
|
31
|
+
insist(absent,`authority grant still present: ${name}`);out[name]=true;
|
|
51
32
|
}
|
|
52
33
|
return out;
|
|
53
34
|
}
|
|
54
35
|
async function atomicCreate(file,bytes){
|
|
55
36
|
await mkdir(path.dirname(file),{recursive:true});
|
|
56
|
-
const temp=`${file}.tmp-${process.pid}-${
|
|
37
|
+
const temp=`${file}.tmp-${process.pid}-${Math.random().toString(16).slice(2)}`;
|
|
57
38
|
const handle=await open(temp,'wx');
|
|
58
|
-
try
|
|
59
|
-
try
|
|
39
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
40
|
+
try{await rename(temp,file);}catch(error){await rm(temp,{force:true}).catch(()=>{});throw error;}
|
|
60
41
|
}
|
|
61
42
|
|
|
62
43
|
export async function writeFinalClosure({parityPath,activationPath,windowsGatePath}){
|
|
63
44
|
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
64
|
-
insist(pointer.version==='0.
|
|
45
|
+
insist(pointer.version==='0.10.0','final closure requires V0.10.0 active release');
|
|
46
|
+
insist(pointer.release_id===`v${pointer.version}-${pointer.manifest_sha256.slice(0,16)}`,'final closure release id/content address mismatch');
|
|
47
|
+
|
|
65
48
|
const parity=JSON.parse(await readFile(path.resolve(parityPath),'utf8'));
|
|
66
|
-
insist(parity.schema==='deadbyte.
|
|
67
|
-
|
|
49
|
+
insist(parity.schema==='deadbyte.r36-parity.v1','R36 parity schema mismatch at final closure');
|
|
50
|
+
validateParityEvidence(parity);
|
|
51
|
+
insist(parity.candidate?.release_id===pointer.release_id&&parity.candidate?.manifest_sha256===pointer.manifest_sha256,'parity candidate/current release mismatch');
|
|
52
|
+
insist(parity.successor?.release_id===pointer.release_id&&parity.successor?.manifest_sha256===pointer.manifest_sha256,'parity successor/current release mismatch');
|
|
53
|
+
insist(parity.predecessor?.release_id===R35_PREDECESSOR_RELEASE_ID&&parity.predecessor?.manifest_sha256===R35_PREDECESSOR_MANIFEST,'parity predecessor identity mismatch');
|
|
68
54
|
insist(parity.rollback?.receipt_path&&parity.restore?.receipt_path,'parity rollback/restore receipt paths missing');
|
|
55
|
+
insist(parity.immutable_slot?.protected===true,'parity immutable-slot proof missing');
|
|
56
|
+
insist(samePath(parity.immutable_slot.release_root,pointer.release_root),'parity immutable release root mismatch');
|
|
57
|
+
insist(!samePath(parity.immutable_slot.development_root,pointer.release_root),'development_root must differ from immutable release_root');
|
|
58
|
+
|
|
69
59
|
const windowsGate=JSON.parse(await readFile(path.resolve(windowsGatePath),'utf8'));
|
|
70
|
-
insist(windowsGate.schema==='deadbyte.
|
|
71
|
-
insist(windowsGate.manifest_sha256===pointer.manifest_sha256,'Windows gate/current release mismatch');
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
insist(windowsGate.schema==='deadbyte.r36-windows-gate.v1'&&windowsGate.status==='passed'&&windowsGate.exit_code===0,'R36 Windows gate evidence invalid');
|
|
61
|
+
insist(windowsGate.version==='0.10.0'&&windowsGate.manifest_sha256===pointer.manifest_sha256,'R36 Windows gate/current release mismatch');
|
|
62
|
+
insist(windowsGate.marker==='DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS','R36 Windows gate marker mismatch');
|
|
63
|
+
|
|
64
|
+
const localFull=await probeR36FullSurface({packageRoot:pointer.release_root,generationRoot:pointer.generation_root,stateRoot});
|
|
65
|
+
insist(localFull.status==='passed'&&localFull.tool_count===76&&localFull.strict_output_schemas===true,'final local full 76-tool proof failed');
|
|
66
|
+
const compact=await probeR36CompactDisarmed(await publicUrl());
|
|
67
|
+
insist(compact.status==='passed'&&compact.tool_count<=42&&compact.capability_closure===true,'final public compact capability closure failed');
|
|
68
|
+
insist(compact.coding_armed===false&&compact.autonomous_armed===false,'final public compact authority is not DISARMED');
|
|
69
|
+
insist(compact.release_id===pointer.release_id&&compact.release_manifest_sha256===pointer.manifest_sha256,'final public compact release binding mismatch');
|
|
70
|
+
insist(compact.machine_status?.release_id===pointer.release_id&&compact.machine_status?.release_manifest_sha256===pointer.manifest_sha256,'final machine status/current release mismatch');
|
|
71
|
+
insist(compact.machine_ping?.nonce===compact.nonce,'final machine ping nonce mismatch');
|
|
72
|
+
|
|
74
73
|
const evidence={
|
|
75
|
-
parity:await fileEvidence(parityPath),
|
|
76
|
-
rollback:await fileEvidence(parity.rollback.receipt_path),
|
|
74
|
+
parity:await fileEvidence(parityPath),activation:await fileEvidence(activationPath),
|
|
75
|
+
rollback:await fileEvidence(parity.rollback.receipt_path),restore:await fileEvidence(parity.restore.receipt_path),
|
|
77
76
|
windows_gate:await fileEvidence(windowsGatePath)
|
|
78
77
|
};
|
|
79
|
-
const body={
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
const body={
|
|
79
|
+
schema:'deadbyte.r36-final-closure.v1',version:'0.10.0',release_label:'R36 FINAL MACHINE BASELINE',
|
|
80
|
+
completed_at_utc:new Date().toISOString(),active_pointer:pointer,
|
|
81
|
+
predecessor:{release_id:R35_PREDECESSOR_RELEASE_ID,version:'0.9.0',manifest_sha256:R35_PREDECESSOR_MANIFEST},
|
|
82
|
+
immutable_slot:{protected:true,release_root:path.resolve(pointer.release_root),development_root:path.resolve(parity.immutable_slot.development_root)},
|
|
83
|
+
surfaces:{
|
|
84
|
+
local_full:{status:localFull.status,profile:localFull.profile,tool_count:localFull.tool_count,catalog_sha256:localFull.catalog_sha256,strict_output_schemas:localFull.strict_output_schemas,protocol_revision:localFull.protocol_revision},
|
|
85
|
+
public_compact:{status:compact.status,profile:compact.profile,tool_count:compact.tool_count,catalog_sha256:compact.catalog_sha256,strict_output_schemas:compact.strict_output_schemas,ledger_sha256:compact.ledger_sha256,capability_count:compact.capability_count,production_non_host_capabilities:compact.production_non_host_capabilities,capability_closure:compact.capability_closure,coding_armed:compact.coding_armed,autonomous_armed:compact.autonomous_armed,protocol_revision:compact.protocol_revision}
|
|
86
|
+
},
|
|
87
|
+
evidence,
|
|
88
|
+
machine:{status:compact.machine_status,ping:compact.machine_ping,nonce:compact.nonce},
|
|
89
|
+
authority:{all_disarmed:true,grants_absent:await grantAbsence()}
|
|
90
|
+
};
|
|
84
91
|
const privateKey=createPrivateKey(await readFile(path.join(stateRoot,'trust','ed25519-private.pem')));
|
|
85
92
|
const publicKey=createPublicKey(privateKey);
|
|
86
93
|
insist(privateKey.asymmetricKeyType==='ed25519'&&publicKey.asymmetricKeyType==='ed25519','closure signing key must be Ed25519');
|
|
87
94
|
const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
|
|
88
95
|
const bodyBytes=Buffer.from(canonicalJson(body),'utf8');
|
|
89
96
|
const signature=cryptoSign(null,Buffer.concat([DOMAIN,bodyBytes]),privateKey);
|
|
90
|
-
const envelope={body,body_sha256:sha256(bodyBytes),attestation:{algorithm:'ed25519',domain:'DEADBYTE-
|
|
97
|
+
const envelope={body,body_sha256:sha256(bodyBytes),attestation:{algorithm:'ed25519',domain:'DEADBYTE-R36-FINAL-CLOSURE-V1',key_id:keyId,signature_base64:signature.toString('base64')}};
|
|
91
98
|
envelope.closure_sha256=sha256(Buffer.from(canonicalJson(envelope),'utf8'));
|
|
92
99
|
const dir=path.join(stateRoot,'evidence','release');
|
|
93
|
-
const file=path.join(dir,`
|
|
100
|
+
const file=path.join(dir,`r36-final-${pointer.manifest_sha256.slice(0,16)}.json`);
|
|
94
101
|
const bytes=Buffer.from(`${JSON.stringify(envelope,null,2)}\n`,'utf8');
|
|
95
102
|
await atomicCreate(file,bytes);
|
|
96
103
|
return {status:'written',path:file,sha256:sha256(bytes),closure_sha256:envelope.closure_sha256,release_id:pointer.release_id};
|
|
@@ -102,5 +109,5 @@ if(invoked){
|
|
|
102
109
|
if(!parityPath||!activationPath||!windowsGatePath) throw new Error('usage: node scripts/final-closure.mjs <parity.json> <activation-receipt.json> <windows-gate.json>');
|
|
103
110
|
writeFinalClosure({parityPath,activationPath,windowsGatePath})
|
|
104
111
|
.then(result=>console.log(JSON.stringify(result,null,2)))
|
|
105
|
-
.catch(error=>{
|
|
112
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
106
113
|
}
|
package/scripts/gate-windows.ps1
CHANGED
|
@@ -9,7 +9,7 @@ function Invoke-GateStep {
|
|
|
9
9
|
Write-Host "==== $Name ===="
|
|
10
10
|
& npm.cmd @NpmArgs
|
|
11
11
|
if ($LASTEXITCODE -ne 0) {
|
|
12
|
-
throw "DEADBYTE V0.
|
|
12
|
+
throw "DEADBYTE V0.10.0 / R36 gate failed at $Name with exit code $LASTEXITCODE"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -36,7 +36,7 @@ Invoke-GateStep -Name 'release-manifest-final' -NpmArgs @('run', 'manifest:verif
|
|
|
36
36
|
Write-Host '==== windows-gate-evidence ===='
|
|
37
37
|
& node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence.mjs')
|
|
38
38
|
if ($LASTEXITCODE -ne 0) {
|
|
39
|
-
throw "DEADBYTE V0.
|
|
39
|
+
throw "DEADBYTE V0.10.0 / R36 gate failed at windows-gate-evidence with exit code $LASTEXITCODE"
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
Write-Host '==== DEADBYTE V0.
|
|
42
|
+
Write-Host '==== DEADBYTE V0.10.0 / R36 WINDOWS BATCH GATE: PASS ===='
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
$root = Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $root
|
|
5
|
+
try {
|
|
6
|
+
& node.exe --test test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "release parity tests failed with exit code $LASTEXITCODE" }
|
|
8
|
+
Write-Output 'R36 RELEASE PARITY TEST PASS'
|
|
9
|
+
} finally { Pop-Location }
|