deadbyte-mcp 0.15.0 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +109 -64
- package/PROVENANCE.json +11 -0
- package/R45-PARENT.json +18 -0
- package/README.txt +36 -20
- package/RELEASE.json +53 -0
- package/SBOM.cdx.json +46 -0
- package/TEST-MATRIX.json +31 -0
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +51 -3
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/native/deadbyte-hardware-observer.cpp +308 -0
- package/package.json +14 -7
- package/scripts/autonomous-tests.ps1 +2 -2
- package/scripts/build-hardware-r45.ps1 +71 -0
- package/scripts/deferred-slot-operation-r45.mjs +126 -0
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/derive-r45-parent.mjs +24 -0
- package/scripts/derive-r45-release.mjs +72 -0
- package/scripts/derive-r45-tests.mjs +20 -0
- package/scripts/final-closure-r45.mjs +121 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r45.mjs +220 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/finalize-r45.mjs +219 -0
- package/scripts/full-regression-r45.mjs +20 -0
- package/scripts/full-regression-r46.mjs +20 -0
- package/scripts/full-regression-summary-r45.mjs +14 -0
- package/scripts/gate-windows-r45.ps1 +44 -0
- package/scripts/gate-windows-r46.ps1 +56 -0
- package/scripts/init-host-policy.ps1 +39 -1
- package/scripts/inspect-active-release-r45.mjs +20 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-manifest.mjs +3 -1
- package/scripts/release-parity-r45.mjs +253 -0
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/release-parity-tests-r45.ps1 +8 -0
- package/scripts/verify-r45-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r45.mjs +60 -0
- package/scripts/windows-gate-evidence-r46.mjs +110 -0
- package/src/autonomous-runtime.mjs +12 -7
- package/src/capability-ledger.mjs +2 -0
- package/src/compact-gateway-mcp-tools.mjs +11 -7
- package/src/deadbyte-cli.mjs +24 -12
- package/src/doctor-r45.mjs +65 -0
- package/src/host-plane.mjs +133 -85
- package/src/machine-fs-mcp-tools.mjs +3 -3
- package/src/machine-fs-output-contracts.mjs +1 -1
- package/src/machine-fs-read-worker.mjs +46 -0
- package/src/machine-fs.mjs +63 -10
- package/src/mcp-server.mjs +68 -23
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/native-diff-v2.mjs +9 -4
- package/src/native-diff.mjs +29 -7
- package/src/observation-mcp-tools.mjs +5 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +57 -5
- package/src/observation-smoke-client.mjs +6 -2
- package/src/output-contracts.mjs +27 -11
- package/src/process-mcp-tools.mjs +18 -5
- package/src/process-output-contracts.mjs +20 -3
- package/src/process-policy.mjs +3 -1
- package/src/process-runtime.mjs +99 -19
- package/src/process-smoke-client.mjs +67 -22
- package/src/r45-compact-disarmed-probe.mjs +74 -0
- package/src/r45-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +23 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-r45-parity-client.mjs +206 -0
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/runtime-update.mjs +36 -8
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -0
- package/test/autonomous-loop.test.mjs +24 -0
- package/test/autonomous-runtime.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +4 -4
- package/test/coding-plane.test.mjs +1 -0
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/compact-max-catalog-r45.test.mjs +99 -0
- package/test/contained.test.mjs +23 -11
- package/test/controller.test.mjs +5 -1
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r45.test.mjs +91 -0
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/hardware-observation-r45.test.mjs +44 -0
- package/test/host-inline-r45.test.mjs +67 -0
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/machine-mcp-integration.test.mjs +1 -0
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/observation-runtime.test.mjs +29 -2
- package/test/output-schema.test.mjs +1 -0
- package/test/package-boundary.test.mjs +1 -1
- package/test/process-mcp-tools.test.mjs +23 -5
- package/test/process-policy-refresh.test.mjs +87 -10
- package/test/process-runtime.test.mjs +111 -1
- package/test/process-supervision.test.mjs +4 -2
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +20 -19
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-autonomous-quick-test.test.mjs +10 -0
- package/test/r45-finalization.test.mjs +84 -0
- package/test/release-generation.test.mjs +26 -11
- package/test/release-version.test.mjs +112 -46
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
- package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { verifyGenerationBundle } from '../src/release-generation.mjs';
|
|
7
|
+
import { loadProcessPolicy, processSessionStatus } from '../src/process-policy.mjs';
|
|
8
|
+
import { createProcessRuntime } from '../src/process-runtime.mjs';
|
|
9
|
+
|
|
10
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const packageRoot=path.resolve(here,'..');
|
|
12
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
13
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
15
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
16
|
+
function samePath(a,b){return path.resolve(String(a)).toLowerCase()===path.resolve(String(b)).toLowerCase();}
|
|
17
|
+
|
|
18
|
+
async function waitForExit(runtime,sessionId,timeoutMs=10000){
|
|
19
|
+
const deadline=Date.now()+timeoutMs;
|
|
20
|
+
let page=null,text='';
|
|
21
|
+
while(Date.now()<deadline){
|
|
22
|
+
page=await runtime.read({sessionId,offset:0,maxBytes:65536});
|
|
23
|
+
text=Buffer.from(page.data_base64,'base64').toString('utf8');
|
|
24
|
+
if(page.state==='exited'||page.state==='terminated') return {page,text};
|
|
25
|
+
await sleep(50);
|
|
26
|
+
}
|
|
27
|
+
throw new Error('R46 PowerShell parity timed out');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runReleaseParity({
|
|
31
|
+
root=packageRoot,
|
|
32
|
+
runtimeStateRoot=stateRoot,
|
|
33
|
+
clock=()=>new Date()
|
|
34
|
+
}={}){
|
|
35
|
+
const candidate=path.resolve(root);
|
|
36
|
+
const state=path.resolve(runtimeStateRoot);
|
|
37
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
38
|
+
const manifestSha256=sha256(manifestBytes);
|
|
39
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
40
|
+
insist(pkg.version==='0.15.2','R46 parity requires package version 0.15.2');
|
|
41
|
+
|
|
42
|
+
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
43
|
+
insist(pointer.version==='0.15.2'&&pointer.manifest_sha256===manifestSha256,'R46 active pointer candidate mismatch');
|
|
44
|
+
insist(pointer.release_id===`v0.15.2-${manifestSha256.slice(0,16)}`,'R46 active release id mismatch');
|
|
45
|
+
const releaseManifest=await readFile(path.join(pointer.release_root,'MANIFEST.SHA256'));
|
|
46
|
+
insist(sha256(releaseManifest)===manifestSha256,'R46 immutable release manifest mismatch');
|
|
47
|
+
|
|
48
|
+
const generation=await verifyGenerationBundle(pointer.generation_root,pointer.generation_manifest_sha256);
|
|
49
|
+
const processPolicyPath=path.join(pointer.generation_root,'process-policy.json');
|
|
50
|
+
const policy=await loadProcessPolicy(processPolicyPath);
|
|
51
|
+
const status=await processSessionStatus(policy);
|
|
52
|
+
insist(status.armed===true&&status.reason==='standing-grant','R46 parity requires restored Process standing grant');
|
|
53
|
+
insist(status.profile_ids.includes('powershell')&&status.root_ids.includes('core'),'R46 parity Process grant missing powershell/core');
|
|
54
|
+
|
|
55
|
+
const systemRoot=process.env.SystemRoot||process.env.WINDIR;
|
|
56
|
+
insist(typeof systemRoot==='string'&&systemRoot.length>0,'R46 parity requires SystemRoot/WINDIR');
|
|
57
|
+
const expectedPowerShell=path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe');
|
|
58
|
+
const expectedCmd=path.join(systemRoot,'System32','cmd.exe');
|
|
59
|
+
insist(samePath(policy.profiles?.powershell?.executable,expectedPowerShell),'R46 generated PowerShell path is not target-bound');
|
|
60
|
+
insist(samePath(policy.profiles?.ping_hold?.executable,expectedCmd),'R46 generated cmd path is not target-bound');
|
|
61
|
+
insist(policy.profiles.powershell.executable_sha256===sha256(await readFile(expectedPowerShell)),'R46 generated PowerShell digest mismatch');
|
|
62
|
+
insist(policy.profiles.ping_hold.executable_sha256===sha256(await readFile(expectedCmd)),'R46 generated cmd digest mismatch');
|
|
63
|
+
insist(JSON.stringify(policy.profiles.powershell.prefix_args)===JSON.stringify(['-NoLogo','-NoProfile','-NonInteractive','-Command']),'R46 PowerShell prefix contract mismatch');
|
|
64
|
+
insist(policy.profiles.powershell.max_args===1&&policy.profiles.powershell.max_arg_bytes===8192,'R46 PowerShell argument bounds mismatch');
|
|
65
|
+
|
|
66
|
+
const runtime=createProcessRuntime({
|
|
67
|
+
policy,
|
|
68
|
+
signingKeyPath:path.join(state,'trust','ed25519-private.pem'),
|
|
69
|
+
verifyKeyPath:path.join(state,'trust','ed25519-public.pem')
|
|
70
|
+
});
|
|
71
|
+
const source="$ErrorActionPreference='Stop';$before=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});[Console]::WriteLine('DB_R46_FILELESS_PARITY');[Console]::WriteLine('PSCommandPath=' + [string]$PSCommandPath);[Console]::WriteLine('InvocationPath=' + [string]$MyInvocation.MyCommand.Path);$after=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});$new=@($after|Where-Object{$_ -notin $before});[Console]::WriteLine('NEW_TEMP_PS1_COUNT=' + $new.Count)";
|
|
72
|
+
const started=await runtime.start({profileId:'powershell',rootId:'core',cwd:'.',args:[source]});
|
|
73
|
+
const observed=await waitForExit(runtime,started.session_id);
|
|
74
|
+
insist(observed.page.state==='exited'&&observed.page.exit_code===0,'R46 PowerShell parity process did not exit cleanly');
|
|
75
|
+
insist(/DB_R46_FILELESS_PARITY/.test(observed.text),'R46 PowerShell parity marker missing');
|
|
76
|
+
insist(/PSCommandPath=\r?\n/.test(observed.text),'R46 PowerShell unexpectedly materialized PSCommandPath');
|
|
77
|
+
insist(/InvocationPath=\r?\n/.test(observed.text),'R46 PowerShell invocation path is not fileless');
|
|
78
|
+
insist(/NEW_TEMP_PS1_COUNT=0/.test(observed.text),'R46 PowerShell parity created a temp ps1');
|
|
79
|
+
const journal=await runtime.verifyEventJournal({sessionId:started.session_id});
|
|
80
|
+
insist(journal.ok===true,'R46 PowerShell process journal verification failed');
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
insist(status.profile_ids.includes('powershell_repl'),'R46 parity Process grant missing powershell_repl');
|
|
84
|
+
insist(samePath(policy.profiles?.powershell_repl?.executable,expectedPowerShell),'R46 generated PowerShell REPL path is not target-bound');
|
|
85
|
+
insist(policy.profiles.powershell_repl.executable_sha256===sha256(await readFile(expectedPowerShell)),'R46 generated PowerShell REPL digest mismatch');
|
|
86
|
+
insist(policy.profiles.powershell_repl.allow_raw_input===true&&policy.profiles.powershell_repl.transport==='pipe','R46 PowerShell REPL raw-input contract mismatch');
|
|
87
|
+
|
|
88
|
+
const repl=await runtime.start({profileId:'powershell_repl',rootId:'core',cwd:'.',args:[]});
|
|
89
|
+
const firstWrite=await runtime.write({sessionId:repl.session_id,input:"$x=41;[Console]::WriteLine('DB_R46_REPL_SET')",appendNewline:true});
|
|
90
|
+
insist(firstWrite.session.state==='running','R46 PowerShell REPL did not remain running after first write');
|
|
91
|
+
const firstRead=await runtime.read({sessionId:repl.session_id,offset:0,maxBytes:65536,waitMs:5000,waitFor:'DB_R46_REPL_SET'});
|
|
92
|
+
insist(firstRead.wait_satisfied===true,'R46 PowerShell REPL first wait was not satisfied');
|
|
93
|
+
const beforeSecond=(await runtime.status({sessionId:repl.session_id})).stream_head;
|
|
94
|
+
await runtime.write({sessionId:repl.session_id,input:"[Console]::WriteLine('DB_R46_REPL_VALUE=' + ($x+1))",appendNewline:true});
|
|
95
|
+
const secondRead=await runtime.read({sessionId:repl.session_id,offset:beforeSecond,maxBytes:65536,waitMs:5000,waitFor:'DB_R46_REPL_VALUE=42'});
|
|
96
|
+
const secondText=Buffer.from(secondRead.data_base64,'base64').toString('utf8');
|
|
97
|
+
insist(secondRead.wait_satisfied===true&&/DB_R46_REPL_VALUE=42/.test(secondText),'R46 PowerShell REPL state did not persist across writes');
|
|
98
|
+
const tailRead=await runtime.read({sessionId:repl.session_id,offset:-4096,maxBytes:4096});
|
|
99
|
+
insist(/DB_R46_REPL_VALUE=42/.test(Buffer.from(tailRead.data_base64,'base64').toString('utf8')),'R46 PowerShell REPL tail read missing state proof');
|
|
100
|
+
await runtime.terminate({sessionId:repl.session_id});
|
|
101
|
+
const replJournal=await runtime.verifyEventJournal({sessionId:repl.session_id});
|
|
102
|
+
insist(replJournal.ok===true,'R46 PowerShell REPL journal verification failed');
|
|
103
|
+
const replJournalText=await readFile(path.join(policy.state_dir,`${repl.session_id}.jsonl`),'utf8');
|
|
104
|
+
insist(!replJournalText.includes('$x=41')&&!replJournalText.includes('DB_R46_REPL_VALUE'),'R46 raw process input leaked into signed journal');
|
|
105
|
+
|
|
106
|
+
const ringSource="[Console]::Write(('A'*2097152));[Console]::WriteLine('DB_R46_RING_TAIL')";
|
|
107
|
+
const ring=await runtime.start({profileId:'powershell',rootId:'core',cwd:'.',args:[ringSource]});
|
|
108
|
+
const ringObserved=await waitForExit(runtime,ring.session_id,15000);
|
|
109
|
+
const ringStatus=await runtime.status({sessionId:ring.session_id});
|
|
110
|
+
insist(ringObserved.page.exit_code===0&&ringStatus.earliest_offset>0,'R46 retained-output ring did not evict under heavy output');
|
|
111
|
+
const ringTail=await runtime.read({sessionId:ring.session_id,offset:-4096,maxBytes:4096});
|
|
112
|
+
insist(/DB_R46_RING_TAIL/.test(Buffer.from(ringTail.data_base64,'base64').toString('utf8')),'R46 retained-output ring lost tail marker');
|
|
113
|
+
|
|
114
|
+
const completed=clock();
|
|
115
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'R46 parity clock invalid');
|
|
116
|
+
const evidence={
|
|
117
|
+
schema:'deadbyte.r46-parity.v1',
|
|
118
|
+
status:'passed',
|
|
119
|
+
version:'0.15.2',
|
|
120
|
+
completed_at_utc:completed.toISOString(),
|
|
121
|
+
candidate:{
|
|
122
|
+
release_id:pointer.release_id,
|
|
123
|
+
manifest_sha256:pointer.manifest_sha256,
|
|
124
|
+
generation_manifest_sha256:pointer.generation_manifest_sha256
|
|
125
|
+
},
|
|
126
|
+
generation:{files:generation.files,generation_manifest_sha256:generation.generation_manifest_sha256},
|
|
127
|
+
process_policy:{
|
|
128
|
+
sha256:policy.policy_sha256,
|
|
129
|
+
profile_ids:Object.keys(policy.profiles).sort(),
|
|
130
|
+
powershell_executable:policy.profiles.powershell.executable,
|
|
131
|
+
powershell_executable_sha256:policy.profiles.powershell.executable_sha256,
|
|
132
|
+
cmd_executable:policy.profiles.ping_hold.executable,
|
|
133
|
+
cmd_executable_sha256:policy.profiles.ping_hold.executable_sha256,
|
|
134
|
+
target_repin_verified:true
|
|
135
|
+
},
|
|
136
|
+
fileless_powershell:{
|
|
137
|
+
session_id:started.session_id,
|
|
138
|
+
child_pid:started.child_pid,
|
|
139
|
+
exit_code:observed.page.exit_code,
|
|
140
|
+
stream_sha256:observed.page.stream_sha256,
|
|
141
|
+
pscommandpath_empty:true,
|
|
142
|
+
invocation_path_empty:true,
|
|
143
|
+
new_temp_ps1_count:0,
|
|
144
|
+
journal_head_sha256:journal.head_sha256,
|
|
145
|
+
journal_event_count:journal.event_count
|
|
146
|
+
},
|
|
147
|
+
persistent_powershell_repl:{
|
|
148
|
+
session_id:repl.session_id,
|
|
149
|
+
child_pid:repl.child_pid,
|
|
150
|
+
state_persisted:true,
|
|
151
|
+
observed_value:42,
|
|
152
|
+
wait_for_verified:true,
|
|
153
|
+
tail_read_verified:true,
|
|
154
|
+
raw_input_not_journaled:true,
|
|
155
|
+
journal_head_sha256:replJournal.head_sha256,
|
|
156
|
+
journal_event_count:replJournal.event_count
|
|
157
|
+
},
|
|
158
|
+
retained_output_ring:{
|
|
159
|
+
session_id:ring.session_id,
|
|
160
|
+
stream_head:ringStatus.stream_head,
|
|
161
|
+
earliest_offset:ringStatus.earliest_offset,
|
|
162
|
+
eviction_verified:true,
|
|
163
|
+
tail_marker_verified:true
|
|
164
|
+
},
|
|
165
|
+
execution_provider:'deadbyte_mcp',
|
|
166
|
+
rdc_operation_count:0
|
|
167
|
+
};
|
|
168
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
169
|
+
const dir=path.join(state,'evidence','release');
|
|
170
|
+
await mkdir(dir,{recursive:true});
|
|
171
|
+
const file=path.join(dir,`r46-parity-${manifestSha256.slice(0,16)}.json`);
|
|
172
|
+
try{
|
|
173
|
+
const handle=await open(file,'wx',0o600);
|
|
174
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
175
|
+
return {status:'written',evidence_path:file,evidence_sha256:sha256(bytes),evidence};
|
|
176
|
+
}catch(error){
|
|
177
|
+
if(error?.code!=='EEXIST')throw error;
|
|
178
|
+
const existingBytes=await readFile(file);
|
|
179
|
+
const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
180
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed','existing R46 parity evidence invalid');
|
|
181
|
+
insist(existing.candidate?.manifest_sha256===manifestSha256,'existing R46 parity candidate mismatch');
|
|
182
|
+
return {status:'existing',evidence_path:file,evidence_sha256:sha256(existingBytes),evidence:existing};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
187
|
+
if(invoked)runReleaseParity().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
& node.exe --test --test-concurrency=1 test\host-inline-r45.test.mjs test\hardware-observation-r45.test.mjs test\runtime-host-bootstrap-r45.test.mjs test\full-max-catalog-r45.test.mjs test\compact-max-catalog-r45.test.mjs test\r45-finalization.test.mjs test\skill-plane-r44.test.mjs test\skill-mcp-r44.test.mjs test\agent-backend-r44.test.mjs test\codex-app-server-r44.test.mjs test\codex-autonomous-r44.test.mjs test\remote-device-r44.test.mjs test\durable-job-r44.test.mjs test\external-job-provider-r44.test.mjs test\coding-runtime.test.mjs test\host-plane.test.mjs test\process-policy.test.mjs test\process-runtime.test.mjs test\native-diff.test.mjs test\native-diff-topology-r36.test.mjs test\autonomous-loop.test.mjs test\autonomous-adversarial.test.mjs test\standing-grants.test.mjs test\runtime-self-update.test.mjs
|
|
4
|
+
if($LASTEXITCODE -ne 0){exit $LASTEXITCODE}
|
|
5
|
+
& node.exe --test --test-concurrency=1 test\r44-finalization.test.mjs test\r43-finalization.test.mjs test\provider-responses-r42.test.mjs test\task-contract-r42.test.mjs test\human-clarification-r42.test.mjs test\agent-governance-r42.test.mjs test\evidence-publisher-r42.test.mjs test\agent-eval-r42.test.mjs test\agent-eval-runner-r42.test.mjs test\r42-finalization.test.mjs
|
|
6
|
+
if($LASTEXITCODE -ne 0){exit $LASTEXITCODE}
|
|
7
|
+
& node.exe --test --test-concurrency=1 test\memory-snapshot-r41.test.mjs test\memory-projections-r41.test.mjs test\memory-retrieval-r41.test.mjs test\memory-history-r41.test.mjs test\memory-autonomous-integration-r41.test.mjs
|
|
8
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash, createPublicKey, verify as cryptoVerify } from 'node:crypto';
|
|
2
|
+
import { lstat, readFile, readdir } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const CLOSURE_DOMAIN=Buffer.from('DEADBYTE-R44-FINAL-CLOSURE-V1\0','utf8');
|
|
8
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
11
|
+
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
12
|
+
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
13
|
+
const same=(a,b)=>canonicalJson(a)===canonicalJson(b);
|
|
14
|
+
const samePath=(a,b)=>path.resolve(String(a)).toLowerCase()===path.resolve(String(b)).toLowerCase();
|
|
15
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
16
|
+
|
|
17
|
+
function parseManifest(text){
|
|
18
|
+
const out=new Map();
|
|
19
|
+
for(const [i,line] of text.split(/\r?\n/).entries()){
|
|
20
|
+
if(!line) continue;
|
|
21
|
+
const m=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
22
|
+
insist(m,`manifest line ${i+1} malformed`);
|
|
23
|
+
const rel=m[2];
|
|
24
|
+
insist(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!out.has(rel),`unsafe/duplicate manifest path ${rel}`);
|
|
25
|
+
out.set(rel,m[1]);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
async function walk(root,rel=''){
|
|
30
|
+
const dir=path.join(root,...(rel?rel.split('/'):[]));
|
|
31
|
+
const names=(await readdir(dir)).sort((a,b)=>a.localeCompare(b,'en'));
|
|
32
|
+
const files=[];
|
|
33
|
+
for(const name of names){
|
|
34
|
+
const childRel=rel?`${rel}/${name}`:name;
|
|
35
|
+
const full=path.join(root,...childRel.split('/'));
|
|
36
|
+
const st=await lstat(full);
|
|
37
|
+
insist(!st.isSymbolicLink(),`symlink forbidden: ${childRel}`);
|
|
38
|
+
if(st.isDirectory()) files.push(...await walk(root,childRel));
|
|
39
|
+
else { insist(st.isFile(),`non-file forbidden: ${childRel}`); files.push(childRel); }
|
|
40
|
+
}
|
|
41
|
+
return files;
|
|
42
|
+
}
|
|
43
|
+
async function verifyTree(root,manifestName,expectedDigest,expectedCount){
|
|
44
|
+
const manifestBytes=await readFile(path.join(root,manifestName));
|
|
45
|
+
insist(sha256(manifestBytes)===expectedDigest,`${manifestName} digest mismatch`);
|
|
46
|
+
const entries=parseManifest(manifestBytes.toString('utf8'));
|
|
47
|
+
insist(entries.size===expectedCount,`${manifestName} file count mismatch`);
|
|
48
|
+
const actual=(await walk(root)).filter(rel=>rel!==manifestName).sort((a,b)=>a.localeCompare(b,'en'));
|
|
49
|
+
const expected=[...entries.keys()].sort((a,b)=>a.localeCompare(b,'en'));
|
|
50
|
+
insist(same(actual,expected),`${manifestName} exact tree mismatch`);
|
|
51
|
+
for(const [rel,digest] of entries){
|
|
52
|
+
insist(sha256(await readFile(path.join(root,...rel.split('/'))))===digest,`${manifestName} file hash mismatch: ${rel}`);
|
|
53
|
+
}
|
|
54
|
+
return entries.size;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function verifyR45Parent(parentFile,{stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
|
|
58
|
+
const parentPath=path.resolve(parentFile);
|
|
59
|
+
const parent=JSON.parse(await readFile(parentPath,'utf8'));
|
|
60
|
+
insist(parent?.schema==='deadbyte.r45-parent.v1','R45 parent schema mismatch');
|
|
61
|
+
insist(parent.parent_release_id==='v0.15.0-c15fa41719be61b3','R45 parent release id mismatch');
|
|
62
|
+
insist(parent.parent_version==='0.15.0','R45 parent version mismatch');
|
|
63
|
+
insist(parent.parent_manifest_sha256==='c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483','R45 parent manifest mismatch');
|
|
64
|
+
insist(parent.parent_manifest_file_count===1871,'R45 parent file count mismatch');
|
|
65
|
+
insist(parent.parent_generation_manifest_sha256==='6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30','R45 parent generation mismatch');
|
|
66
|
+
insist(parent.parent_generation_file_count===7,'R45 parent generation file count mismatch');
|
|
67
|
+
insist(parent.parent_final_closure_file_sha256==='8aa9c31ab18dfde67275d7cd59295ba08099839d35d6c97ee277fb184c05cae0','R45 parent closure file hash mismatch');
|
|
68
|
+
insist(parent.parent_final_closure_sha256==='533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667','R45 parent closure hash mismatch');
|
|
69
|
+
insist(parent.parent_final_closure_signer_key_id==='c0727a7911e29a0f1edd3cd910e47f89f43edb484743988c89cd82bfd5e37936','R45 parent closure signer mismatch');
|
|
70
|
+
insist(parent.parent_proof==='immutable-slot+generation+signed-final-closure','R45 parent proof class mismatch');
|
|
71
|
+
|
|
72
|
+
const closureBytes=await readFile(path.resolve(parent.parent_final_closure_path));
|
|
73
|
+
insist(sha256(closureBytes)===parent.parent_final_closure_file_sha256,'R45 parent closure file bytes mismatch');
|
|
74
|
+
const envelope=JSON.parse(closureBytes.toString('utf8'));
|
|
75
|
+
insist(envelope?.body?.schema==='deadbyte.r44-final-closure.v1','R45 parent closure schema mismatch');
|
|
76
|
+
insist(envelope.body.version===parent.parent_version,'R45 parent closure version mismatch');
|
|
77
|
+
insist(envelope.body.release_label==='R44 REMOTE AGENT SKILL BASELINE','R45 parent closure label mismatch');
|
|
78
|
+
|
|
79
|
+
const publicKey=createPublicKey(await readFile(path.join(path.resolve(stateRoot),'trust','ed25519-public.pem')));
|
|
80
|
+
insist(publicKey.asymmetricKeyType==='ed25519','R45 parent trust key must be Ed25519');
|
|
81
|
+
const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
|
|
82
|
+
insist(keyId===parent.parent_final_closure_signer_key_id,'R45 parent trust key id mismatch');
|
|
83
|
+
const bodyBytes=Buffer.from(canonicalJson(envelope.body),'utf8');
|
|
84
|
+
insist(sha256(bodyBytes)===envelope.body_sha256,'R45 parent closure body hash mismatch');
|
|
85
|
+
const {closure_sha256,...unsigned}=envelope;
|
|
86
|
+
insist(sha256(Buffer.from(canonicalJson(unsigned),'utf8'))===closure_sha256,'R45 parent closure envelope hash mismatch');
|
|
87
|
+
insist(closure_sha256===parent.parent_final_closure_sha256,'R45 parent closure content address mismatch');
|
|
88
|
+
insist(envelope.attestation?.algorithm==='ed25519'&&envelope.attestation?.domain==='DEADBYTE-R44-FINAL-CLOSURE-V1','R45 parent closure attestation contract mismatch');
|
|
89
|
+
insist(envelope.attestation.key_id===keyId,'R45 parent closure attestation signer mismatch');
|
|
90
|
+
insist(cryptoVerify(null,Buffer.concat([CLOSURE_DOMAIN,bodyBytes]),publicKey,Buffer.from(envelope.attestation.signature_base64,'base64')),'R45 parent closure signature invalid');
|
|
91
|
+
|
|
92
|
+
const pointer=envelope.body.active_pointer;
|
|
93
|
+
insist(pointer?.release_id===parent.parent_release_id&&pointer?.version===parent.parent_version&&pointer?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent closure release binding mismatch');
|
|
94
|
+
insist(pointer.generation_manifest_sha256===parent.parent_generation_manifest_sha256,'R45 parent closure generation binding mismatch');
|
|
95
|
+
insist(pointer.launcher_sha256===parent.parent_launcher_sha256,'R45 parent launcher binding mismatch');
|
|
96
|
+
insist(samePath(pointer.release_root,parent.parent_release_root),'R45 parent release root mismatch');
|
|
97
|
+
insist(samePath(pointer.generation_root,parent.parent_generation_root),'R45 parent generation root mismatch');
|
|
98
|
+
insist(envelope.body.immutable_slot?.protected===true,'R45 parent immutable slot proof missing');
|
|
99
|
+
insist(samePath(envelope.body.immutable_slot.release_root,parent.parent_release_root),'R45 parent immutable release root mismatch');
|
|
100
|
+
insist(!samePath(envelope.body.immutable_slot.development_root,parent.parent_release_root),'R45 parent development root aliases release slot');
|
|
101
|
+
insist(samePath(envelope.body.immutable_slot.development_root,parent.development_root),'R45 parent development root mismatch');
|
|
102
|
+
insist(envelope.body.authority?.all_disarmed===true,'R45 parent closure did not record all-disarmed state');
|
|
103
|
+
for(const name of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
|
|
104
|
+
insist(envelope.body.authority.grants_absent?.[name]===true,`R45 parent closure grant absence missing: ${name}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const evidence=envelope.body.evidence;
|
|
108
|
+
insist(evidence&&evidence.parity&&evidence.activation&&evidence.rollback&&evidence.restore&&evidence.windows_gate,'R45 parent closure evidence set incomplete');
|
|
109
|
+
for(const [name,item] of Object.entries(evidence)){
|
|
110
|
+
insist(typeof item?.path==='string'&&HEX64.test(item?.sha256??''),`R45 parent ${name} evidence identity malformed`);
|
|
111
|
+
insist(sha256(await readFile(item.path))===item.sha256,`R45 parent ${name} evidence hash mismatch`);
|
|
112
|
+
}
|
|
113
|
+
const parity=JSON.parse(await readFile(evidence.parity.path,'utf8'));
|
|
114
|
+
insist(parity?.schema==='deadbyte.r44-parity.v1'&&parity.status==='passed','R45 parent parity status mismatch');
|
|
115
|
+
insist(parity.candidate?.release_id===parent.parent_release_id&&parity.candidate?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent parity candidate mismatch');
|
|
116
|
+
insist(parity.successor?.release_id===parent.parent_release_id&&parity.successor?.manifest_sha256===parent.parent_manifest_sha256,'R45 parent parity successor mismatch');
|
|
117
|
+
insist(parity.successor_disarmed?.status==='passed','R45 parent successor-disarmed proof missing');
|
|
118
|
+
|
|
119
|
+
const releaseFiles=await verifyTree(parent.parent_release_root,'MANIFEST.SHA256',parent.parent_manifest_sha256,parent.parent_manifest_file_count);
|
|
120
|
+
const generationFiles=await verifyTree(parent.parent_generation_root,'GENERATION.SHA256',parent.parent_generation_manifest_sha256,parent.parent_generation_file_count);
|
|
121
|
+
return {
|
|
122
|
+
ok:true,
|
|
123
|
+
status:'passed',
|
|
124
|
+
proof_class:parent.parent_proof,
|
|
125
|
+
release_id:parent.parent_release_id,
|
|
126
|
+
manifest_sha256:parent.parent_manifest_sha256,
|
|
127
|
+
generation_manifest_sha256:parent.parent_generation_manifest_sha256,
|
|
128
|
+
release_files:releaseFiles,
|
|
129
|
+
generation_files:generationFiles,
|
|
130
|
+
closure_sha256:parent.parent_final_closure_sha256,
|
|
131
|
+
closure_file_sha256:parent.parent_final_closure_file_sha256,
|
|
132
|
+
signer_key_id:keyId,
|
|
133
|
+
recorded_all_disarmed:true
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
138
|
+
if(invoked){
|
|
139
|
+
const file=process.argv[2]??path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..','R45-PARENT.json');
|
|
140
|
+
verifyR45Parent(file).then(result=>console.log(JSON.stringify(result,null,2)))
|
|
141
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
142
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { verifyR45Parent } from './verify-r45-parent.mjs';
|
|
7
|
+
import { doctor } from '../src/doctor-r45.mjs';
|
|
8
|
+
|
|
9
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const root=path.resolve(here,'..');
|
|
11
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const marker='DEADBYTE V0.15.1 / R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS';
|
|
13
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
14
|
+
|
|
15
|
+
export async function writeWindowsGateEvidence({
|
|
16
|
+
candidateRoot=root,
|
|
17
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
18
|
+
clock=()=>new Date()
|
|
19
|
+
}={}){
|
|
20
|
+
const candidate=path.resolve(candidateRoot);
|
|
21
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
22
|
+
const manifestSha256=sha256(manifestBytes);
|
|
23
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
24
|
+
insist(pkg.version==='0.15.1',`Windows gate evidence requires V0.15.1, found ${pkg.version}`);
|
|
25
|
+
const parent=await verifyR45Parent(path.join(candidate,'R45-PARENT.json'));
|
|
26
|
+
const doctorResult=await doctor();
|
|
27
|
+
insist(doctorResult?.status==='ok'&&doctorResult?.package_version==='0.15.1'&&doctorResult?.manifest?.verified===true,'R45 doctor evidence invalid');
|
|
28
|
+
insist(doctorResult?.policy?.host_v2_ready===true,'R45 doctor did not verify Host policy v2');
|
|
29
|
+
const completed=clock();
|
|
30
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
31
|
+
const evidence={
|
|
32
|
+
schema:'deadbyte.r45-windows-gate.v1',status:'passed',exit_code:0,version:'0.15.1',
|
|
33
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256,
|
|
34
|
+
doctor:{status:doctorResult.status,manifest_sha256:doctorResult.manifest.sha256,verified_entries:doctorResult.manifest.verified_entries,host_v2_ready:doctorResult.policy.host_v2_ready},
|
|
35
|
+
parent:{release_id:parent.release_id,manifest_sha256:parent.manifest_sha256,
|
|
36
|
+
generation_manifest_sha256:parent.generation_manifest_sha256,
|
|
37
|
+
final_closure_sha256:parent.closure_sha256,final_closure_file_sha256:parent.closure_file_sha256,
|
|
38
|
+
signer_key_id:parent.signer_key_id},
|
|
39
|
+
features:{skill_integrity:true,agent_backend_persistence:true,codex_protocol_contract:true,
|
|
40
|
+
remote_device_identity:true,durable_job_supervisor:true,real_external_supervisor_smoke:true,
|
|
41
|
+
full_regression_required:true,codex_live_binary_required:false,host_inline_native:true,native_hardware_observation:true,doctor_release_truth:true}
|
|
42
|
+
};
|
|
43
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
44
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');await mkdir(dir,{recursive:true});
|
|
45
|
+
const file=path.join(dir,`r45-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
46
|
+
try{
|
|
47
|
+
const handle=await open(file,'wx',0o600);try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
48
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256,parent:evidence.parent};
|
|
49
|
+
}catch(error){
|
|
50
|
+
if(error?.code!=='EEXIST')throw error;
|
|
51
|
+
const existingBytes=await readFile(file);
|
|
52
|
+
const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
53
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing R45 Windows gate evidence invalid');
|
|
54
|
+
insist(existing.version==='0.15.1'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing R45 Windows gate identity mismatch');
|
|
55
|
+
insist(existing.parent?.release_id===parent.release_id&&existing.parent?.final_closure_sha256===parent.closure_sha256,'existing R45 parent evidence mismatch');
|
|
56
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256,parent:existing.parent};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
60
|
+
if(invoked)writeWindowsGateEvidence().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { doctor } from '../src/doctor-r45.mjs';
|
|
7
|
+
|
|
8
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const root=path.resolve(here,'..');
|
|
10
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
const marker='DEADBYTE V0.15.2 / R46 FILELESS POWERSHELL WINDOWS BATCH GATE: PASS';
|
|
12
|
+
const PARENT_RELEASE_ID='v0.15.1-d55f00b5144dc31a';
|
|
13
|
+
const PARENT_MANIFEST='d55f00b5144dc31a7fe39e4dc60d640a94780da6aaeca0918e87636b816d7854';
|
|
14
|
+
const PARENT_GENERATION='00cde7bf540cc2804d679a39040d5b07193c5f8a0d49cac3b882402f8f4e6ad8';
|
|
15
|
+
const REOPENED_RUNTIME_RELEASE_ID='v0.15.2-0fb46542f6a4345f';
|
|
16
|
+
const REOPENED_RUNTIME_MANIFEST='0fb46542f6a4345f1c527565d06bf6e9b8e283260e47bf684fb70a60308fcf34';
|
|
17
|
+
const REOPENED_RUNTIME_GENERATION='4717e0eb9c515ce4b1e90b8496c49c68abde765a6de7490176b803912385afe4';
|
|
18
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
19
|
+
|
|
20
|
+
export async function writeWindowsGateEvidence({
|
|
21
|
+
candidateRoot=root,
|
|
22
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
23
|
+
clock=()=>new Date()
|
|
24
|
+
}={}){
|
|
25
|
+
const candidate=path.resolve(candidateRoot);
|
|
26
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
27
|
+
const manifestSha256=sha256(manifestBytes);
|
|
28
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
29
|
+
insist(pkg.version==='0.15.2',`R46 Windows gate evidence requires V0.15.2, found ${pkg.version}`);
|
|
30
|
+
const release=JSON.parse(await readFile(path.join(candidate,'RELEASE.json'),'utf8'));
|
|
31
|
+
insist(release.version==='0.15.2'&&release.train==='R46','R46 release metadata mismatch');
|
|
32
|
+
const active=JSON.parse(await readFile(path.join(path.resolve(stateRoot),'active-release.json'),'utf8'));
|
|
33
|
+
const originalParent=active.release_id===PARENT_RELEASE_ID&&active.manifest_sha256===PARENT_MANIFEST&&active.generation_manifest_sha256===PARENT_GENERATION;
|
|
34
|
+
const reopenedPredecessor=active.release_id===REOPENED_RUNTIME_RELEASE_ID&&active.manifest_sha256===REOPENED_RUNTIME_MANIFEST&&active.generation_manifest_sha256===REOPENED_RUNTIME_GENERATION;
|
|
35
|
+
insist(originalParent||reopenedPredecessor,'R46 runtime predecessor identity mismatch');
|
|
36
|
+
const parent={release_id:PARENT_RELEASE_ID,version:'0.15.1',manifest_sha256:PARENT_MANIFEST,generation_manifest_sha256:PARENT_GENERATION};
|
|
37
|
+
|
|
38
|
+
const doctorResult=await doctor();
|
|
39
|
+
insist(doctorResult?.status==='ok'&&doctorResult?.package_version==='0.15.2'&&doctorResult?.manifest?.verified===true,'R46 doctor evidence invalid');
|
|
40
|
+
insist(doctorResult?.policy?.host_v2_ready===true,'R46 doctor did not verify Host policy v2');
|
|
41
|
+
|
|
42
|
+
const completed=clock();
|
|
43
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'R46 Windows gate evidence clock invalid');
|
|
44
|
+
const evidence={
|
|
45
|
+
schema:'deadbyte.r46-windows-gate.v1',
|
|
46
|
+
status:'passed',
|
|
47
|
+
exit_code:0,
|
|
48
|
+
version:'0.15.2',
|
|
49
|
+
train:'R46',
|
|
50
|
+
marker,
|
|
51
|
+
completed_at_utc:completed.toISOString(),
|
|
52
|
+
manifest_sha256:manifestSha256,
|
|
53
|
+
doctor:{
|
|
54
|
+
status:doctorResult.status,
|
|
55
|
+
manifest_sha256:doctorResult.manifest.sha256,
|
|
56
|
+
verified_entries:doctorResult.manifest.verified_entries,
|
|
57
|
+
host_v2_ready:doctorResult.policy.host_v2_ready
|
|
58
|
+
},
|
|
59
|
+
parent,
|
|
60
|
+
runtime_predecessor:{
|
|
61
|
+
release_id:active.release_id,
|
|
62
|
+
version:active.version,
|
|
63
|
+
manifest_sha256:active.manifest_sha256,
|
|
64
|
+
generation_manifest_sha256:active.generation_manifest_sha256,
|
|
65
|
+
mode:reopenedPredecessor?'reopened-unpublished-0.15.2':'sealed-0.15.1-parent'
|
|
66
|
+
},
|
|
67
|
+
features:{
|
|
68
|
+
fileless_powershell:true,
|
|
69
|
+
powershell_source_via_process_argv:true,
|
|
70
|
+
no_ps1_materialization_regression:true,
|
|
71
|
+
target_machine_process_profile_repin:true,
|
|
72
|
+
target_machine_cmd_repin:true,
|
|
73
|
+
target_machine_powershell_repin:true,
|
|
74
|
+
powershell_persistent_repl:true,
|
|
75
|
+
guarded_process_raw_input:true,
|
|
76
|
+
process_tail_reads:true,
|
|
77
|
+
process_wait_reads:true,
|
|
78
|
+
process_chunked_output_ring:true,
|
|
79
|
+
remote_reachability_ttl:true,
|
|
80
|
+
remote_probe_timeout_retry:true,
|
|
81
|
+
remote_dispatch_timeout_no_retry:true,
|
|
82
|
+
observation_search_terminal_ttl:true,
|
|
83
|
+
observation_search_hard_cap:128,
|
|
84
|
+
full_regression_required:true,
|
|
85
|
+
compact_tool_count:43,
|
|
86
|
+
full_tool_count:83
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
90
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
91
|
+
await mkdir(dir,{recursive:true});
|
|
92
|
+
const file=path.join(dir,`r46-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
93
|
+
try{
|
|
94
|
+
const handle=await open(file,'wx',0o600);
|
|
95
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
96
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256,parent:evidence.parent};
|
|
97
|
+
}catch(error){
|
|
98
|
+
if(error?.code!=='EEXIST')throw error;
|
|
99
|
+
const existingBytes=await readFile(file);
|
|
100
|
+
const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
101
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing R46 Windows gate evidence invalid');
|
|
102
|
+
insist(existing.version==='0.15.2'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing R46 Windows gate identity mismatch');
|
|
103
|
+
insist(existing.parent?.release_id===parent.release_id&&existing.parent?.manifest_sha256===parent.manifest_sha256,'existing R46 parent evidence mismatch');
|
|
104
|
+
insist(existing.runtime_predecessor?.release_id===active.release_id&&existing.runtime_predecessor?.manifest_sha256===active.manifest_sha256,'existing R46 runtime predecessor evidence mismatch');
|
|
105
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256,parent:existing.parent};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
110
|
+
if(invoked)writeWindowsGateEvidence().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
@@ -1230,10 +1230,13 @@ async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, co
|
|
|
1230
1230
|
return results;
|
|
1231
1231
|
}
|
|
1232
1232
|
|
|
1233
|
-
async function collectNativeHead(state, nativeDiffRuntime, codingRuntime) {
|
|
1233
|
+
async function collectNativeHead(state, nativeDiffRuntime, codingRuntime, { requireCurrentState = true } = {}) {
|
|
1234
1234
|
const diffBindings = []; const head = new Map();
|
|
1235
1235
|
for (const item of state.diffs) {
|
|
1236
|
-
const
|
|
1236
|
+
const verifier = requireCurrentState
|
|
1237
|
+
? nativeDiffRuntime.verify.bind(nativeDiffRuntime)
|
|
1238
|
+
: (nativeDiffRuntime.verifyEvidence ?? nativeDiffRuntime.verify).bind(nativeDiffRuntime);
|
|
1239
|
+
const checked = await verifier({ diffId:item.diff_id });
|
|
1237
1240
|
if (!checked.ok || !Array.isArray(checked.final_states)) throw new Error(`release diff '${item.diff_id}' verification failed`);
|
|
1238
1241
|
diffBindings.push({ diff_id:item.diff_id, receipt_sha256:checked.receipt_sha256,
|
|
1239
1242
|
coding_receipts_verified:checked.coding_receipts_verified });
|
|
@@ -1243,10 +1246,12 @@ async function collectNativeHead(state, nativeDiffRuntime, codingRuntime) {
|
|
|
1243
1246
|
}
|
|
1244
1247
|
}
|
|
1245
1248
|
const finalSourceStates = [...head.values()].sort((a,b) => `${a.root_id}/${a.path}`.localeCompare(`${b.root_id}/${b.path}`));
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1249
|
+
if (requireCurrentState) {
|
|
1250
|
+
for (const expected of finalSourceStates) {
|
|
1251
|
+
const current = await codingRuntime.fileState({ rootId:expected.root_id, relativePath:expected.path });
|
|
1252
|
+
if (current.exists !== expected.exists || current.sha256 !== expected.sha256) {
|
|
1253
|
+
throw new Error(`release native HEAD mismatch '${expected.path}'`);
|
|
1254
|
+
}
|
|
1250
1255
|
}
|
|
1251
1256
|
}
|
|
1252
1257
|
return { diffBindings, finalSourceStates };
|
|
@@ -1309,7 +1314,7 @@ async function verifyReleaseReceipt({ ctx, goal, events, policy, verifyKeyPath,
|
|
|
1309
1314
|
return { ok:false, reason:'release profile evidence mismatch' };
|
|
1310
1315
|
}
|
|
1311
1316
|
let nativeHead;
|
|
1312
|
-
try { nativeHead = await collectNativeHead(preCompletionState,nativeDiffRuntime,codingRuntime); }
|
|
1317
|
+
try { nativeHead = await collectNativeHead(preCompletionState,nativeDiffRuntime,codingRuntime,{requireCurrentState:false}); }
|
|
1313
1318
|
catch (error) { return { ok:false, reason:truncated(error) }; }
|
|
1314
1319
|
if (canonicalJson(nativeHead.diffBindings) !== canonicalJson(body.native_diffs ?? [])) {
|
|
1315
1320
|
return { ok:false, reason:'release native diff binding mismatch' };
|
|
@@ -89,6 +89,7 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
|
|
|
89
89
|
|
|
90
90
|
gateway('process.start','process_session_start','process_session','start','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
91
91
|
gateway('process.read','process_session_read','process_session','read','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
92
|
+
gateway('process.write','process_session_write','process_session','write','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
92
93
|
gateway('process.status','process_session_status','process_session','status','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
93
94
|
gateway('process.list','process_session_list','process_session','list','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
94
95
|
gateway('process.action','process_session_action','process_session','action','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
@@ -135,6 +136,7 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
|
|
|
135
136
|
gateway('observation.search_cancel','machine_search_cancel','machine_observe','search_cancel','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
136
137
|
gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
137
138
|
gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
139
|
+
gateway('observation.hardware_query','machine_hardware_query','machine_observe','hardware_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
138
140
|
|
|
139
141
|
gateway('desktop.status','desktop_status','desktop_control','status','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
|
|
140
142
|
gateway('desktop.observe','desktop_observe','desktop_control','observe','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
|