deadbyte-mcp 0.15.1 → 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 +54 -44
- package/PROVENANCE.json +6 -5
- package/README.txt +36 -20
- package/RELEASE.json +24 -8
- package/SBOM.cdx.json +3 -3
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +50 -2
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/package.json +9 -7
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/full-regression-r46.mjs +20 -0
- package/scripts/gate-windows-r46.ps1 +56 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/windows-gate-evidence-r46.mjs +110 -0
- package/src/capability-ledger.mjs +1 -0
- package/src/compact-gateway-mcp-tools.mjs +7 -5
- package/src/deadbyte-cli.mjs +7 -3
- 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 +4 -0
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/observation-runtime.mjs +25 -4
- 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/release-generation.mjs +22 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +3 -3
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/observation-runtime.test.mjs +29 -2
- 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-finalization.test.mjs +3 -3
- package/test/release-generation.test.mjs +25 -11
- package/test/release-version.test.mjs +99 -44
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
|
@@ -16,6 +16,40 @@ $LogDir = Join-Path $RuntimeDir 'logs'
|
|
|
16
16
|
$ConfigPath = if ($env:DEADBYTE_CONTROLLER_CONFIG) { $env:DEADBYTE_CONTROLLER_CONFIG } else { Join-Path $Root 'deadbyte-controller.config.psd1' }
|
|
17
17
|
|
|
18
18
|
function Fail([string]$Message) { throw "DEADBYTE controller: $Message" }
|
|
19
|
+
|
|
20
|
+
function Get-ControllerMutationMutexName {
|
|
21
|
+
$normalized = ([System.IO.Path]::GetFullPath($RuntimeDir)).TrimEnd('\\').ToLowerInvariant()
|
|
22
|
+
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
23
|
+
try {
|
|
24
|
+
$bytes = [System.Text.Encoding]::UTF8.GetBytes($normalized)
|
|
25
|
+
$digest = $sha.ComputeHash($bytes)
|
|
26
|
+
} finally {
|
|
27
|
+
$sha.Dispose()
|
|
28
|
+
}
|
|
29
|
+
$hex = ([System.BitConverter]::ToString($digest)).Replace('-', '').ToLowerInvariant().Substring(0, 24)
|
|
30
|
+
return "Local\DEADBYTE-MCP-Controller-$hex"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Invoke-WithControllerMutationLock([string]$Name, [scriptblock]$Body) {
|
|
34
|
+
$mutexName = Get-ControllerMutationMutexName
|
|
35
|
+
$mutex = New-Object System.Threading.Mutex($false, $mutexName)
|
|
36
|
+
$held = $false
|
|
37
|
+
try {
|
|
38
|
+
try {
|
|
39
|
+
$held = $mutex.WaitOne([TimeSpan]::FromSeconds(180))
|
|
40
|
+
} catch [System.Threading.AbandonedMutexException] {
|
|
41
|
+
$held = $true
|
|
42
|
+
}
|
|
43
|
+
if (-not $held) { Fail "controller mutation lock timeout for '$Name'" }
|
|
44
|
+
& $Body
|
|
45
|
+
} finally {
|
|
46
|
+
if ($held) {
|
|
47
|
+
try { $mutex.ReleaseMutex() } catch {}
|
|
48
|
+
}
|
|
49
|
+
$mutex.Dispose()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
19
53
|
function Ensure-Directory([string]$Path) { if (-not (Test-Path -LiteralPath $Path -PathType Container)) { New-Item -ItemType Directory -Path $Path -Force | Out-Null } }
|
|
20
54
|
function Ledger-Path([string]$Name) { Join-Path $RuntimeDir ("{0}.instance.json" -f $Name.ToLowerInvariant()) }
|
|
21
55
|
function Log-Path([string]$Name, [string]$Kind) { Join-Path $LogDir ("{0}.{1}.log" -f $Name.ToLowerInvariant(), $Kind) }
|
|
@@ -1275,7 +1309,7 @@ function Clean-Stale {
|
|
|
1275
1309
|
}
|
|
1276
1310
|
}
|
|
1277
1311
|
|
|
1278
|
-
function Run-
|
|
1312
|
+
function Run-CommandCore([string]$Name) {
|
|
1279
1313
|
$cfg = Get-Config
|
|
1280
1314
|
Assert-ControllerRootBound $cfg
|
|
1281
1315
|
Ensure-Directory $RuntimeDir; Ensure-Directory $LogDir
|
|
@@ -1318,6 +1352,20 @@ function Run-Command([string]$Name) {
|
|
|
1318
1352
|
}
|
|
1319
1353
|
}
|
|
1320
1354
|
|
|
1355
|
+
function Run-Command([string]$Name) {
|
|
1356
|
+
$mutating = $Name -in @(
|
|
1357
|
+
'start','resume','park','stop','restart','toggle','clean',
|
|
1358
|
+
'armhost','disarmhost','armprocess','disarmprocess','armcoding','disarmcoding',
|
|
1359
|
+
'armautonomous','disarmautonomous','armdesktop','disarmdesktop',
|
|
1360
|
+
'supervisorstart','supervisorstop','supervisorrecover'
|
|
1361
|
+
)
|
|
1362
|
+
if (-not $mutating) {
|
|
1363
|
+
Run-CommandCore $Name
|
|
1364
|
+
return
|
|
1365
|
+
}
|
|
1366
|
+
Invoke-WithControllerMutationLock $Name { Run-CommandCore $Name }
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1321
1369
|
if ($Command -ne 'menu') {
|
|
1322
1370
|
$recordsStartFailure = $Command -in @('start','resume','restart')
|
|
1323
1371
|
if ($recordsStartFailure) { Remove-Item -LiteralPath (Controller-StartFailurePath) -Force -ErrorAction SilentlyContinue }
|
|
@@ -1336,7 +1384,7 @@ while ($true) {
|
|
|
1336
1384
|
Clear-Host
|
|
1337
1385
|
$cfg = Get-Config
|
|
1338
1386
|
Assert-ControllerRootBound $cfg
|
|
1339
|
-
Write-Host 'DEADBYTE MCP ON-DEMAND CONTROLLER V0.15.
|
|
1387
|
+
Write-Host 'DEADBYTE MCP ON-DEMAND CONTROLLER V0.15.2 / R46'
|
|
1340
1388
|
Write-Host '======================================'
|
|
1341
1389
|
Show-State $cfg | Out-Null
|
|
1342
1390
|
Write-Host ''
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"profiles": {
|
|
21
21
|
"ping_hold": {
|
|
22
|
-
"executable": "C:\\
|
|
22
|
+
"executable": "C:\\WINDOWS\\System32\\cmd.exe",
|
|
23
23
|
"executable_sha256": "97ac98b1a92c286054cce55239cfccdfc23a5517bd07fe693072c9ca96c7dabb",
|
|
24
24
|
"root_ids": [
|
|
25
25
|
"core"
|
|
@@ -42,6 +42,68 @@
|
|
|
42
42
|
"input_actions": {
|
|
43
43
|
"ping": "echo DEADBYTE_ACTION_PING\r\n"
|
|
44
44
|
}
|
|
45
|
+
},
|
|
46
|
+
"powershell": {
|
|
47
|
+
"executable": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
48
|
+
"executable_sha256": "8bb6fa8c283b4d92120b1ef249a9b311b0f804d4cabbe9981159976c8be76a5e",
|
|
49
|
+
"root_ids": [
|
|
50
|
+
"core"
|
|
51
|
+
],
|
|
52
|
+
"prefix_args": [
|
|
53
|
+
"-NoLogo",
|
|
54
|
+
"-NoProfile",
|
|
55
|
+
"-NonInteractive",
|
|
56
|
+
"-Command"
|
|
57
|
+
],
|
|
58
|
+
"transport": "pipe",
|
|
59
|
+
"max_args": 1,
|
|
60
|
+
"max_arg_bytes": 8192,
|
|
61
|
+
"max_sessions": 4,
|
|
62
|
+
"max_input_bytes": 65536,
|
|
63
|
+
"max_retained_output_bytes": 1048576,
|
|
64
|
+
"env_allow": [
|
|
65
|
+
"PATH",
|
|
66
|
+
"TEMP",
|
|
67
|
+
"TMP",
|
|
68
|
+
"SystemRoot",
|
|
69
|
+
"WINDIR",
|
|
70
|
+
"USERPROFILE",
|
|
71
|
+
"PSModulePath",
|
|
72
|
+
"COMSPEC",
|
|
73
|
+
"PATHEXT"
|
|
74
|
+
],
|
|
75
|
+
"input_actions": {}
|
|
76
|
+
},
|
|
77
|
+
"powershell_repl": {
|
|
78
|
+
"executable": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
79
|
+
"executable_sha256": "8bb6fa8c283b4d92120b1ef249a9b311b0f804d4cabbe9981159976c8be76a5e",
|
|
80
|
+
"root_ids": [
|
|
81
|
+
"core"
|
|
82
|
+
],
|
|
83
|
+
"prefix_args": [
|
|
84
|
+
"-NoLogo",
|
|
85
|
+
"-NoProfile",
|
|
86
|
+
"-NonInteractive"
|
|
87
|
+
],
|
|
88
|
+
"transport": "pipe",
|
|
89
|
+
"allow_raw_input": true,
|
|
90
|
+
"max_args": 0,
|
|
91
|
+
"max_arg_bytes": 256,
|
|
92
|
+
"max_sessions": 2,
|
|
93
|
+
"max_input_bytes": 65536,
|
|
94
|
+
"max_retained_output_bytes": 4194304,
|
|
95
|
+
"env_allow": [
|
|
96
|
+
"PATH",
|
|
97
|
+
"TEMP",
|
|
98
|
+
"TMP",
|
|
99
|
+
"SystemRoot",
|
|
100
|
+
"WINDIR",
|
|
101
|
+
"USERPROFILE",
|
|
102
|
+
"PSModulePath",
|
|
103
|
+
"COMSPEC",
|
|
104
|
+
"PATHEXT"
|
|
105
|
+
],
|
|
106
|
+
"input_actions": {}
|
|
45
107
|
}
|
|
46
108
|
}
|
|
47
109
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# DEADBYTE MCP V0.15.
|
|
1
|
+
# DEADBYTE MCP V0.15.2 / R46 ARCHITECTURE
|
|
2
2
|
|
|
3
3
|
```mermaid
|
|
4
4
|
flowchart TD
|
|
@@ -9,7 +9,7 @@ flowchart TD
|
|
|
9
9
|
D --> MI[Optional Machine 4 Tools]
|
|
10
10
|
D --> F[Optional Host 3 Tools]
|
|
11
11
|
D --> G[Optional Agent 7 Tools]
|
|
12
|
-
D --> PR[Optional Process
|
|
12
|
+
D --> PR[Optional Process 7 Tools]
|
|
13
13
|
D --> V[Optional Coding 22 Tools]
|
|
14
14
|
D --> MF[Optional Machine FS 8 Tools]
|
|
15
15
|
D --> OB[Optional Observation 6 Tools]
|
|
@@ -59,8 +59,7 @@ invocation may recover a stale owner only when no live guardian remains.
|
|
|
59
59
|
## Exact capability catalogs
|
|
60
60
|
|
|
61
61
|
Only complete combinations of base plus optional machine, host, agent, process, coding,
|
|
62
|
-
machine-filesystem, observation, and autonomous surfaces are valid at the HTTP bridge.
|
|
63
|
-
enabled R34 runtime exposes exactly 73 tools. Process authority is independently granted;
|
|
62
|
+
machine-filesystem, observation, and autonomous surfaces are valid at the HTTP bridge. The current fully enabled R46 runtime exposes exactly 83 full tools and 43 compact tools. Process authority is independently granted;
|
|
64
63
|
machine-filesystem and observation reuse coding-policy roots; autonomous requires the complete
|
|
65
64
|
coding surface. Partial, dependency-invalid, unknown, or extra catalogs fail closed.
|
|
66
65
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"r42:parent:verify": "node scripts/verify-r42-parent.mjs",
|
|
17
17
|
"r42:eval": "node scripts/agent-eval-r42.mjs",
|
|
18
18
|
"self-update:runtime": "node scripts/runtime-self-update.mjs",
|
|
19
|
-
"self-update:deferred": "node scripts/deferred-slot-operation-
|
|
20
|
-
"release:parity": "node scripts/release-parity-
|
|
21
|
-
"release:final:write": "node scripts/final-closure-
|
|
22
|
-
"release:final:verify": "node scripts/final-closure-verify-
|
|
19
|
+
"self-update:deferred": "node scripts/deferred-slot-operation-r46.mjs",
|
|
20
|
+
"release:parity": "node scripts/release-parity-r46.mjs",
|
|
21
|
+
"release:final:write": "node scripts/final-closure-r46.mjs",
|
|
22
|
+
"release:final:verify": "node scripts/final-closure-verify-r46.mjs",
|
|
23
23
|
"proof": "node src/proof-cli.mjs workspaces/demo input.txt",
|
|
24
24
|
"verify": "node src/verifier-cli.mjs workspaces/demo evidence/latest.json",
|
|
25
25
|
"trust:init": "node src/trust-init.mjs",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"contained:run": "node src/contained-cli.mjs workspaces/demo input.txt",
|
|
43
43
|
"contained:smoke": "node src/contained-smoke.mjs",
|
|
44
44
|
"mcp:contained:smoke": "node src/mcp-contained-smoke-client.mjs",
|
|
45
|
-
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
45
|
+
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r46.ps1",
|
|
46
46
|
"authority:smoke": "node src/authority-smoke.mjs",
|
|
47
47
|
"mcp:remote:smoke": "node src/remote-mcp-smoke-client.mjs",
|
|
48
48
|
"mcp:host:smoke": "node src/mcp-host-smoke-client.mjs",
|
|
@@ -63,7 +63,9 @@
|
|
|
63
63
|
"doctor": "node src/deadbyte-cli.mjs doctor",
|
|
64
64
|
"r45:parent:verify": "node scripts/verify-r45-parent.mjs",
|
|
65
65
|
"r45:full:test": "node scripts/full-regression-r45.mjs",
|
|
66
|
-
"finalize:r45": "node scripts/finalize-r45.mjs"
|
|
66
|
+
"finalize:r45": "node scripts/finalize-r45.mjs",
|
|
67
|
+
"r46:fileless:test": "node --test --test-concurrency=1 test/process-policy.test.mjs test/process-policy-refresh.test.mjs test/release-generation.test.mjs test/process-runtime.test.mjs test/process-mcp-tools.test.mjs test/compact-gateway-r36.test.mjs test/capability-ledger-r36.test.mjs test/remote-device-r44.test.mjs test/observation-runtime.test.mjs test/observation-mcp-tools.test.mjs",
|
|
68
|
+
"r46:full:test": "node scripts/full-regression-r46.mjs"
|
|
67
69
|
},
|
|
68
70
|
"bundleDependencies": [
|
|
69
71
|
"@modelcontextprotocol/server",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createHash, createPublicKey, randomBytes } from 'node:crypto';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { parseActivePointer } from '../src/release-slot.mjs';
|
|
8
|
+
import { loadPowerShellDataFile, runSlotUpdateCli, verifySlotUpdateEnvelope } from '../src/runtime-update.mjs';
|
|
9
|
+
|
|
10
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
11
|
+
const RECEIPT_ID=/^r27-[A-Za-z0-9-]+$/;
|
|
12
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const packageRoot=path.resolve(here,'..');
|
|
14
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
15
|
+
const evidenceRoot=path.join(stateRoot,'evidence','deferred-slot-operations');
|
|
16
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
17
|
+
const atomicJson=async(file,value)=>{
|
|
18
|
+
await mkdir(path.dirname(file),{recursive:true});
|
|
19
|
+
const temp=`${file}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;
|
|
20
|
+
await writeFile(temp,`${JSON.stringify(value,null,2)}\n`,{flag:'wx'});
|
|
21
|
+
await import('node:fs/promises').then(fs=>fs.rename(temp,file));
|
|
22
|
+
};
|
|
23
|
+
function activationArgs(manifestSha){
|
|
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.15.2',
|
|
26
|
+
'--expected-manifest',manifestSha,'--process-policy',path.join(packageRoot,'controller','deadbyte-process-policy.json'),
|
|
27
|
+
'--process-policy-sha256',null,'--desktop-policy',path.join(packageRoot,'controller','deadbyte-desktop-policy.json'),
|
|
28
|
+
'--desktop-policy-sha256',null,'--semantic-lsp-config',path.join(packageRoot,'controller','deadbyte-semantic-lsp-config.json'),
|
|
29
|
+
'--semantic-lsp-config-sha256',null,'--enable-machine-fs','--enable-observation','--enable-desktop','--enable-autonomous-supervisor'];
|
|
30
|
+
}
|
|
31
|
+
async function bridgeBootstrapArgs(explicitBridgeRoot=null,explicitBridgeSha256=null){
|
|
32
|
+
const hasRoot=typeof explicitBridgeRoot==='string'&&explicitBridgeRoot.length>0;
|
|
33
|
+
const hasSha=typeof explicitBridgeSha256==='string'&&explicitBridgeSha256.length>0;
|
|
34
|
+
if(hasRoot!==hasSha) throw new Error('bridge override requires exact root + SHA pair');
|
|
35
|
+
let bridgeRootRaw,expectedSha256=null;
|
|
36
|
+
if(hasRoot){
|
|
37
|
+
if(!path.isAbsolute(explicitBridgeRoot)||!HEX64.test(explicitBridgeSha256)) throw new Error('bridge override requires exact root + SHA pair');
|
|
38
|
+
bridgeRootRaw=explicitBridgeRoot;expectedSha256=explicitBridgeSha256;
|
|
39
|
+
}else{
|
|
40
|
+
const pointer=parseActivePointer(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
41
|
+
const config=await loadPowerShellDataFile(pointer.config_path);
|
|
42
|
+
bridgeRootRaw=config?.Bridge?.WorkingDirectory;
|
|
43
|
+
if(typeof bridgeRootRaw!=='string'||!path.isAbsolute(bridgeRootRaw)) throw new Error('active controller Bridge.WorkingDirectory must be absolute');
|
|
44
|
+
}
|
|
45
|
+
const bridgeRoot=await realpath(path.resolve(bridgeRootRaw));
|
|
46
|
+
const packageFile=path.join(bridgeRoot,'package.json');
|
|
47
|
+
const bridgeScript=path.join(bridgeRoot,'src','bridge.mjs');
|
|
48
|
+
const pkg=JSON.parse(await readFile(packageFile,'utf8'));
|
|
49
|
+
if(pkg?.name!=='deadbyte-chatgpt-bridge') throw new Error('active Bridge package identity mismatch');
|
|
50
|
+
const scriptSha256=sha256(await readFile(bridgeScript));
|
|
51
|
+
if(expectedSha256&&scriptSha256!==expectedSha256) throw new Error('bridge override SHA-256 mismatch');
|
|
52
|
+
return ['--bridge-root',bridgeRoot,'--bridge-script-sha256',scriptSha256];
|
|
53
|
+
}
|
|
54
|
+
async function rollbackArgs(receiptPath){
|
|
55
|
+
const resolved=await realpath(path.resolve(receiptPath));
|
|
56
|
+
const allowed=path.join(stateRoot,'evidence','updates')+path.sep;
|
|
57
|
+
if(!resolved.startsWith(allowed)||path.basename(resolved).toLowerCase()!=='receipt.json'||!RECEIPT_ID.test(path.basename(path.dirname(resolved)))) {
|
|
58
|
+
throw new Error('rollback receipt must be one signed activation receipt under the update evidence store');
|
|
59
|
+
}
|
|
60
|
+
const envelope=JSON.parse(await readFile(resolved,'utf8'));
|
|
61
|
+
const publicKey=createPublicKey(await readFile(path.join(stateRoot,'trust','ed25519-public.pem')));
|
|
62
|
+
if(!verifySlotUpdateEnvelope(envelope,publicKey)||envelope.body?.rollback_performed!==false) throw new Error('rollback activation receipt verification failed');
|
|
63
|
+
const pointer=parseActivePointer(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
64
|
+
if(pointer.release_id!==envelope.body?.active_pointer?.release_id||pointer.generation_root!==envelope.body?.active_pointer?.generation_root) {
|
|
65
|
+
throw new Error('rollback receipt does not identify the active release generation');
|
|
66
|
+
}
|
|
67
|
+
return ['--rollback-receipt',resolved];
|
|
68
|
+
}
|
|
69
|
+
async function normalizedArgs(mode,value,{explicitBridgeRoot=null,explicitBridgeSha256=null}={}){
|
|
70
|
+
if(mode==='activate'){
|
|
71
|
+
const args=activationArgs(value);
|
|
72
|
+
args[args.indexOf('--process-policy-sha256')+1]=sha256(await readFile(path.join(packageRoot,'controller','deadbyte-process-policy.json')));
|
|
73
|
+
args[args.indexOf('--desktop-policy-sha256')+1]=sha256(await readFile(path.join(packageRoot,'controller','deadbyte-desktop-policy.json')));
|
|
74
|
+
args[args.indexOf('--semantic-lsp-config-sha256')+1]=sha256(await readFile(path.join(packageRoot,'controller','deadbyte-semantic-lsp-config.json')));
|
|
75
|
+
args.push(...await bridgeBootstrapArgs(explicitBridgeRoot,explicitBridgeSha256));
|
|
76
|
+
return args;
|
|
77
|
+
}
|
|
78
|
+
if(mode==='rollback'){
|
|
79
|
+
if(explicitBridgeRoot||explicitBridgeSha256) throw new Error('rollback does not accept bridge override');
|
|
80
|
+
return rollbackArgs(value);
|
|
81
|
+
}
|
|
82
|
+
throw new Error("operation must be exactly 'activate' or 'rollback'");
|
|
83
|
+
}
|
|
84
|
+
const rawArgs=process.argv.slice(2);
|
|
85
|
+
const mode=rawArgs[0],value=rawArgs[1];
|
|
86
|
+
if(!value||!['activate','rollback'].includes(mode)) throw new Error('usage: deferred-slot-operation-r46.mjs <activate manifest_sha256|rollback activation_receipt> [--bridge-root ABS --bridge-script-sha256 HEX64]');
|
|
87
|
+
let cursor=2,explicitBridgeRoot=null,explicitBridgeSha256=null;
|
|
88
|
+
if(rawArgs[cursor]==='--bridge-root'){
|
|
89
|
+
explicitBridgeRoot=rawArgs[cursor+1]??null;
|
|
90
|
+
if(rawArgs[cursor+2]!=='--bridge-script-sha256') throw new Error('bridge override requires exact root + SHA pair');
|
|
91
|
+
explicitBridgeSha256=rawArgs[cursor+3]??null;cursor+=4;
|
|
92
|
+
}
|
|
93
|
+
let workerFlag=null,operationIdArg=null;
|
|
94
|
+
if(rawArgs[cursor]==='--worker'){workerFlag='--worker';operationIdArg=rawArgs[cursor+1]??null;cursor+=2;}
|
|
95
|
+
if(cursor!==rawArgs.length) throw new Error('deferred slot operation arguments invalid');
|
|
96
|
+
if((explicitBridgeRoot===null)!==(explicitBridgeSha256===null)) throw new Error('bridge override requires exact root + SHA pair');
|
|
97
|
+
if(mode==='rollback'&&(explicitBridgeRoot||explicitBridgeSha256)) throw new Error('rollback does not accept bridge override');
|
|
98
|
+
if(workerFlag==='--worker'){
|
|
99
|
+
if(!/^[0-9a-f]{32}$/.test(operationIdArg??'')) throw new Error('deferred worker operation id invalid');
|
|
100
|
+
const statePath=path.join(evidenceRoot,`${operationIdArg}.json`);
|
|
101
|
+
await new Promise(resolve=>setTimeout(resolve,4000));
|
|
102
|
+
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});
|
|
103
|
+
try{
|
|
104
|
+
const args=await normalizedArgs(mode,value,{explicitBridgeRoot,explicitBridgeSha256});
|
|
105
|
+
const result=await runSlotUpdateCli(args,{stateRoot});
|
|
106
|
+
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'completed',
|
|
107
|
+
completed_at_utc:new Date().toISOString(),receipt_path:result.receipt_path,receipt_sha256:result.receipt_sha256,
|
|
108
|
+
active_release:result.pointer.release_id,generation_root:result.pointer.generation_root,bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
109
|
+
}catch(error){
|
|
110
|
+
await atomicJson(statePath,{schema:'deadbyte.deferred-slot-operation.v1',operation_id:operationIdArg,mode,state:'failed',
|
|
111
|
+
completed_at_utc:new Date().toISOString(),error:String(error?.message??error),bridge_root:explicitBridgeRoot,bridge_script_sha256:explicitBridgeSha256});
|
|
112
|
+
process.exitCode=1;
|
|
113
|
+
}
|
|
114
|
+
}else{
|
|
115
|
+
const operationId=randomBytes(16).toString('hex');
|
|
116
|
+
const statePath=path.join(evidenceRoot,`${operationId}.json`);
|
|
117
|
+
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});
|
|
118
|
+
const workerArgs=[fileURLToPath(import.meta.url),mode,value];
|
|
119
|
+
if(explicitBridgeRoot) workerArgs.push('--bridge-root',explicitBridgeRoot,'--bridge-script-sha256',explicitBridgeSha256);
|
|
120
|
+
workerArgs.push('--worker',operationId);
|
|
121
|
+
const child=spawn(process.execPath,workerArgs,{
|
|
122
|
+
cwd:packageRoot,detached:true,stdio:'ignore',windowsHide:true,env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}
|
|
123
|
+
});
|
|
124
|
+
child.unref();
|
|
125
|
+
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}));
|
|
126
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign } 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
|
+
|
|
7
|
+
const DOMAIN=Buffer.from('DEADBYTE-R46-FINAL-CLOSURE-V1\0','utf8');
|
|
8
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const packageRoot=path.resolve(here,'..');
|
|
10
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
11
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
13
|
+
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
14
|
+
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
15
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
16
|
+
async function fileEvidence(file){const bytes=await readFile(file);return {path:path.resolve(file),sha256:sha256(bytes)};}
|
|
17
|
+
|
|
18
|
+
export async function writeFinalClosure({root=packageRoot,runtimeStateRoot=stateRoot,clock=()=>new Date()}={}){
|
|
19
|
+
const candidate=path.resolve(root),state=path.resolve(runtimeStateRoot);
|
|
20
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
21
|
+
const manifestSha256=sha256(manifestBytes);
|
|
22
|
+
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
23
|
+
insist(pointer.version==='0.15.2'&&pointer.manifest_sha256===manifestSha256,'R46 final closure active candidate mismatch');
|
|
24
|
+
insist(pointer.release_id===`v0.15.2-${manifestSha256.slice(0,16)}`,'R46 final closure release id mismatch');
|
|
25
|
+
|
|
26
|
+
const gatePath=path.join(state,'evidence','release',`r46-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
27
|
+
const parityPath=path.join(state,'evidence','release',`r46-parity-${manifestSha256.slice(0,16)}.json`);
|
|
28
|
+
const gate=JSON.parse(await readFile(gatePath,'utf8'));
|
|
29
|
+
const parity=JSON.parse(await readFile(parityPath,'utf8'));
|
|
30
|
+
insist(gate.schema==='deadbyte.r46-windows-gate.v1'&&gate.status==='passed'&&gate.version==='0.15.2','R46 gate evidence invalid');
|
|
31
|
+
insist(gate.manifest_sha256===manifestSha256,'R46 gate evidence candidate mismatch');
|
|
32
|
+
insist(parity.schema==='deadbyte.r46-parity.v1'&&parity.status==='passed'&&parity.version==='0.15.2','R46 parity evidence invalid');
|
|
33
|
+
insist(parity.candidate?.manifest_sha256===manifestSha256&&parity.candidate?.generation_manifest_sha256===pointer.generation_manifest_sha256,'R46 parity active generation mismatch');
|
|
34
|
+
insist(parity.process_policy?.target_repin_verified===true,'R46 target-machine process repin proof missing');
|
|
35
|
+
insist(parity.fileless_powershell?.exit_code===0&&parity.fileless_powershell?.pscommandpath_empty===true&&parity.fileless_powershell?.invocation_path_empty===true&&parity.fileless_powershell?.new_temp_ps1_count===0,'R46 fileless PowerShell proof missing');
|
|
36
|
+
insist(parity.persistent_powershell_repl?.state_persisted===true&&parity.persistent_powershell_repl?.observed_value===42&&parity.persistent_powershell_repl?.wait_for_verified===true&&parity.persistent_powershell_repl?.tail_read_verified===true&&parity.persistent_powershell_repl?.raw_input_not_journaled===true,'R46 persistent PowerShell REPL proof missing');
|
|
37
|
+
insist(parity.retained_output_ring?.eviction_verified===true&&parity.retained_output_ring?.tail_marker_verified===true&&parity.retained_output_ring?.earliest_offset>0,'R46 retained-output ring proof missing');
|
|
38
|
+
|
|
39
|
+
const completed=clock();
|
|
40
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'R46 final closure clock invalid');
|
|
41
|
+
const body={
|
|
42
|
+
schema:'deadbyte.r46-final-closure.v1',
|
|
43
|
+
version:'0.15.2',
|
|
44
|
+
train:'R46',
|
|
45
|
+
release_label:'FILELESS POWERSHELL PRODUCT BASELINE',
|
|
46
|
+
completed_at_utc:completed.toISOString(),
|
|
47
|
+
active_pointer:pointer,
|
|
48
|
+
parent:gate.parent,
|
|
49
|
+
runtime_predecessor:gate.runtime_predecessor,
|
|
50
|
+
features:gate.features,
|
|
51
|
+
process_policy:parity.process_policy,
|
|
52
|
+
fileless_powershell:parity.fileless_powershell,
|
|
53
|
+
persistent_powershell_repl:parity.persistent_powershell_repl,
|
|
54
|
+
retained_output_ring:parity.retained_output_ring,
|
|
55
|
+
evidence:{
|
|
56
|
+
windows_gate:await fileEvidence(gatePath),
|
|
57
|
+
parity:await fileEvidence(parityPath)
|
|
58
|
+
},
|
|
59
|
+
execution_provider:'deadbyte_mcp',
|
|
60
|
+
rdc_operation_count:0
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const privateKey=createPrivateKey(await readFile(path.join(state,'trust','ed25519-private.pem')));
|
|
64
|
+
const publicKey=createPublicKey(privateKey);
|
|
65
|
+
insist(privateKey.asymmetricKeyType==='ed25519'&&publicKey.asymmetricKeyType==='ed25519','R46 closure signing key must be Ed25519');
|
|
66
|
+
const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
|
|
67
|
+
const bodyBytes=Buffer.from(canonicalJson(body),'utf8');
|
|
68
|
+
const signature=cryptoSign(null,Buffer.concat([DOMAIN,bodyBytes]),privateKey);
|
|
69
|
+
const envelope={
|
|
70
|
+
body,
|
|
71
|
+
body_sha256:sha256(bodyBytes),
|
|
72
|
+
attestation:{
|
|
73
|
+
algorithm:'ed25519',
|
|
74
|
+
domain:'DEADBYTE-R46-FINAL-CLOSURE-V1',
|
|
75
|
+
key_id:keyId,
|
|
76
|
+
signature_base64:signature.toString('base64')
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
envelope.closure_sha256=sha256(Buffer.from(canonicalJson(envelope),'utf8'));
|
|
80
|
+
const bytes=Buffer.from(`${JSON.stringify(envelope,null,2)}\n`,'utf8');
|
|
81
|
+
const dir=path.join(state,'evidence','release');
|
|
82
|
+
await mkdir(dir,{recursive:true});
|
|
83
|
+
const file=path.join(dir,`r46-final-${manifestSha256.slice(0,16)}.json`);
|
|
84
|
+
try{
|
|
85
|
+
const handle=await open(file,'wx',0o600);
|
|
86
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
87
|
+
return {status:'written',path:file,sha256:sha256(bytes),closure_sha256:envelope.closure_sha256,release_id:pointer.release_id};
|
|
88
|
+
}catch(error){
|
|
89
|
+
if(error?.code!=='EEXIST')throw error;
|
|
90
|
+
const existingBytes=await readFile(file);
|
|
91
|
+
const existing=JSON.parse(existingBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
92
|
+
insist(existing.body?.version==='0.15.2'&&existing.body?.active_pointer?.manifest_sha256===manifestSha256,'existing R46 closure identity mismatch');
|
|
93
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),closure_sha256:existing.closure_sha256,release_id:pointer.release_id};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
98
|
+
if(invoked)writeFinalClosure().then(value=>console.log(JSON.stringify(value))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash, createPublicKey, verify as cryptoVerify } from 'node:crypto';
|
|
2
|
+
import { readFile } 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 DOMAIN=Buffer.from('DEADBYTE-R46-FINAL-CLOSURE-V1\0','utf8');
|
|
8
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const packageRoot=path.resolve(here,'..');
|
|
10
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
11
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
|
+
const canonical=value=>Array.isArray(value)?value.map(canonical):value&&typeof value==='object'
|
|
13
|
+
?Object.fromEntries(Object.keys(value).sort().map(key=>[key,canonical(value[key])])):value;
|
|
14
|
+
const canonicalJson=value=>JSON.stringify(canonical(value));
|
|
15
|
+
function insist(ok,message){if(!ok)throw new Error(message);}
|
|
16
|
+
|
|
17
|
+
export async function verifyFinalClosureObject(envelope,{root=packageRoot,runtimeStateRoot=stateRoot}={}){
|
|
18
|
+
const candidate=path.resolve(root),state=path.resolve(runtimeStateRoot);
|
|
19
|
+
insist(envelope&&typeof envelope==='object'&&!Array.isArray(envelope),'R46 closure envelope invalid');
|
|
20
|
+
insist(envelope.body?.schema==='deadbyte.r46-final-closure.v1','R46 closure schema mismatch');
|
|
21
|
+
insist(envelope.body?.version==='0.15.2'&&envelope.body?.train==='R46','R46 closure version/train mismatch');
|
|
22
|
+
insist(envelope.body?.release_label==='FILELESS POWERSHELL PRODUCT BASELINE','R46 closure label mismatch');
|
|
23
|
+
|
|
24
|
+
const bodyBytes=Buffer.from(canonicalJson(envelope.body),'utf8');
|
|
25
|
+
insist(envelope.body_sha256===sha256(bodyBytes),'R46 closure body SHA-256 mismatch');
|
|
26
|
+
insist(envelope.attestation?.algorithm==='ed25519'&&envelope.attestation?.domain==='DEADBYTE-R46-FINAL-CLOSURE-V1','R46 closure attestation contract mismatch');
|
|
27
|
+
const publicKey=createPublicKey(await readFile(path.join(state,'trust','ed25519-public.pem')));
|
|
28
|
+
insist(publicKey.asymmetricKeyType==='ed25519','R46 closure verify key must be Ed25519');
|
|
29
|
+
const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
|
|
30
|
+
insist(envelope.attestation.key_id===keyId,'R46 closure signer key mismatch');
|
|
31
|
+
insist(typeof envelope.attestation.signature_base64==='string'&&cryptoVerify(
|
|
32
|
+
null,Buffer.concat([DOMAIN,bodyBytes]),publicKey,Buffer.from(envelope.attestation.signature_base64,'base64')
|
|
33
|
+
),'R46 closure signature invalid');
|
|
34
|
+
|
|
35
|
+
const unsigned={body:envelope.body,body_sha256:envelope.body_sha256,attestation:envelope.attestation};
|
|
36
|
+
const closureSha256=sha256(Buffer.from(canonicalJson(unsigned),'utf8'));
|
|
37
|
+
insist(envelope.closure_sha256===closureSha256,'R46 closure digest mismatch');
|
|
38
|
+
|
|
39
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
40
|
+
const manifestSha256=sha256(manifestBytes);
|
|
41
|
+
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
42
|
+
insist(pointer.version==='0.15.2'&&pointer.manifest_sha256===manifestSha256,'R46 active pointer/candidate mismatch');
|
|
43
|
+
insist(pointer.release_id===`v0.15.2-${manifestSha256.slice(0,16)}`,'R46 active release id mismatch');
|
|
44
|
+
insist(envelope.body.active_pointer?.release_id===pointer.release_id&&
|
|
45
|
+
envelope.body.active_pointer?.manifest_sha256===pointer.manifest_sha256&&
|
|
46
|
+
envelope.body.active_pointer?.generation_manifest_sha256===pointer.generation_manifest_sha256,
|
|
47
|
+
'R46 closure active pointer drift');
|
|
48
|
+
|
|
49
|
+
const gateRef=envelope.body.evidence?.windows_gate;
|
|
50
|
+
const parityRef=envelope.body.evidence?.parity;
|
|
51
|
+
for(const [label,ref] of [['windows gate',gateRef],['parity',parityRef]]){
|
|
52
|
+
insist(ref&&typeof ref.path==='string'&&/^[0-9a-f]{64}$/.test(String(ref.sha256??'')),`R46 ${label} evidence reference invalid`);
|
|
53
|
+
const bytes=await readFile(path.resolve(ref.path));
|
|
54
|
+
insist(sha256(bytes)===ref.sha256,`R46 ${label} evidence hash mismatch`);
|
|
55
|
+
}
|
|
56
|
+
const gate=JSON.parse(await readFile(path.resolve(gateRef.path),'utf8'));
|
|
57
|
+
const parity=JSON.parse(await readFile(path.resolve(parityRef.path),'utf8'));
|
|
58
|
+
insist(gate.schema==='deadbyte.r46-windows-gate.v1'&&gate.status==='passed'&&gate.version==='0.15.2','R46 gate evidence invalid');
|
|
59
|
+
insist(gate.manifest_sha256===manifestSha256,'R46 gate candidate mismatch');
|
|
60
|
+
insist(gate.parent?.release_id==='v0.15.1-d55f00b5144dc31a'&&gate.parent?.manifest_sha256==='d55f00b5144dc31a7fe39e4dc60d640a94780da6aaeca0918e87636b816d7854','R46 sealed parent identity invalid');
|
|
61
|
+
insist(gate.runtime_predecessor?.release_id==='v0.15.2-0fb46542f6a4345f'&&gate.runtime_predecessor?.manifest_sha256==='0fb46542f6a4345f1c527565d06bf6e9b8e283260e47bf684fb70a60308fcf34','R46 reopened runtime predecessor identity invalid');
|
|
62
|
+
insist(envelope.body.runtime_predecessor?.release_id===gate.runtime_predecessor.release_id&&envelope.body.runtime_predecessor?.manifest_sha256===gate.runtime_predecessor.manifest_sha256,'R46 closure runtime predecessor mismatch');
|
|
63
|
+
insist(parity.schema==='deadbyte.r46-parity.v1'&&parity.status==='passed'&&parity.version==='0.15.2','R46 parity evidence invalid');
|
|
64
|
+
insist(parity.candidate?.release_id===pointer.release_id&&
|
|
65
|
+
parity.candidate?.manifest_sha256===pointer.manifest_sha256&&
|
|
66
|
+
parity.candidate?.generation_manifest_sha256===pointer.generation_manifest_sha256,
|
|
67
|
+
'R46 parity active generation mismatch');
|
|
68
|
+
insist(parity.process_policy?.target_repin_verified===true,'R46 target-machine process repin proof missing');
|
|
69
|
+
insist(parity.fileless_powershell?.exit_code===0&&
|
|
70
|
+
parity.fileless_powershell?.pscommandpath_empty===true&&
|
|
71
|
+
parity.fileless_powershell?.invocation_path_empty===true&&
|
|
72
|
+
parity.fileless_powershell?.new_temp_ps1_count===0,
|
|
73
|
+
'R46 fileless PowerShell proof invalid');
|
|
74
|
+
insist(parity.persistent_powershell_repl?.state_persisted===true&&
|
|
75
|
+
parity.persistent_powershell_repl?.observed_value===42&&
|
|
76
|
+
parity.persistent_powershell_repl?.wait_for_verified===true&&
|
|
77
|
+
parity.persistent_powershell_repl?.tail_read_verified===true&&
|
|
78
|
+
parity.persistent_powershell_repl?.raw_input_not_journaled===true,
|
|
79
|
+
'R46 persistent PowerShell REPL proof invalid');
|
|
80
|
+
insist(parity.retained_output_ring?.eviction_verified===true&&
|
|
81
|
+
parity.retained_output_ring?.tail_marker_verified===true&&
|
|
82
|
+
parity.retained_output_ring?.earliest_offset>0,
|
|
83
|
+
'R46 retained-output ring proof invalid');
|
|
84
|
+
insist(envelope.body.process_policy?.target_repin_verified===true,'R46 closure process repin proof invalid');
|
|
85
|
+
insist(envelope.body.fileless_powershell?.exit_code===0&&
|
|
86
|
+
envelope.body.fileless_powershell?.new_temp_ps1_count===0,
|
|
87
|
+
'R46 closure fileless proof invalid');
|
|
88
|
+
insist(envelope.body.persistent_powershell_repl?.state_persisted===true&&
|
|
89
|
+
envelope.body.persistent_powershell_repl?.observed_value===42&&
|
|
90
|
+
envelope.body.persistent_powershell_repl?.raw_input_not_journaled===true,
|
|
91
|
+
'R46 closure persistent REPL proof invalid');
|
|
92
|
+
insist(envelope.body.retained_output_ring?.eviction_verified===true&&
|
|
93
|
+
envelope.body.retained_output_ring?.tail_marker_verified===true,
|
|
94
|
+
'R46 closure retained-output ring proof invalid');
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
ok:true,status:'passed',version:'0.15.2',train:'R46',
|
|
98
|
+
release_id:pointer.release_id,
|
|
99
|
+
manifest_sha256:pointer.manifest_sha256,
|
|
100
|
+
generation_manifest_sha256:pointer.generation_manifest_sha256,
|
|
101
|
+
closure_sha256:envelope.closure_sha256,
|
|
102
|
+
signer_key_id:keyId,
|
|
103
|
+
fileless_powershell:true,
|
|
104
|
+
target_machine_process_repin:true
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function verifyFinalClosure({file=null,root=packageRoot,runtimeStateRoot=stateRoot}={}){
|
|
109
|
+
const state=path.resolve(runtimeStateRoot),candidate=path.resolve(root);
|
|
110
|
+
const manifestSha256=sha256(await readFile(path.join(candidate,'MANIFEST.SHA256')));
|
|
111
|
+
const target=file?path.resolve(file):path.join(state,'evidence','release',`r46-final-${manifestSha256.slice(0,16)}.json`);
|
|
112
|
+
const envelope=JSON.parse(await readFile(target,'utf8'));
|
|
113
|
+
return {...await verifyFinalClosureObject(envelope,{root:candidate,runtimeStateRoot:state}),path:target};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
117
|
+
if(invoked)verifyFinalClosure({file:process.argv[2]??null})
|
|
118
|
+
.then(value=>console.log(JSON.stringify(value,null,2)))
|
|
119
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const root=path.resolve(here,'..');
|
|
8
|
+
const testDir=path.join(root,'test');
|
|
9
|
+
const files=(await readdir(testDir,{withFileTypes:true}))
|
|
10
|
+
.filter(entry=>entry.isFile()&&entry.name.endsWith('.test.mjs')&&!['full-max-catalog-r44.test.mjs','full-max-catalog-r45.test.mjs'].includes(entry.name))
|
|
11
|
+
.map(entry=>path.join('test',entry.name))
|
|
12
|
+
.sort((a,b)=>a.localeCompare(b,'en'));
|
|
13
|
+
if(files.length<1) throw new Error('R46 full regression found no test files');
|
|
14
|
+
console.log(`R46-FULL-REGRESSION files=${files.length}`);
|
|
15
|
+
const result=spawnSync(process.execPath,['--test','--test-concurrency=1',...files],{
|
|
16
|
+
cwd:root,stdio:'inherit',windowsHide:true,shell:false
|
|
17
|
+
});
|
|
18
|
+
if(result.error) throw result.error;
|
|
19
|
+
if(result.signal) throw new Error(`R46 full regression terminated by ${result.signal}`);
|
|
20
|
+
process.exitCode=Number.isInteger(result.status)?result.status:1;
|