deadbyte-mcp 0.15.28 → 0.15.30
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 +76 -54
- package/PROVENANCE.json +21 -10
- package/README.txt +16 -14
- package/RELEASE.json +29 -10
- package/SBOM.cdx.json +3 -3
- package/STABILIZATION-PROGRESS.md +60 -0
- package/TEST-MATRIX.json +51 -93
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/deadbyte-controller.ps1 +4 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-host-policy.json +170 -0
- package/controller/deadbyte-process-policy.json +5 -5
- package/docs/R51-PROVENANCE.json +18 -0
- package/docs/R51-RELEASE.json +115 -0
- package/docs/R51-TEST-MATRIX.json +166 -0
- package/docs/R52-ACTIVATION-FORENSICS.md +85 -0
- package/native/deadbyte-process-host.cpp +5 -3
- package/package.json +96 -94
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/gate-windows-r52.ps1 +58 -0
- package/scripts/gate-windows-r53.ps1 +58 -0
- package/scripts/verify-r42-parent.mjs +1 -1
- package/scripts/verify-r43-parent.mjs +1 -1
- package/scripts/verify-r44-parent.mjs +1 -1
- package/scripts/verify-r45-parent.mjs +1 -1
- package/scripts/windows-gate-evidence-r52.mjs +94 -0
- package/scripts/windows-gate-evidence-r53.mjs +102 -0
- package/src/authority-smoke.mjs +1 -0
- package/src/coding-plane.mjs +43 -12
- package/src/coding-task-runtime.mjs +10 -4
- package/src/deadbyte-cli.mjs +4 -2
- package/src/doctor-r45.mjs +2 -1
- package/src/machine-fs-smoke-client.mjs +1 -0
- package/src/mcp-contained-smoke-client.mjs +1 -0
- package/src/mcp-host-smoke-client.mjs +1 -0
- package/src/mcp-smoke-client.mjs +1 -0
- package/src/process-smoke-client.mjs +1 -0
- package/src/release-slot.mjs +5 -2
- package/src/remote-session-guardian.mjs +12 -4
- package/src/runtime-coherence.mjs +13 -4
- package/src/runtime-update.mjs +28 -7
- package/src/state-root.mjs +8 -0
- package/src/trust.mjs +2 -1
- package/src/tunnel-stdio-compat-smoke.mjs +1 -0
- package/src/version.mjs +1 -1
- package/test/coding-runtime.test.mjs +70 -1
- package/test/commander-biff-reference-r48.test.mjs +1 -1
- package/test/helpers/coding-disconnect-server.mjs +11 -0
- package/test/r44-finalizer.test.mjs +4 -3
- package/test/r49-windows-gate.test.mjs +19 -9
- package/test/r51-release-contract.test.mjs +22 -20
- package/test/r52-host-revocation.test.mjs +65 -0
- package/test/r52-offline-predecessor-recovery.test.mjs +77 -0
- package/test/r52-release-contract.test.mjs +64 -0
- package/test/r53-release-contract.test.mjs +66 -0
- package/test/release-slot.test.mjs +2 -1
- package/test/skill-mcp-r44.test.mjs +3 -0
- package/test/stabilization-guardian.test.mjs +25 -0
- package/test/stabilization-parent-context.test.mjs +18 -0
- package/test/stabilization-powershell.test.mjs +26 -0
- package/test/stabilization-semver.test.mjs +23 -0
- package/test/stabilization-smoke-root.test.mjs +23 -0
- package/test/stabilization-state-root.test.mjs +60 -0
- package/test/stabilization-update-child-root.test.mjs +20 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, writeFile, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import * as runtimeUpdate from '../src/runtime-update.mjs';
|
|
7
|
+
|
|
8
|
+
async function fixture(t,{revoke='success',armed=true}={}) {
|
|
9
|
+
const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r52-offline-host-'));
|
|
10
|
+
t.after(()=>rm(base,{recursive:true,force:true}));
|
|
11
|
+
const controllerDir=path.join(base,'controller');
|
|
12
|
+
const runtimeDir=path.join(controllerDir,'runtime');
|
|
13
|
+
await mkdir(runtimeDir,{recursive:true});
|
|
14
|
+
const marker=path.join(runtimeDir,'host-grant.json');
|
|
15
|
+
if(armed) await writeFile(marker,'stale-host-grant\n','utf8');
|
|
16
|
+
const revokeSource=revoke==='failure' ? 'exit 7' : revoke==='unchanged' ? 'exit 0' : 'Remove-Item -LiteralPath $marker -Force -ErrorAction SilentlyContinue; exit 0';
|
|
17
|
+
const controller=[
|
|
18
|
+
'param([string]$Command)',
|
|
19
|
+
'$marker = Join-Path $PSScriptRoot "runtime\\host-grant.json"',
|
|
20
|
+
`if($Command -eq "disarmhost"){ ${revokeSource} }`,
|
|
21
|
+
'if($Command -eq "status"){',
|
|
22
|
+
' if(Test-Path -LiteralPath $marker){ $hostState = "ARMED [standing-grant]" } else { $hostState = "DISARMED [disarmed]" }',
|
|
23
|
+
' Write-Output "DEADBYTE MCP = OFFLINE"',
|
|
24
|
+
' Write-Output "Bridge PID : -"',
|
|
25
|
+
' Write-Output "Cloud PID : -"',
|
|
26
|
+
' Write-Output "Local ready : False"',
|
|
27
|
+
' Write-Output "Process plane : DISARMED [disarmed]"',
|
|
28
|
+
' Write-Output "Coding plane : DISARMED [disarmed]"',
|
|
29
|
+
' Write-Output "Autonomous : DISARMED [disarmed]"',
|
|
30
|
+
' Write-Output "Desktop plane : DISARMED [disarmed]"',
|
|
31
|
+
' Write-Output ("Host plane : {0}" -f $hostState)',
|
|
32
|
+
' exit 0',
|
|
33
|
+
'}',
|
|
34
|
+
'exit 9'
|
|
35
|
+
].join('\r\n');
|
|
36
|
+
await writeFile(path.join(controllerDir,'deadbyte-controller.ps1'),controller,'utf8');
|
|
37
|
+
return {base,marker};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
test('R52 offline predecessor recovery revokes stale Host authority before capture', async t => {
|
|
41
|
+
if(process.platform!=='win32'){ t.skip('Windows controller regression'); return; }
|
|
42
|
+
assert.equal(typeof runtimeUpdate.capturePredecessor,'function','capturePredecessor must be behavior-testable');
|
|
43
|
+
const {base,marker}=await fixture(t);
|
|
44
|
+
const result=await runtimeUpdate.capturePredecessor(runtimeUpdate.legacyContext(base),true);
|
|
45
|
+
assert.equal(result.mode,'offline-recovery');
|
|
46
|
+
assert.equal(result.status.host_armed,false);
|
|
47
|
+
await assert.rejects(readFile(marker,'utf8'),error=>error?.code==='ENOENT');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('R52 offline predecessor fails closed if Host revoke returns success but grant remains',async t=>{
|
|
51
|
+
if(process.platform!=='win32'){t.skip('Windows controller regression');return;}
|
|
52
|
+
const {base,marker}=await fixture(t,{revoke:'unchanged'});
|
|
53
|
+
await assert.rejects(runtimeUpdate.capturePredecessor(runtimeUpdate.legacyContext(base),true),/requires host plane DISARMED/);
|
|
54
|
+
assert.equal(await readFile(marker,'utf8'),'stale-host-grant\n');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('R52 failed Host revoke leaves predecessor grant intact and denies capture',async t=>{
|
|
58
|
+
if(process.platform!=='win32'){t.skip('Windows controller regression');return;}
|
|
59
|
+
const {base,marker}=await fixture(t,{revoke:'failure'});
|
|
60
|
+
await assert.rejects(runtimeUpdate.capturePredecessor(runtimeUpdate.legacyContext(base),true));
|
|
61
|
+
assert.equal(await readFile(marker,'utf8'),'stale-host-grant\n');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('R52 disarmed offline predecessor is accepted without invoking a failing revoke',async t=>{
|
|
65
|
+
if(process.platform!=='win32'){t.skip('Windows controller regression');return;}
|
|
66
|
+
const {base}=await fixture(t,{revoke:'failure',armed:false});
|
|
67
|
+
const result=await runtimeUpdate.capturePredecessor(runtimeUpdate.legacyContext(base),true);
|
|
68
|
+
assert.equal(result.mode,'offline-recovery');
|
|
69
|
+
assert.equal(result.status.host_armed,false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('R52 offline recovery requires explicit opt-in before any Host revocation',async t=>{
|
|
73
|
+
if(process.platform!=='win32'){t.skip('Windows controller regression');return;}
|
|
74
|
+
const {base,marker}=await fixture(t);
|
|
75
|
+
await assert.rejects(runtimeUpdate.capturePredecessor(runtimeUpdate.legacyContext(base),false),/requires ONLINE runtime/);
|
|
76
|
+
assert.equal(await readFile(marker,'utf8'),'stale-host-grant\n');
|
|
77
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
const root=new URL('../',import.meta.url);
|
|
6
|
+
const read=rel=>readFile(new URL(rel,root),'utf8');
|
|
7
|
+
const json=rel=>read(rel).then(JSON.parse);
|
|
8
|
+
const current=await json('package.json');
|
|
9
|
+
const historical=current.version!=='0.15.29';
|
|
10
|
+
const skipHistorical=t=>{if(historical){t.skip('R52 historical release contract is frozen at 0.15.29; current successor is '+current.version);return true;}return false;};
|
|
11
|
+
|
|
12
|
+
test('R52 current release identity is coherent across package lock source metadata SBOM and gate',async t=>{
|
|
13
|
+
if(skipHistorical(t))return;
|
|
14
|
+
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
15
|
+
json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
|
|
16
|
+
]);
|
|
17
|
+
assert.equal(pkg.version,'0.15.29');
|
|
18
|
+
assert.equal(lock.version,pkg.version);
|
|
19
|
+
assert.equal(lock.packages[''].version,pkg.version);
|
|
20
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.29'/);
|
|
21
|
+
assert.equal(release.version,pkg.version);
|
|
22
|
+
assert.equal(release.train,'R52');
|
|
23
|
+
assert.equal(provenance.version,pkg.version);
|
|
24
|
+
assert.equal(matrix.version,pkg.version);
|
|
25
|
+
assert.equal(matrix.train,'R52');
|
|
26
|
+
assert.equal(sbom.metadata.component.version,pkg.version);
|
|
27
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r52:0.15.29');
|
|
28
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r52\.ps1/);
|
|
29
|
+
assert.match(pkg.scripts['r52:focused:test'],/r52-offline-predecessor-recovery\.test\.mjs/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime rollback target',async t=>{
|
|
33
|
+
if(skipHistorical(t))return;
|
|
34
|
+
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
35
|
+
const activeParent={
|
|
36
|
+
release_id:'v0.15.27-1b1dbe24b45ad06a',
|
|
37
|
+
manifest_sha256:'1b1dbe24b45ad06a62771d15f510662952f3d7c3e5aa08053c6c61511c523e47',
|
|
38
|
+
generation_manifest_sha256:'f87a1637b3c55ba050d079d8a5e0862a2244b306be7e985880708e0d8ded3db0'
|
|
39
|
+
};
|
|
40
|
+
assert.deepEqual(release.runtime_predecessor,activeParent);
|
|
41
|
+
assert.equal(release.parent.release_id,'v0.15.28-b879bdff63478f28');
|
|
42
|
+
assert.equal(release.parent.version,'0.15.28');
|
|
43
|
+
assert.equal(release.parent.manifest_sha256,release.source_predecessor.manifest_file_sha256);
|
|
44
|
+
assert.equal(release.parent.tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
45
|
+
assert.equal(release.parent.generation_manifest_sha256,undefined);
|
|
46
|
+
assert.equal(release.source_predecessor.version,'0.15.28');
|
|
47
|
+
assert.equal(release.source_predecessor.tarball_sha256,'0a74332ba715d80a532dc871ece4826bdb917b8c7a442e71388a06d44698c411');
|
|
48
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,'b879bdff63478f28d5dafadeedc8182a8fd090ab676dc0ff3566ea51ee07326f');
|
|
49
|
+
assert.equal(provenance.parent_release_id,release.parent.release_id);
|
|
50
|
+
assert.equal(provenance.parent_manifest_sha256,release.parent.manifest_sha256);
|
|
51
|
+
assert.equal(provenance.parent_tarball_sha256,release.parent.tarball_sha256);
|
|
52
|
+
assert.deepEqual(provenance.runtime_predecessor,activeParent);
|
|
53
|
+
assert.equal(provenance.parent_generation_manifest_sha256,undefined);
|
|
54
|
+
assert.equal(provenance.source_predecessor_version,'0.15.28');
|
|
55
|
+
assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
56
|
+
assert.equal(provenance.source_predecessor_manifest_file_sha256,release.source_predecessor.manifest_file_sha256);
|
|
57
|
+
assert.equal(provenance.source_predecessor_activation_failure_receipt_sha256,'090f165c96da0d5a8e9c0815fd389fe94e8fe36c23a6878ee825b8f46bfb0bc2');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('R52 advertises updater-owned offline predecessor Host revocation only after implementation',async t=>{
|
|
61
|
+
if(skipHistorical(t))return;
|
|
62
|
+
const release=await json('RELEASE.json');
|
|
63
|
+
assert.equal(release.capabilities.offline_predecessor_host_revoke_before_capture,true);
|
|
64
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
const root=new URL('../',import.meta.url);
|
|
6
|
+
const read=rel=>readFile(new URL(rel,root),'utf8');
|
|
7
|
+
const json=rel=>read(rel).then(JSON.parse);
|
|
8
|
+
|
|
9
|
+
test('R53 current release identity is coherent across package lock source metadata SBOM and gate',async()=>{
|
|
10
|
+
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
11
|
+
json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
|
|
12
|
+
]);
|
|
13
|
+
assert.equal(pkg.version,'0.15.30');
|
|
14
|
+
assert.equal(lock.version,pkg.version);
|
|
15
|
+
assert.equal(lock.packages[''].version,pkg.version);
|
|
16
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.30'/);
|
|
17
|
+
assert.equal(release.version,pkg.version);
|
|
18
|
+
assert.equal(release.train,'R53');
|
|
19
|
+
assert.equal(provenance.version,pkg.version);
|
|
20
|
+
assert.equal(matrix.version,pkg.version);
|
|
21
|
+
assert.equal(matrix.train,'R53');
|
|
22
|
+
assert.equal(sbom.metadata.component.version,pkg.version);
|
|
23
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r53:0.15.30');
|
|
24
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r53\.ps1/);
|
|
25
|
+
assert.match(pkg.scripts['r53:focused:test'],/stabilization-semver\.test\.mjs/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('R53 lineage binds exact published 0.15.29 as parent and runtime predecessor',async()=>{
|
|
29
|
+
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
30
|
+
const parent={
|
|
31
|
+
release_id:'v0.15.29-f74eaa1b3d00e6fb',
|
|
32
|
+
manifest_sha256:'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c',
|
|
33
|
+
generation_manifest_sha256:'289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a'
|
|
34
|
+
};
|
|
35
|
+
assert.deepEqual(release.runtime_predecessor,parent);
|
|
36
|
+
assert.equal(release.parent.version,'0.15.29');
|
|
37
|
+
assert.equal(release.parent.release_id,parent.release_id);
|
|
38
|
+
assert.equal(release.parent.manifest_sha256,parent.manifest_sha256);
|
|
39
|
+
assert.equal(release.parent.generation_manifest_sha256,parent.generation_manifest_sha256);
|
|
40
|
+
assert.equal(release.parent.tarball_sha256,'f535d7121a384bed106d34bc7525afa9d4048c7e290f270699e25136d1e82eeb');
|
|
41
|
+
assert.equal(release.source_predecessor.version,'0.15.29');
|
|
42
|
+
assert.equal(release.source_predecessor.tarball_sha256,release.parent.tarball_sha256);
|
|
43
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,release.parent.manifest_sha256);
|
|
44
|
+
assert.equal(release.source_predecessor.generation_manifest_sha256,release.parent.generation_manifest_sha256);
|
|
45
|
+
assert.equal(provenance.parent_release_id,parent.release_id);
|
|
46
|
+
assert.equal(provenance.parent_manifest_sha256,parent.manifest_sha256);
|
|
47
|
+
assert.equal(provenance.parent_generation_manifest_sha256,parent.generation_manifest_sha256);
|
|
48
|
+
assert.equal(provenance.parent_tarball_sha256,release.parent.tarball_sha256);
|
|
49
|
+
assert.deepEqual(provenance.runtime_predecessor,parent);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('R53 records stabilization evidence and explicit soak bypass without STABLE/FINAL claim',async()=>{
|
|
53
|
+
const [release,provenance,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
|
|
54
|
+
assert.equal(release.stabilization.semver_strict_validation,true);
|
|
55
|
+
assert.equal(release.stabilization.powershell_child_environment_normalized,true);
|
|
56
|
+
assert.equal(release.stabilization.explicit_state_root_propagation,true);
|
|
57
|
+
assert.equal(release.stabilization.guardian_cleanup_fail_closed,true);
|
|
58
|
+
assert.equal(release.stabilization.local_isolated_state_acceptance,'PASS');
|
|
59
|
+
assert.equal(release.stabilization.full_regression,'PASS');
|
|
60
|
+
assert.equal(release.stabilization.four_hour_soak,'NOT_COMPLETED_OPERATOR_BYPASS');
|
|
61
|
+
assert.equal(release.stabilization.stable_final_claim,false);
|
|
62
|
+
assert.equal(provenance.operator_override.four_hour_soak,'NOT_COMPLETED');
|
|
63
|
+
assert.equal(provenance.operator_override.stable_final_claim,false);
|
|
64
|
+
assert.equal(matrix.operator_override.four_hour_soak,'NOT_COMPLETED');
|
|
65
|
+
assert.equal(matrix.operator_override.stable_final_claim,false);
|
|
66
|
+
});
|
|
@@ -104,7 +104,8 @@ test('R27 active pointer is schema bound and atomically replaceable', async () =
|
|
|
104
104
|
test('R27 stable launcher resolves only the active pointer, not a release hardcode', () => {
|
|
105
105
|
const cmd=renderStableLauncherCmd();
|
|
106
106
|
const ps1=renderStableLauncherPs1();
|
|
107
|
-
assert.match(cmd
|
|
107
|
+
assert.match(cmd,/if not defined DEADBYTE_STATE_ROOT set "DEADBYTE_STATE_ROOT=%USERPROFILE%\\\.deadbyte-mcp"/i);
|
|
108
|
+
assert.match(cmd,/%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP\.ps1/i);
|
|
108
109
|
assert.doesNotMatch(cmd,/deadbyte-mcp-v0\.6\.0/i);
|
|
109
110
|
assert.match(ps1,/active-release\.json/);
|
|
110
111
|
assert.match(ps1,/DEADBYTE_CONTROLLER_CONFIG/);
|
|
@@ -6,6 +6,7 @@ import { mkdtemp, rm } from 'node:fs/promises';
|
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
9
10
|
|
|
10
11
|
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const repoRoot=path.resolve(here,'..');
|
|
@@ -13,8 +14,10 @@ const serverPath=path.join(repoRoot,'src','mcp-server.mjs');
|
|
|
13
14
|
|
|
14
15
|
test('R44 compact surface exposes one read-only skill_catalog without authority',async()=>{
|
|
15
16
|
const state=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r44-skill-mcp-'));
|
|
17
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(state,'trust','private.pem'),publicKeyPath:path.join(state,'trust','public.pem')});
|
|
16
18
|
const client=new Client({name:'deadbyte-r44-skill-mcp',version:'0.15.0-dev'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
17
19
|
const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:state,DEADBYTE_CAPABILITY_PROFILE:'compact',
|
|
20
|
+
DEADBYTE_SIGNING_KEY:keys.privateKeyPath,DEADBYTE_VERIFY_KEY:keys.publicKeyPath,
|
|
18
21
|
DEADBYTE_AUDIT_RUNTIME:'0',DEADBYTE_MACHINE_RUNTIME:'0',DEADBYTE_AGENT_RUNTIME:'0',
|
|
19
22
|
DEADBYTE_PROCESS_RUNTIME:'0',DEADBYTE_CODING_RUNTIME:'0',DEADBYTE_MACHINE_FS_RUNTIME:'0',
|
|
20
23
|
DEADBYTE_OBSERVATION_RUNTIME:'0',DEADBYTE_DESKTOP_RUNTIME:'0',DEADBYTE_AUTONOMOUS_RUNTIME:'0'};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdir,mkdtemp,writeFile,readFile} from 'node:fs/promises';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
test('failed guardian cleanup preserves ownership evidence and reports failure',async()=>{
|
|
8
|
+
const base='D:/Projects/deadbyte-mcp/testing/0.15.30';
|
|
9
|
+
await mkdir(base,{recursive:true});
|
|
10
|
+
const root=await mkdtemp(path.join(base,'guardian-failure-'));
|
|
11
|
+
await mkdir(path.join(root,'bin'),{recursive:true});
|
|
12
|
+
await mkdir(path.join(root,'runtime'),{recursive:true});
|
|
13
|
+
const observedRoot=path.join(root,'observed-root.txt');
|
|
14
|
+
await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`[IO.File]::WriteAllText('${observedRoot.replaceAll("'","''")}',[string]$env:DEADBYTE_STATE_ROOT)\nexit 1\n`);
|
|
15
|
+
const owner=spawnSync(process.execPath,['-e','process.exit(0)']);
|
|
16
|
+
assert.equal(owner.status,0);
|
|
17
|
+
const marker={schema:'deadbyte.remote-session.v2',owner_pid:owner.pid,session_nonce:'a'.repeat(64)};
|
|
18
|
+
const markerPath=path.join(root,'runtime','remote-session.json');
|
|
19
|
+
await writeFile(markerPath,JSON.stringify(marker));
|
|
20
|
+
const result=spawnSync(process.execPath,['src/remote-session-guardian.mjs','--state-root',root,'--owner-pid',String(owner.pid),'--session-nonce',marker.session_nonce],{encoding:'utf8',timeout:20000,env:{...process.env,DEADBYTE_STATE_ROOT:path.join(base,'different-guardian-parent')}});
|
|
21
|
+
assert.equal(result.error,undefined,String(result.error));
|
|
22
|
+
assert.notEqual(result.status,0,'cleanup failure must not report success');
|
|
23
|
+
assert.deepEqual(JSON.parse(await readFile(markerPath,'utf8')),marker);
|
|
24
|
+
assert.equal(await readFile(observedRoot,'utf8'),root);
|
|
25
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdir,mkdtemp} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {generateTrustKeypair} from '../src/trust.mjs';
|
|
6
|
+
import {verifyR43Parent} from '../scripts/verify-r43-parent.mjs';
|
|
7
|
+
|
|
8
|
+
test('historical parent verifier rejects a different explicit public key',async()=>{
|
|
9
|
+
const base='D:/Projects/deadbyte-mcp/testing/0.15.30';
|
|
10
|
+
await mkdir(base,{recursive:true});
|
|
11
|
+
const root=await mkdtemp(path.join(base,'historical-key-context-'));
|
|
12
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
13
|
+
const prior=process.env.DEADBYTE_PARENT_VERIFY_KEY;
|
|
14
|
+
try{
|
|
15
|
+
process.env.DEADBYTE_PARENT_VERIFY_KEY=keys.publicKeyPath;
|
|
16
|
+
await assert.rejects(verifyR43Parent(new URL('../R43-PARENT.json',import.meta.url)),/activation signer mismatch/);
|
|
17
|
+
}finally{if(prior===undefined)delete process.env.DEADBYTE_PARENT_VERIFY_KEY;else process.env.DEADBYTE_PARENT_VERIFY_KEY=prior;}
|
|
18
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
|
|
6
|
+
|
|
7
|
+
test('child normalization preserves custom module paths and unrelated environment',()=>{
|
|
8
|
+
const base={...process.env,PSModulePath:'D:\\custom-modules;D:\\other-modules',STABILIZATION_SENTINEL:'preserved'};
|
|
9
|
+
const normalized=windowsPowerShellEnvironment(base);
|
|
10
|
+
assert.ok(normalized.PSModulePath.split(';').includes('D:\\custom-modules'));
|
|
11
|
+
assert.ok(normalized.PSModulePath.split(';').includes('D:\\other-modules'));
|
|
12
|
+
assert.equal(normalized.STABILIZATION_SENTINEL,'preserved');
|
|
13
|
+
assert.equal(base.PSModulePath,'D:\\custom-modules;D:\\other-modules');
|
|
14
|
+
});
|
|
15
|
+
test('normalized Windows PowerShell resolves native Get-FileHash for inherited, missing and custom paths',()=>{
|
|
16
|
+
assert.equal(process.platform,'win32','this acceptance test requires Windows');
|
|
17
|
+
const executable=path.join(process.env.SystemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe');
|
|
18
|
+
for(const value of [process.env.PSModulePath,undefined,'D:\\custom-modules']){
|
|
19
|
+
const base={...process.env};
|
|
20
|
+
if(value===undefined) delete base.PSModulePath; else base.PSModulePath=value;
|
|
21
|
+
const result=spawnSync(executable,['-NoProfile','-Command',"$ErrorActionPreference='Stop'; $c=Get-Command Get-FileHash; $c.Name; $c.Module.Path"],{encoding:'utf8',timeout:20000,env:windowsPowerShellEnvironment(base)});
|
|
22
|
+
assert.equal(result.status,0,result.stderr);
|
|
23
|
+
assert.match(result.stdout,/Get-FileHash/);
|
|
24
|
+
assert.ok(result.stdout.toLowerCase().includes(path.join(process.env.SystemRoot,'System32','WindowsPowerShell','v1.0','Modules').toLowerCase()),result.stdout);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {compareRuntimeVersions as compare,ensureRuntimeCoherence} from '../src/runtime-coherence.mjs';
|
|
4
|
+
|
|
5
|
+
test('strict SemVer grammar',()=>{
|
|
6
|
+
for(const v of ['01.0.0','1.00.0','1.0.00','1.0.0-01','1.0.0-alpha..1','1.0.0+.','1.0.0-','1.0.0+','1.0.0\n']) assert.throws(()=>compare(v,v),/invalid/,v);
|
|
7
|
+
for(const v of ['1.0.0','1.0.0-0','1.0.0-alpha.1','1.0.0+001','1.0.0-x-y']) assert.equal(compare(v,v),0);
|
|
8
|
+
});
|
|
9
|
+
test('ASCII, arbitrary precision and SemVer precedence laws',()=>{
|
|
10
|
+
const ordered=['1.0.0-0','1.0.0-1','1.0.0-A','1.0.0-a','1.0.0-alpha','1.0.0-alpha.1','1.0.0-alpha.beta','1.0.0-beta','1.0.0-beta.2','1.0.0-beta.11','1.0.0-rc.1','1.0.0','1.0.1','2.0.0'];
|
|
11
|
+
for(let i=0;i<ordered.length;i++) for(let j=0;j<ordered.length;j++){
|
|
12
|
+
assert.equal(compare(ordered[i],ordered[j]),Math.sign(i-j));
|
|
13
|
+
for(let k=j;k<ordered.length;k++) if(i<=j) assert.ok(compare(ordered[i],ordered[k])<=0);
|
|
14
|
+
}
|
|
15
|
+
assert.equal(compare('9007199254740992.0.0','9007199254740993.0.0'),-1);
|
|
16
|
+
assert.equal(compare('1.0.0-9007199254740992','1.0.0-9007199254740993'),-1);
|
|
17
|
+
assert.equal(compare('1.0.0+a','1.0.0+b'),0);
|
|
18
|
+
});
|
|
19
|
+
test('ASCII downgrade never dispatches authority or activation',async()=>{
|
|
20
|
+
let calls=0;
|
|
21
|
+
await assert.rejects(ensureRuntimeCoherence({cliVersion:'1.0.0-A',packageManifestSha256:'b'.repeat(64),readActive:async()=>({version:'1.0.0-a',manifest_sha256:'a'.repeat(64)}),disarmPrivileged:async()=>{calls++;},activateCurrentPackage:async()=>{calls++;}}),/refusing automatic downgrade/);
|
|
22
|
+
assert.equal(calls,0);
|
|
23
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdir,mkdtemp,writeFile,readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {spawnSync} from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
test('MCP smoke preserves caller state root across SDK stdio transport',async()=>{
|
|
8
|
+
const base='D:/Projects/deadbyte-mcp/testing/0.15.30';
|
|
9
|
+
await mkdir(base,{recursive:true});
|
|
10
|
+
const root=await mkdtemp(path.join(base,'smoke-root-'));
|
|
11
|
+
const state=path.join(root,'isolated'),profile=path.join(root,'profile'),temp=path.join(root,'temp');
|
|
12
|
+
for(const dir of [path.join(state,'evidence','updates'),path.join(profile,'.deadbyte-mcp'),temp])await mkdir(dir,{recursive:true});
|
|
13
|
+
await writeFile(path.join(state,'active-release.json'),'INVALID_ISOLATED_POINTER');
|
|
14
|
+
const result=spawnSync(process.execPath,['src/mcp-smoke-client.mjs'],{encoding:'utf8',timeout:30000,env:{...process.env,DEADBYTE_STATE_ROOT:state,USERPROFILE:profile,TEMP:temp,TMP:temp}});
|
|
15
|
+
assert.notEqual(result.status,0,'smoke ignored corrupt explicitly-selected state');
|
|
16
|
+
assert.equal(result.error,undefined,String(result.error));
|
|
17
|
+
assert.match(result.stderr,/JSON|Unexpected token|connection closed/i);
|
|
18
|
+
});
|
|
19
|
+
test('release smoke child environments explicitly bind state root',async()=>{
|
|
20
|
+
for(const name of ['mcp-smoke-client','mcp-contained-smoke-client','process-smoke-client','machine-fs-smoke-client','authority-smoke','mcp-host-smoke-client']){
|
|
21
|
+
assert.match(await readFile(new URL('../src/'+name+'.mjs',import.meta.url),'utf8'),/DEADBYTE_STATE_ROOT\s*:/,name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,writeFile,readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {spawnSync} from 'node:child_process';
|
|
6
|
+
import {renderStableLauncherPs1,renderStableLauncherCmd} from '../src/release-slot.mjs';
|
|
7
|
+
import {windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
|
|
8
|
+
import {resolveStateRoot} from '../src/state-root.mjs';
|
|
9
|
+
|
|
10
|
+
const base='D:/Projects/deadbyte-mcp/testing/0.15.30';
|
|
11
|
+
test('trust default follows explicit environment in a fresh child',()=>{
|
|
12
|
+
const root=path.join(base,'trust-isolated');
|
|
13
|
+
const r=spawnSync(process.execPath,['--input-type=module','-e',"import {defaultTrustDir} from './src/trust.mjs'; console.log(defaultTrustDir());"],{encoding:'utf8',env:{...process.env,DEADBYTE_STATE_ROOT:root}});
|
|
14
|
+
assert.equal(r.status,0,r.stderr);
|
|
15
|
+
assert.equal(r.stdout.trim(),path.resolve(root,'trust'));
|
|
16
|
+
});
|
|
17
|
+
test('updater recovery reads the isolated pointer, not production',async()=>{
|
|
18
|
+
await mkdir(base,{recursive:true});
|
|
19
|
+
const root=await mkdtemp(path.join(base,'recovery-root-'));
|
|
20
|
+
await mkdir(path.join(root,'evidence','updates'),{recursive:true});
|
|
21
|
+
await writeFile(path.join(root,'active-release.json'),'INVALID_ISOLATED_POINTER');
|
|
22
|
+
const r=spawnSync(process.execPath,['scripts/runtime-self-update.mjs','--recover-interrupted'],{encoding:'utf8',env:{...process.env,DEADBYTE_STATE_ROOT:root},timeout:20000});
|
|
23
|
+
assert.notEqual(r.status,0);
|
|
24
|
+
assert.match(r.stderr,/JSON|Unexpected token/);
|
|
25
|
+
});
|
|
26
|
+
test('root precedence, default and invalid explicit roots',()=>{
|
|
27
|
+
assert.equal(resolveStateRoot('D:/explicit',{DEADBYTE_STATE_ROOT:'D:/environment'}),path.resolve('D:/explicit'));
|
|
28
|
+
assert.equal(resolveStateRoot(undefined,{DEADBYTE_STATE_ROOT:'D:/environment'}),path.resolve('D:/environment'));
|
|
29
|
+
assert.match(resolveStateRoot(undefined,{}),/\.deadbyte-mcp$/);
|
|
30
|
+
assert.throws(()=>resolveStateRoot(''),/invalid/);
|
|
31
|
+
assert.throws(()=>resolveStateRoot(null),/invalid/);
|
|
32
|
+
assert.throws(()=>resolveStateRoot('D:/bad\0'),/invalid/);
|
|
33
|
+
});
|
|
34
|
+
test('PS launcher selects isolated missing pointer rather than decoy production pointer',async()=>{
|
|
35
|
+
await mkdir(base,{recursive:true});
|
|
36
|
+
const fixture=await mkdtemp(path.join(base,'root-red-'));
|
|
37
|
+
const profile=path.join(fixture,'profile'),isolated=path.join(fixture,'isolated');
|
|
38
|
+
await mkdir(path.join(profile,'.deadbyte-mcp'),{recursive:true});
|
|
39
|
+
const decoy=path.join(profile,'.deadbyte-mcp','active-release.json');
|
|
40
|
+
await writeFile(decoy,'{"schema":"DECOY_PRODUCTION"}');
|
|
41
|
+
const launcher=path.join(fixture,'launcher.ps1');
|
|
42
|
+
await writeFile(launcher,renderStableLauncherPs1());
|
|
43
|
+
const r=spawnSync(path.join(process.env.SystemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),['-NoProfile','-File',launcher,'status'],{encoding:'utf8',timeout:20000,env:windowsPowerShellEnvironment({...process.env,USERPROFILE:profile,DEADBYTE_STATE_ROOT:isolated})});
|
|
44
|
+
assert.notEqual(r.status,0);
|
|
45
|
+
assert.match(r.stderr,/active release pointer missing/);
|
|
46
|
+
assert.doesNotMatch(r.stderr,/schema mismatch/);
|
|
47
|
+
assert.equal(await readFile(decoy,'utf8'),'{"schema":"DECOY_PRODUCTION"}');
|
|
48
|
+
});
|
|
49
|
+
test('CMD launcher transports explicit root with spaces to nested Node child',async()=>{
|
|
50
|
+
await mkdir(base,{recursive:true});
|
|
51
|
+
const fixture=await mkdtemp(path.join(base,'cmd-red-'));
|
|
52
|
+
const isolated=path.join(fixture,'isolated with spaces');
|
|
53
|
+
await mkdir(path.join(isolated,'bin'),{recursive:true});
|
|
54
|
+
await writeFile(path.join(isolated,'bin','DEADBYTE-MCP.ps1'),`& '${process.execPath.replaceAll("'","''")}' -e 'console.log(process.env.DEADBYTE_STATE_ROOT)'\nexit $LASTEXITCODE\n`);
|
|
55
|
+
const cmd=path.join(fixture,'launcher.cmd');
|
|
56
|
+
await writeFile(cmd,renderStableLauncherCmd());
|
|
57
|
+
const r=spawnSync(process.env.ComSpec,['/d','/c',cmd,'status'],{encoding:'utf8',timeout:20000,env:windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:isolated,USERPROFILE:fixture})});
|
|
58
|
+
assert.equal(r.status,0,r.stderr);
|
|
59
|
+
assert.equal(r.stdout.trim(),isolated);
|
|
60
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdir,mkdtemp,writeFile,readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {runControllerStartNoCapture} from '../src/runtime-update.mjs';
|
|
6
|
+
|
|
7
|
+
test('explicit updater context root overrides inherited root for controller child',async()=>{
|
|
8
|
+
const base='D:/Projects/deadbyte-mcp/testing/0.15.30';
|
|
9
|
+
await mkdir(base,{recursive:true});
|
|
10
|
+
const root=await mkdtemp(path.join(base,'explicit-child-root-'));
|
|
11
|
+
const output=path.join(root,'observed-root.txt'),controller=path.join(root,'controller.ps1');
|
|
12
|
+
await writeFile(controller,`[IO.File]::WriteAllText('${output.replaceAll("'","''")}',[string]$env:DEADBYTE_STATE_ROOT)\nexit 0\n`);
|
|
13
|
+
const old=process.env.DEADBYTE_STATE_ROOT;
|
|
14
|
+
try{
|
|
15
|
+
process.env.DEADBYTE_STATE_ROOT=path.join(base,'different-parent-root');
|
|
16
|
+
const result=runControllerStartNoCapture({kind:'slot',state_root:root,controller_path:controller,cwd:root,runtime_root:path.join(root,'runtime'),pointer:{config_path:path.join(root,'config.psd1')}},10000);
|
|
17
|
+
assert.equal(result.status,0);
|
|
18
|
+
assert.equal(await readFile(output,'utf8'),root);
|
|
19
|
+
}finally{if(old===undefined)delete process.env.DEADBYTE_STATE_ROOT;else process.env.DEADBYTE_STATE_ROOT=old;}
|
|
20
|
+
});
|