deadbyte-mcp 0.15.44 → 0.15.46
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 +86 -138
- package/PROVENANCE.json +35 -18
- package/README.txt +22 -21
- package/RELEASE.json +369 -334
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +27 -17
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +1871 -1769
- package/controller/deadbyte-desktop-policy.json +6 -6
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R68-COMPAT-HOST-EXEC-CANDIDATE.md +63 -0
- package/docs/R69-LIVE-OUTCOME-R70-POLICY-TRANSFER.md +136 -0
- package/docs/R70-CODING-DIGEST-SUPERVISOR-CHECKPOINT-20261013.md +42 -0
- package/docs/R70-FINITE-GRANT-OWNER-BOUNDARY-20261013.md +80 -0
- package/docs/R70-OWNER-BOOTSTRAP-CHECKPOINT-20261013.md +60 -0
- package/docs/R70-REFERENCE-MECHANISMS.json +95 -0
- package/docs/R70-TRUST-GUARD-EXPIRY-CHECKPOINT-20261013.md +47 -0
- package/package.json +2 -2
- package/scripts/benchmark-l0-r71.mjs +76 -0
- package/scripts/gate-windows-r71.ps1 +64 -0
- package/scripts/gate-windows-r72.ps1 +76 -0
- package/scripts/npm-release-r48.mjs +4 -0
- package/scripts/npm-release-readiness-r71.mjs +21 -0
- package/scripts/npm-release-readiness-r72.mjs +76 -0
- package/scripts/verify-r70-full-regression.ps1 +72 -0
- package/src/authority-continuity-r65.mjs +28 -8
- package/src/autonomous-mcp-tools.mjs +1 -1
- package/src/autonomous-output-contracts.mjs +4 -1
- package/src/autonomous-policy.mjs +54 -7
- package/src/capability-ledger.mjs +1 -1
- package/src/capability-resolver.mjs +2 -1
- package/src/commander-compat-runtime.mjs +30 -2
- package/src/deadbyte-cli.mjs +722 -719
- package/src/launcher-isolation.mjs +58 -0
- package/src/native-diff-v2.mjs +18 -5
- package/src/native-diff.mjs +21 -5
- package/src/r70-owner-approval.mjs +78 -0
- package/src/remote-authority.mjs +75 -75
- package/src/remote-live-log.mjs +48 -14
- package/src/remote-mcp-machine-smoke-client.mjs +84 -84
- package/src/remote-result-journal.mjs +1 -1
- package/src/runtime-update.mjs +232 -24
- package/src/staging-input-isolation.mjs +79 -0
- package/src/tool-audit.mjs +2 -0
- package/src/version.mjs +1 -1
- package/test/autonomous-local-supervisor.test.mjs +2 -1
- package/test/autonomous-output-contract.test.mjs +17 -1
- package/test/autonomous-supervisor-integration.test.mjs +2 -1
- package/test/capability-resolver.test.mjs +17 -0
- package/test/codex-autonomous-r44.test.mjs +1 -1
- package/test/commander-compat-runtime.test.mjs +42 -0
- package/test/controller-policy-preflight-r59.test.mjs +1 -0
- package/test/deferred-slot-operation-behavior.test.mjs +2 -2
- package/test/native-diff-topology-r36.test.mjs +35 -2
- package/test/native-diff.test.mjs +83 -3
- package/test/r58-cleanup-retention.test.mjs +3 -3
- package/test/r58-concurrent-recovery.test.mjs +3 -3
- package/test/r58-stale-owner.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +9 -5
- package/test/r60-authority-coherence.test.mjs +10 -2
- package/test/r65-authority-runtime-epoch.test.mjs +9 -1
- package/test/r67-isolated-slot-rollback-restore.test.mjs +2 -1
- package/test/r67-npm-publish-gate.test.mjs +4 -2
- package/test/r67-release-contract.test.mjs +8 -7
- package/test/r69-live-outcome.test.mjs +137 -0
- package/test/r70-autonomous-grant-expiry.test.mjs +89 -0
- package/test/r70-autonomous-policy-bootstrap.test.mjs +166 -0
- package/test/r70-controller-autonomous-root.test.mjs +104 -0
- package/test/r70-controller-grant-subset.test.mjs +28 -0
- package/test/r70-controller-owner-issue-boundary.test.mjs +80 -0
- package/test/r70-controller-policy-snapshot.test.mjs +66 -0
- package/test/r70-owner-approval.test.mjs +67 -0
- package/test/r71-full-verifier-artifact.test.mjs +28 -0
- package/test/r71-isolated-slot-rollback-restore.test.mjs +62 -0
- package/test/r71-native-diff-mcp-wire.test.mjs +75 -0
- package/test/r71-publish-fence.test.mjs +38 -0
- package/test/r71-release-identity.test.mjs +33 -0
- package/test/r72-launcher-isolation.test.mjs +99 -0
- package/test/r72-publication-fence.test.mjs +56 -0
- package/test/r72-publication-output-integrity.test.mjs +110 -0
- package/test/r72-remote-safe-contract.test.mjs +101 -0
- package/test/r72-staging-input-isolation.test.mjs +87 -0
- package/test/r72-staging-workspace-isolation.test.mjs +114 -0
- package/test/remote-result-journal.test.mjs +12 -0
- package/test/remote-session-cli.test.mjs +112 -65
- package/test/standing-grants.test.mjs +2 -2
- package/test/tool-audit.test.mjs +14 -0
- 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-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-native-inspect-r67.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/conpty-input-observer.obj +0 -0
- package/miniconpty.obj +0 -0
- package/proof/CONTAINMENT-BUILD.txt +0 -163
- package/proof/MCP-SMOKE-STATUS.txt +0 -9
- package/proof/NODE-SYNTAX.txt +0 -4
- package/proof/PROOF.txt +0 -6
- package/proof/R34-TASK8-SUPERVISOR-CHECKPOINT.json +0 -29
- package/proof/R34-TASK9-MCP-SURFACE-CHECKPOINT.json +0 -28
- package/proof/SECRET-SCAN.txt +0 -4
- package/proof/SOURCE-GATES.txt +0 -16
- package/proof/STATUS.txt +0 -11
- package/proof/TARGET-V0313-CONTAINMENT-PASS.sha256 +0 -1
- package/proof/TARGET-V0313-CONTAINMENT-PASS.txt +0 -215
- package/proof/TARGET-V040-CONTAINED-IPC-FAIL.sha256 +0 -1
- package/proof/TARGET-V040-CONTAINED-IPC-FAIL.txt +0 -299
- package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.txt +0 -311
- package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.txt +0 -313
- package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.txt +0 -308
- package/proof/TARGET-V044-CONTAINED-PASS.sha256 +0 -1
- package/proof/TARGET-V044-CONTAINED-PASS.txt +0 -432
- package/proof/TARGET-V045-DUAL-OPERATION-PASS.sha256 +0 -1
- package/proof/TARGET-V045-DUAL-OPERATION-PASS.txt +0 -481
- package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.sha256 +0 -1
- package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.txt +0 -610
- package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.sha256 +0 -1
- package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.txt +0 -642
- package/proof/TARGET-V050-BATCH-GATE-PASS.sha256 +0 -1
- package/proof/TARGET-V050-BATCH-GATE-PASS.txt +0 -713
- package/proof/TESTS.txt +0 -686
- package/proof/TRUST-EPHEMERAL.txt +0 -4
- package/proof/V03-FREEZE.txt +0 -27
- package/proof/V041-IPC-HOTFIX.txt +0 -16
- package/proof/V042-ENV-HOTFIX.txt +0 -15
- package/proof/V043-DRIVE-CWD-HOTFIX.txt +0 -14
- package/proof/V044-LOCALAPPDATA-HOTFIX.txt +0 -18
- package/proof/V045-OPERATION-REGISTRY.txt +0 -7
- package/proof/V046-WORKER-REGISTRY.txt +0 -12
- package/proof/V047-WORKER-MANIFEST.txt +0 -18
- package/proof/V050-BATCH-TRUST-CLOSURE.txt +0 -21
- package/proof/V060-ACTIVE-NETWORK-AUTHORITY.txt +0 -29
- package/proof/V070-NODE-SYNTAX.txt +0 -36
- package/proof/V070-NODE-TESTS.txt +0 -752
- package/proof/V070-PACK-DRY-RUN.txt +0 -126
- package/proof/V070-SOURCE-GATE.txt +0 -30
- package/proof/V070-STATIC-SCAN.txt +0 -4
- package/proof/V070-WINDOWS-GATE-PENDING.txt +0 -25
- package/proof/V071-NODE-SYNTAX.txt +0 -1
- package/proof/V071-NODE-TESTS.txt +0 -796
- package/proof/V071-PACK-DRY-RUN.json +0 -656
- package/proof/V071-SOURCE-GATE.txt +0 -16
- package/proof/V071-WINDOWS-GATE-PENDING.txt +0 -24
- package/proof/V073-NODE-SYNTAX.txt +0 -4
- package/proof/V073-SOURCE-GATE.txt +0 -21
- package/proof/V073-STATIC-SCAN.txt +0 -10
- package/proof/V073-WINDOWS-GATE-PENDING.txt +0 -5
- package/proof/VERIFY.txt +0 -7
- package/proof/WINDOWS-CONTAINED-STATUS.txt +0 -5
- package/proof/WINDOWS-CONTAINMENT-STATUS.txt +0 -3
- package/proof/commander-closure-20260927/CONTINUATION.json +0 -43
- package/proof/commander-closure-20260927/RESULT.json +0 -30
- package/proof/commander-closure-20260927/SEARCH-CONTINUATION.json +0 -30
- package/proof/commander-closure-20260927/XLS-REFERENCE-PROBE.json +0 -13
- package/proof/commander-closure-20260928/DIFFERENTIAL-CHECKPOINT.json +0 -122
- package/proof/r59-full-regression-current.json +0 -13
- package/proof/r59-full-regression-current.stderr.log +0 -0
- package/proof/r59-full-regression-current.stdout.log +0 -1268
package/src/runtime-update.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, createPrivateKey, createPublicKey, randomBytes, sign as cryptoSign, verify as cryptoVerify } from 'node:crypto';
|
|
2
2
|
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
3
|
+
import { existsSync, lstatSync, realpathSync } from 'node:fs';
|
|
4
4
|
import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm, writeFile } from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
@@ -18,6 +18,9 @@ import {
|
|
|
18
18
|
} from './release-slot.mjs';
|
|
19
19
|
import { createGenerationBundle, verifyGenerationBundle } from './release-generation.mjs';
|
|
20
20
|
import { resolveStateRoot } from './state-root.mjs';
|
|
21
|
+
import {canonicalLauncherPaths,assertCanonicalLaunchers,renderStateBoundLauncherCmd} from './launcher-isolation.mjs';
|
|
22
|
+
import {assertStageOwnedPath,assertStagingBootstrapInputs,assertStagingControlConfig} from './staging-input-isolation.mjs';
|
|
23
|
+
import { verifyProjectOwnerApproval } from './r70-owner-approval.mjs';
|
|
21
24
|
|
|
22
25
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
23
26
|
const SEMVER=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -28,7 +31,6 @@ const FAILURE_SCHEMA='deadbyte.r27-slot-update-failure.v1';
|
|
|
28
31
|
const ROLLBACK_SCHEMA='deadbyte.r31-slot-rollback-receipt.v1';
|
|
29
32
|
const ROLLBACK_FAILURE_SCHEMA='deadbyte.r31-slot-rollback-failure.v1';
|
|
30
33
|
const DEFAULT_STATE_ROOT=undefined;
|
|
31
|
-
const TOP_LAUNCHER=path.join(os.homedir(),'Downloads','DEADBYTE-MCP.cmd');
|
|
32
34
|
const RUNTIME_STABILITY_MS=12000;
|
|
33
35
|
const RUNTIME_STABILITY_POLL_MS=500;
|
|
34
36
|
|
|
@@ -48,8 +50,8 @@ function normalizePersistentPublicOrigin(value){
|
|
|
48
50
|
return publicOrigin.origin;
|
|
49
51
|
}
|
|
50
52
|
export function parseUpdateArgs(argv){
|
|
51
|
-
const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,hostPolicyBootstrap:null,codingPolicyBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
|
|
52
|
-
const cloud={},bridge={},processPolicy={},hostPolicy={},codingPolicy={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
|
|
53
|
+
const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,hostPolicyBootstrap:null,codingPolicyBootstrap:null,autonomousPolicyBootstrap:null,ownerApprovalBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
|
|
54
|
+
const cloud={},bridge={},processPolicy={},hostPolicy={},codingPolicy={},autonomousPolicy={},ownerApproval={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
|
|
53
55
|
const bridgeFlags=new Map([
|
|
54
56
|
['--bridge-root','root'],['--bridge-script-sha256','scriptSha256']
|
|
55
57
|
]);
|
|
@@ -62,6 +64,12 @@ export function parseUpdateArgs(argv){
|
|
|
62
64
|
const codingPolicyFlags=new Map([
|
|
63
65
|
['--coding-policy','path'],['--coding-policy-sha256','sha256']
|
|
64
66
|
]);
|
|
67
|
+
const autonomousPolicyFlags=new Map([
|
|
68
|
+
['--autonomous-policy','path'],['--autonomous-policy-sha256','sha256']
|
|
69
|
+
]);
|
|
70
|
+
const ownerApprovalFlags=new Map([
|
|
71
|
+
['--owner-approval','path'],['--owner-approval-sha256','sha256']
|
|
72
|
+
]);
|
|
65
73
|
const desktopPolicyFlags=new Map([
|
|
66
74
|
['--desktop-policy','path'],['--desktop-policy-sha256','sha256']
|
|
67
75
|
]);
|
|
@@ -73,7 +81,7 @@ export function parseUpdateArgs(argv){
|
|
|
73
81
|
['--cloud-token-file','tokenFilePath'],['--cloud-token-sha256','tokenFileSha256'],
|
|
74
82
|
['--cloud-child-working-directory','childWorkingDirectory'],['--cloud-public-origin','publicOrigin']
|
|
75
83
|
]);
|
|
76
|
-
const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...codingPolicyFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
|
|
84
|
+
const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...codingPolicyFlags.keys(),...autonomousPolicyFlags.keys(),...ownerApprovalFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
|
|
77
85
|
for(let index=0;index<argv.length;index++){
|
|
78
86
|
const arg=argv[index];
|
|
79
87
|
if(arg==='--restore-standing-grants'||arg==='--recover-offline-predecessor'||arg==='--enable-machine-fs'||arg==='--enable-observation'||arg==='--enable-desktop'||arg==='--enable-autonomous-supervisor'){
|
|
@@ -110,6 +118,8 @@ export function parseUpdateArgs(argv){
|
|
|
110
118
|
else if(processPolicyFlags.has(arg)) processPolicy[processPolicyFlags.get(arg)]=value;
|
|
111
119
|
else if(hostPolicyFlags.has(arg)) hostPolicy[hostPolicyFlags.get(arg)]=value;
|
|
112
120
|
else if(codingPolicyFlags.has(arg)) codingPolicy[codingPolicyFlags.get(arg)]=value;
|
|
121
|
+
else if(autonomousPolicyFlags.has(arg)) autonomousPolicy[autonomousPolicyFlags.get(arg)]=value;
|
|
122
|
+
else if(ownerApprovalFlags.has(arg)) ownerApproval[ownerApprovalFlags.get(arg)]=value;
|
|
113
123
|
else if(desktopPolicyFlags.has(arg)) desktopPolicy[desktopPolicyFlags.get(arg)]=value;
|
|
114
124
|
else if(semanticLspFlags.has(arg)) semanticLsp[semanticLspFlags.get(arg)]=value;
|
|
115
125
|
else cloud[cloudFlags.get(arg)]=value;
|
|
@@ -146,6 +156,21 @@ export function parseUpdateArgs(argv){
|
|
|
146
156
|
if(!HEX64.test(sha256)) throw new Error('coding policy SHA-256 must be lowercase hex');
|
|
147
157
|
out.codingPolicyBootstrap={path:path.resolve(codingPolicy.path),sha256};
|
|
148
158
|
}
|
|
159
|
+
const autonomousPolicyFields=[...autonomousPolicyFlags.values()],autonomousPolicyPresent=autonomousPolicyFields.filter(key=>autonomousPolicy[key]!==undefined);
|
|
160
|
+
if(autonomousPolicyPresent.length!==0&&autonomousPolicyPresent.length!==autonomousPolicyFields.length) throw new Error('autonomous policy bootstrap requires path plus SHA-256 as one pair');
|
|
161
|
+
if(autonomousPolicyPresent.length===autonomousPolicyFields.length){
|
|
162
|
+
const sha256=String(autonomousPolicy.sha256).toLowerCase();
|
|
163
|
+
if(!HEX64.test(sha256)) throw new Error('autonomous policy SHA-256 must be lowercase hex');
|
|
164
|
+
out.autonomousPolicyBootstrap={path:path.resolve(autonomousPolicy.path),sha256};
|
|
165
|
+
}
|
|
166
|
+
const ownerApprovalFields=[...ownerApprovalFlags.values()],ownerApprovalPresent=ownerApprovalFields.filter(key=>ownerApproval[key]!==undefined);
|
|
167
|
+
if(ownerApprovalPresent.length!==0&&ownerApprovalPresent.length!==ownerApprovalFields.length) throw new Error('owner approval bootstrap requires path plus SHA-256 as one pair');
|
|
168
|
+
if(ownerApprovalPresent.length===ownerApprovalFields.length){
|
|
169
|
+
const sha256=String(ownerApproval.sha256).toLowerCase();
|
|
170
|
+
if(!HEX64.test(sha256)) throw new Error('owner approval SHA-256 must be lowercase hex');
|
|
171
|
+
out.ownerApprovalBootstrap={path:path.resolve(ownerApproval.path),sha256};
|
|
172
|
+
}
|
|
173
|
+
if(Boolean(out.autonomousPolicyBootstrap)!==Boolean(out.ownerApprovalBootstrap)) throw new Error('autonomous policy bootstrap requires pinned owner approval, and owner approval requires autonomous policy');
|
|
149
174
|
const desktopPolicyFields=[...desktopPolicyFlags.values()],desktopPolicyPresent=desktopPolicyFields.filter(key=>desktopPolicy[key]!==undefined);
|
|
150
175
|
if(desktopPolicyPresent.length!==0&&desktopPolicyPresent.length!==desktopPolicyFields.length) throw new Error('desktop policy bootstrap requires path plus SHA-256 as one pair');
|
|
151
176
|
if(desktopPolicyPresent.length===desktopPolicyFields.length){
|
|
@@ -446,6 +471,21 @@ async function verifyPinnedRegularFile(file,expected,label){
|
|
|
446
471
|
return {path:absolute,sha256:actual};
|
|
447
472
|
}
|
|
448
473
|
|
|
474
|
+
export async function readPinnedJsonPolicy(bootstrap,label,readBytes=readFile){
|
|
475
|
+
const file=bootstrap?.path,expected=bootstrap?.sha256;
|
|
476
|
+
if(typeof file!=='string'||!path.isAbsolute(file)||!HEX64.test(String(expected??''))) throw new Error(`${label} pin contract invalid`);
|
|
477
|
+
const absolute=path.resolve(file),stat=await lstat(absolute);
|
|
478
|
+
if(!stat.isFile()||stat.isSymbolicLink()) throw new Error(`${label} must be a regular file`);
|
|
479
|
+
const bytes=await readBytes(absolute);
|
|
480
|
+
if(!Buffer.isBuffer(bytes)) throw new Error(`${label} read must return bytes`);
|
|
481
|
+
const actual=sha256Bytes(bytes);
|
|
482
|
+
if(actual!==expected) throw new Error(`${label} SHA-256 mismatch: expected ${expected}, found ${actual}`);
|
|
483
|
+
let policy;
|
|
484
|
+
try{policy=JSON.parse(bytes.toString('utf8'));}
|
|
485
|
+
catch{throw new Error(`${label} JSON invalid`);}
|
|
486
|
+
return {path:absolute,sha256:actual,policy};
|
|
487
|
+
}
|
|
488
|
+
|
|
449
489
|
export async function prepareProcessPolicyBootstrap(bootstrap){
|
|
450
490
|
if(!bootstrap) return null;
|
|
451
491
|
const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'process policy');
|
|
@@ -470,8 +510,7 @@ export async function prepareHostPolicyBootstrap(bootstrap){
|
|
|
470
510
|
|
|
471
511
|
export async function prepareCodingPolicyBootstrap(bootstrap){
|
|
472
512
|
if(!bootstrap) return null;
|
|
473
|
-
const pinned=await
|
|
474
|
-
const policy=await readJson(pinned.path);
|
|
513
|
+
const {policy,...pinned}=await readPinnedJsonPolicy(bootstrap,'coding policy');
|
|
475
514
|
if(policy?.schema!=='deadbyte.coding-policy.v1'||policy?.enabled!==true||
|
|
476
515
|
!policy?.roots||typeof policy.roots!=='object'||Array.isArray(policy.roots)||
|
|
477
516
|
!policy?.commands||typeof policy.commands!=='object'||Array.isArray(policy.commands)) {
|
|
@@ -480,6 +519,110 @@ export async function prepareCodingPolicyBootstrap(bootstrap){
|
|
|
480
519
|
return {...pinned,policy};
|
|
481
520
|
}
|
|
482
521
|
|
|
522
|
+
export async function prepareAutonomousPolicyBootstrap(bootstrap){
|
|
523
|
+
if(!bootstrap) return null;
|
|
524
|
+
const {policy,...pinned}=await readPinnedJsonPolicy(bootstrap,'autonomous policy');
|
|
525
|
+
if(policy?.schema!=='deadbyte.autonomous-policy.v1'||policy?.enabled!==true||
|
|
526
|
+
!HEX64.test(String(policy.coding_policy_sha256??''))||
|
|
527
|
+
!Array.isArray(policy.root_ids)||policy.root_ids.length<1||
|
|
528
|
+
!policy.profiles||typeof policy.profiles!=='object'||Array.isArray(policy.profiles)) {
|
|
529
|
+
throw new Error('autonomous policy schema/enabled/coding digest/roots/profiles mismatch');
|
|
530
|
+
}
|
|
531
|
+
return {...pinned,policy};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function assertAutonomousBootstrapRoots(autonomousPolicy,codingPolicy,selectedCodingSha256=null){
|
|
535
|
+
const roots=autonomousPolicy?.root_ids;
|
|
536
|
+
for(const codingRoot of Object.values(codingPolicy?.roots??{})){
|
|
537
|
+
const candidate=codingRoot?.path;
|
|
538
|
+
if((codingRoot?.read===true||codingRoot?.write===true||codingRoot?.exec===true)&&
|
|
539
|
+
typeof candidate==='string'&&path.isAbsolute(candidate)){
|
|
540
|
+
const resolved=path.resolve(candidate),filesystemRoot=path.parse(resolved).root;
|
|
541
|
+
const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
|
|
542
|
+
if(normalize(resolved)===normalize(filesystemRoot)) throw new Error('coding policy contains a broad filesystem root');
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if(!Array.isArray(roots)||roots.length<1||roots.some(id=>typeof id!=='string'||
|
|
546
|
+
!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!Object.hasOwn(codingPolicy?.roots??{},id))) {
|
|
547
|
+
throw new Error('autonomous bootstrap root is outside selected coding policy');
|
|
548
|
+
}
|
|
549
|
+
const profiles=autonomousPolicy?.profiles??{};
|
|
550
|
+
if(!profiles||typeof profiles!=='object'||Array.isArray(profiles)) throw new Error('autonomous bootstrap profiles invalid');
|
|
551
|
+
for(const [id,profile] of Object.entries(profiles)){
|
|
552
|
+
if(typeof profile?.root_id!=='string'||!roots.includes(profile.root_id)){
|
|
553
|
+
throw new Error(`autonomous bootstrap profile '${id}' root outside autonomous roots`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if(selectedCodingSha256!==null&&autonomousPolicy.coding_policy_sha256!==selectedCodingSha256) {
|
|
557
|
+
throw new Error('autonomous bootstrap coding policy SHA-256 mismatch');
|
|
558
|
+
}
|
|
559
|
+
for(const id of roots){
|
|
560
|
+
const rootPath=codingPolicy.roots[id]?.path;
|
|
561
|
+
if(typeof rootPath==='string'&&path.isAbsolute(rootPath)){
|
|
562
|
+
const resolved=path.resolve(rootPath),filesystemRoot=path.parse(resolved).root;
|
|
563
|
+
const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
|
|
564
|
+
if(normalize(resolved)===normalize(filesystemRoot)) throw new Error('autonomous bootstrap requires a project-scoped root');
|
|
565
|
+
}
|
|
566
|
+
try{
|
|
567
|
+
if(typeof rootPath!=='string'||!path.isAbsolute(rootPath)) throw new Error('path invalid');
|
|
568
|
+
const stat=lstatSync(rootPath);
|
|
569
|
+
if(!stat.isDirectory()||stat.isSymbolicLink()) throw new Error('not a regular directory');
|
|
570
|
+
const resolved=path.resolve(rootPath),real=realpathSync(rootPath);
|
|
571
|
+
const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
|
|
572
|
+
if(normalize(resolved)!==normalize(path.resolve(real))) throw new Error('path indirection');
|
|
573
|
+
}catch{ throw new Error(`autonomous bootstrap root '${id}' must be an existing real directory`); }
|
|
574
|
+
}
|
|
575
|
+
return roots;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// R70: Admission check for successor POLICY BOOTSTRAP only. This does not
|
|
579
|
+
// grant authority or establish the owner trust anchor by itself.
|
|
580
|
+
export async function verifyAutonomousOwnerBootstrap({
|
|
581
|
+
stateRoot,bootstrap,autonomousPolicy,codingPolicy,
|
|
582
|
+
codingPolicySha256,autonomousPolicySha256,expectedManifest,now=new Date()
|
|
583
|
+
}={}){
|
|
584
|
+
if(!bootstrap) throw new Error('owner approval bootstrap is required');
|
|
585
|
+
if(typeof stateRoot!=='string'||!path.isAbsolute(stateRoot)) throw new Error('owner approval state root invalid');
|
|
586
|
+
if(!HEX64.test(String(codingPolicySha256??''))||
|
|
587
|
+
!HEX64.test(String(autonomousPolicySha256??''))||
|
|
588
|
+
!HEX64.test(String(expectedManifest??''))) throw new Error('owner approval digest inputs invalid');
|
|
589
|
+
const ids=assertAutonomousBootstrapRoots(autonomousPolicy,codingPolicy,codingPolicySha256);
|
|
590
|
+
if(ids.length!==1) throw new Error('owner approval requires exactly one project-scoped autonomous root');
|
|
591
|
+
const rootId=ids[0];
|
|
592
|
+
const projectRoot=codingPolicy.roots[rootId].path;
|
|
593
|
+
const trustRoot=path.resolve(stateRoot,'trust');
|
|
594
|
+
const ownerKeyPath=path.join(trustRoot,'r70-owner-public.pem');
|
|
595
|
+
const normalize=p=>process.platform==='win32'?path.resolve(p).toLowerCase():path.resolve(p);
|
|
596
|
+
const under=(file,root)=>{
|
|
597
|
+
const relative=path.relative(normalize(root),normalize(file));
|
|
598
|
+
return relative===''||(!relative.startsWith('..'+path.sep)&&relative!=='..'&&!path.isAbsolute(relative));
|
|
599
|
+
};
|
|
600
|
+
// Coding commands execute as the owner OS principal, not inside a
|
|
601
|
+
// filesystem sandbox. Project-scoped write checks cannot protect a trust
|
|
602
|
+
// anchor while any selected Coding root can execute arbitrary scripts.
|
|
603
|
+
for(const root of Object.values(codingPolicy.roots)){
|
|
604
|
+
if(root?.exec===true) throw new Error('owner approval cannot trust an anchor with unsandboxed Coding execution');
|
|
605
|
+
if(root?.write===true&&typeof root.path==='string'&&path.isAbsolute(root.path)&&
|
|
606
|
+
under(ownerKeyPath,root.path)) throw new Error('owner trust key is writable by Coding policy');
|
|
607
|
+
}
|
|
608
|
+
const trustStat=await lstat(trustRoot);
|
|
609
|
+
const keyStat=await lstat(ownerKeyPath);
|
|
610
|
+
if(!trustStat.isDirectory()||trustStat.isSymbolicLink()||
|
|
611
|
+
!keyStat.isFile()||keyStat.isSymbolicLink()||
|
|
612
|
+
normalize(await realpath(trustRoot))!==normalize(trustRoot)||
|
|
613
|
+
normalize(await realpath(ownerKeyPath))!==normalize(ownerKeyPath)){
|
|
614
|
+
throw new Error('owner trust key must be a regular file below a real trust directory');
|
|
615
|
+
}
|
|
616
|
+
const ownerPublicKey=await readFile(ownerKeyPath);
|
|
617
|
+
const pinned=await readPinnedJsonPolicy(bootstrap,'owner approval');
|
|
618
|
+
return verifyProjectOwnerApproval({
|
|
619
|
+
approval:pinned.policy,ownerPublicKey,
|
|
620
|
+
expected:{rootId,projectRoot,codingPolicySha256,autonomousPolicySha256,
|
|
621
|
+
reviewedArtifactSha256:expectedManifest},
|
|
622
|
+
now
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
483
626
|
export async function prepareDesktopPolicyBootstrap(bootstrap){
|
|
484
627
|
if(!bootstrap) return null;
|
|
485
628
|
const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'desktop policy');
|
|
@@ -1000,7 +1143,9 @@ async function loadControlInputs({stateRoot,sourceRoot,pointerInfo,bridgeBootstr
|
|
|
1000
1143
|
if(!sourceRoot) throw new Error('--source-root is required for initial slot migration');
|
|
1001
1144
|
ctx=legacyContext(sourceRoot); configPath=path.join(ctx.source_root,'controller','deadbyte-controller.config.psd1');
|
|
1002
1145
|
}
|
|
1146
|
+
assertStageOwnedPath(configPath,stateRoot,'controller config file');
|
|
1003
1147
|
let config=await loadPowerShellDataFile(configPath);
|
|
1148
|
+
assertStagingControlConfig(config,stateRoot,{configPath});
|
|
1004
1149
|
if(bridgeBootstrap){
|
|
1005
1150
|
if(!bridgeBootstrap.root||!path.isAbsolute(bridgeBootstrap.root)||!HEX64.test(String(bridgeBootstrap.scriptSha256??''))) throw new Error('bridge bootstrap requires absolute root plus lowercase script SHA-256');
|
|
1006
1151
|
const bootstrapRoot=path.resolve(bridgeBootstrap.root);
|
|
@@ -1030,7 +1175,33 @@ export function assertDevelopmentRootSafe(developmentRoot,stateRoot){
|
|
|
1030
1175
|
if(forbidden.some(prefix=>lower===prefix||lower.startsWith(`${prefix}${path.sep}`))) throw new Error('development root cannot live inside immutable/runtime state stores');
|
|
1031
1176
|
return dev;
|
|
1032
1177
|
}
|
|
1033
|
-
|
|
1178
|
+
function assertStageWorkspaceBoundary(currentWorkspace,stateRoot){
|
|
1179
|
+
const state=path.resolve(stateRoot),source=path.resolve(currentWorkspace);
|
|
1180
|
+
const production=path.resolve(os.homedir(),'.deadbyte-mcp');
|
|
1181
|
+
const normalized=file=>process.platform==='win32'?file.toLowerCase():file;
|
|
1182
|
+
if(normalized(state)===normalized(production)) return;
|
|
1183
|
+
// Fail closed before staging can plan, mutate or roll back a workspace migration.
|
|
1184
|
+
canonicalLauncherPaths(state);
|
|
1185
|
+
const within=(parent,child)=>{
|
|
1186
|
+
const relative=path.relative(normalized(parent),normalized(child));
|
|
1187
|
+
return relative===''||(!path.isAbsolute(relative)&&relative!=='..'&&!relative.startsWith('..'+path.sep));
|
|
1188
|
+
};
|
|
1189
|
+
if(!within(state,source)) throw new Error('STAGING_WORKSPACE_OUTSIDE_STATE');
|
|
1190
|
+
if(normalized(state)===normalized(source)) throw new Error('STAGING_WORKSPACE_STATE_ROOT');
|
|
1191
|
+
const forbidden=['releases','generations','runtime','bin','launchers','trust','evidence','controller'];
|
|
1192
|
+
if(forbidden.some(child=>within(path.join(state,child),source))) throw new Error('STAGING_WORKSPACE_RESERVED_STATE');
|
|
1193
|
+
// Reject reparse points on either path, even when final leaf is absent.
|
|
1194
|
+
for(const file of [source,path.join(state,'workspaces')]){
|
|
1195
|
+
let cursor=file;
|
|
1196
|
+
for(;;){
|
|
1197
|
+
try {if(lstatSync(cursor).isSymbolicLink())throw new Error('STAGING_WORKSPACE_REPARSE');}
|
|
1198
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
1199
|
+
const parent=path.dirname(cursor);if(parent===cursor)break;cursor=parent;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
export async function workspacePlan(currentWorkspace,stateRoot){
|
|
1204
|
+
assertStageWorkspaceBoundary(currentWorkspace,stateRoot);
|
|
1034
1205
|
const source=path.resolve(currentWorkspace),target=path.join(path.resolve(stateRoot),'workspaces');
|
|
1035
1206
|
if(source.toLowerCase()===path.resolve(target).toLowerCase()) return {source,target,mode:'existing',moved:false};
|
|
1036
1207
|
const sourceExists=existsSync(source),targetExists=existsSync(target);
|
|
@@ -1044,7 +1215,8 @@ async function workspacePlan(currentWorkspace,stateRoot){
|
|
|
1044
1215
|
if(path.parse(source).root.toLowerCase()!==path.parse(target).root.toLowerCase()) throw new Error('workspace migration requires same-volume rename');
|
|
1045
1216
|
return {source,target,mode:'rename',moved:false};
|
|
1046
1217
|
}
|
|
1047
|
-
async function applyWorkspacePlan(plan){
|
|
1218
|
+
export async function applyWorkspacePlan(plan){
|
|
1219
|
+
assertStageWorkspaceBoundary(plan.source,path.dirname(plan.target));
|
|
1048
1220
|
if(plan.mode==='existing'||plan.mode==='target-existing') return plan;
|
|
1049
1221
|
const sourceExists=existsSync(plan.source),targetExists=existsSync(plan.target);
|
|
1050
1222
|
if(plan.mode==='create'){
|
|
@@ -1057,29 +1229,41 @@ async function applyWorkspacePlan(plan){
|
|
|
1057
1229
|
await mkdir(path.dirname(plan.target),{recursive:true});
|
|
1058
1230
|
await rename(plan.source,plan.target); plan.moved=true; return plan;
|
|
1059
1231
|
}
|
|
1060
|
-
async function rollbackWorkspacePlan(plan){
|
|
1232
|
+
export async function rollbackWorkspacePlan(plan){
|
|
1233
|
+
assertStageWorkspaceBoundary(plan.source,path.dirname(plan.target));
|
|
1061
1234
|
if(plan.mode==='rename'&&existsSync(plan.target)&&!existsSync(plan.source)){ await rename(plan.target,plan.source); plan.moved=false; return true; }
|
|
1062
1235
|
if(plan.mode==='create'&&existsSync(plan.target)){ await rm(plan.target,{recursive:true,force:true}); plan.created=false; return true; }
|
|
1063
1236
|
return false;
|
|
1064
1237
|
}
|
|
1065
1238
|
async function launcherPlan(stateRoot){
|
|
1066
|
-
const
|
|
1067
|
-
const
|
|
1068
|
-
const ps1Bytes=Buffer.from(renderStableLauncherPs1(),'utf8')
|
|
1069
|
-
|
|
1239
|
+
const target=canonicalLauncherPaths(stateRoot);
|
|
1240
|
+
const {state,ps1,cmd,top}=target;
|
|
1241
|
+
const ps1Bytes=Buffer.from(renderStableLauncherPs1(),'utf8');
|
|
1242
|
+
const cmdBytes=Buffer.from(target.production?renderStableLauncherCmd():renderStateBoundLauncherCmd(),'utf8');
|
|
1243
|
+
return {stateRoot:state,ps1,cmd,top,ps1Bytes,cmdBytes,launcher_sha256:sha256Bytes(ps1Bytes),
|
|
1070
1244
|
snapshots:{ps1:await snapshotFile(ps1),cmd:await snapshotFile(cmd),top:await snapshotFile(top)},installed:false};
|
|
1071
1245
|
}
|
|
1072
1246
|
async function installLaunchers(plan){
|
|
1073
|
-
|
|
1247
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1248
|
+
await atomicBytes(plan.ps1,plan.ps1Bytes);
|
|
1249
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1250
|
+
await atomicBytes(plan.cmd,plan.cmdBytes);
|
|
1251
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1252
|
+
await atomicBytes(plan.top,plan.cmdBytes);
|
|
1074
1253
|
plan.installed=true;
|
|
1075
1254
|
if(await sha256File(plan.ps1)!==plan.launcher_sha256) throw new Error('stable launcher post-install hash mismatch');
|
|
1076
1255
|
return plan;
|
|
1077
1256
|
}
|
|
1078
1257
|
async function rollbackLaunchers(plan){
|
|
1079
1258
|
if(!plan) return false;
|
|
1259
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1080
1260
|
const touched=plan.installed===true || (existsSync(plan.ps1)&&sha256Bytes(await readFile(plan.ps1))===plan.launcher_sha256);
|
|
1081
1261
|
if(!touched) return false;
|
|
1082
|
-
await restoreFile(plan.ps1,plan.snapshots.ps1);
|
|
1262
|
+
await restoreFile(plan.ps1,plan.snapshots.ps1);
|
|
1263
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1264
|
+
await restoreFile(plan.cmd,plan.snapshots.cmd);
|
|
1265
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1266
|
+
await restoreFile(plan.top,plan.snapshots.top);
|
|
1083
1267
|
plan.installed=false; return true;
|
|
1084
1268
|
}
|
|
1085
1269
|
async function revokeAuthority(ctx,prior){
|
|
@@ -1205,9 +1389,10 @@ export function rollbackLauncherPlanFromHandoff(body,priorPointer,activePointer,
|
|
|
1205
1389
|
if(!samePublicPointer(handoffPrior,priorPointer)||!samePublicPointer(handoffActive,activePointer)) throw new Error('rollback handoff pointer binding mismatch');
|
|
1206
1390
|
const state=path.resolve(stateRoot);
|
|
1207
1391
|
const launchers={
|
|
1392
|
+
stateRoot:state,
|
|
1208
1393
|
ps1:path.resolve(String(body.launchers?.ps1??'')),
|
|
1209
1394
|
cmd:path.resolve(String(body.launchers?.cmd??'')),
|
|
1210
|
-
top:path.resolve(String(body.launchers?.top??
|
|
1395
|
+
top:path.resolve(String(body.launchers?.top??'')),
|
|
1211
1396
|
launcher_sha256:String(body.launchers?.launcher_sha256??''),
|
|
1212
1397
|
ps1Bytes:Buffer.from(String(body.launchers?.ps1_bytes??''),'base64'),
|
|
1213
1398
|
cmdBytes:Buffer.from(String(body.launchers?.cmd_bytes??''),'base64'),
|
|
@@ -1218,17 +1403,18 @@ export function rollbackLauncherPlanFromHandoff(body,priorPointer,activePointer,
|
|
|
1218
1403
|
},
|
|
1219
1404
|
installed:true
|
|
1220
1405
|
};
|
|
1221
|
-
|
|
1222
|
-
launchers.cmd!==path.resolve(path.join(state,'bin','DEADBYTE-MCP.cmd'))||
|
|
1223
|
-
launchers.top!==path.resolve(TOP_LAUNCHER)) throw new Error('rollback launcher paths are not canonical');
|
|
1406
|
+
assertCanonicalLaunchers(state,launchers);
|
|
1224
1407
|
if(!HEX64.test(launchers.launcher_sha256)||sha256Bytes(launchers.ps1Bytes)!==launchers.launcher_sha256) throw new Error('rollback successor launcher bytes/hash mismatch');
|
|
1225
1408
|
if(launchers.launcher_sha256!==activePointer.launcher_sha256) throw new Error('rollback successor launcher hash does not match active pointer');
|
|
1226
1409
|
if(!launchers.snapshots.ps1.exists||launchers.snapshots.ps1.sha256!==priorPointer.launcher_sha256) throw new Error('rollback predecessor launcher snapshot does not match signed prior pointer');
|
|
1227
1410
|
return launchers;
|
|
1228
1411
|
}
|
|
1229
1412
|
async function restoreRollbackLaunchers(plan){
|
|
1413
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1230
1414
|
await restoreFile(plan.ps1,plan.snapshots.ps1);
|
|
1415
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1231
1416
|
await restoreFile(plan.cmd,plan.snapshots.cmd);
|
|
1417
|
+
assertCanonicalLaunchers(plan.stateRoot,plan);
|
|
1232
1418
|
await restoreFile(plan.top,plan.snapshots.top);
|
|
1233
1419
|
if(await sha256File(plan.ps1)!==plan.snapshots.ps1.sha256) throw new Error('rollback predecessor launcher restore hash mismatch');
|
|
1234
1420
|
return plan;
|
|
@@ -1442,6 +1628,7 @@ async function resumeSlotUpdate({handoffPath}){
|
|
|
1442
1628
|
...verifiedReceipt,historical_receipt_verified:true,current_runtime_verified:false};
|
|
1443
1629
|
}
|
|
1444
1630
|
if(phase.startsWith('failed')) throw new Error('slot update already failed at phase='+phase);
|
|
1631
|
+
assertCanonicalLaunchers(stateRoot,body.launchers);
|
|
1445
1632
|
const priorPointer=handoffPriorPointer(body),candidatePointer=handoffNewPointer(body);
|
|
1446
1633
|
const statePrior=statePointer(releaseState,'prior_pointer'),stateCandidate=statePointer(releaseState,'new_pointer');
|
|
1447
1634
|
if(!samePublicPointer(priorPointer,statePrior)||!samePublicPointer(candidatePointer,stateCandidate)) throw new Error('handoff pointer binding mismatch');
|
|
@@ -1459,7 +1646,8 @@ async function resumeSlotUpdate({handoffPath}){
|
|
|
1459
1646
|
const oldRuntime=body.old_runtime?{bridge:body.old_runtime.bridge??null,cloud:body.old_runtime.cloud??null,core:body.old_runtime.core??null}:null;
|
|
1460
1647
|
if(oldRuntime?.core&&(!Number.isInteger(Number(oldRuntime.core.pid))||String(oldRuntime.core.creation_date??'').length<1)) throw new Error('handoff old runtime core identity invalid');
|
|
1461
1648
|
const launchers={
|
|
1462
|
-
|
|
1649
|
+
stateRoot,
|
|
1650
|
+
ps1:path.resolve(String(body.launchers?.ps1??'')),cmd:path.resolve(String(body.launchers?.cmd??'')),top:path.resolve(String(body.launchers?.top??'')),
|
|
1463
1651
|
launcher_sha256:String(body.launchers?.launcher_sha256??''),
|
|
1464
1652
|
ps1Bytes:Buffer.from(String(body.launchers?.ps1_bytes??''),'base64'),cmdBytes:Buffer.from(String(body.launchers?.cmd_bytes??''),'base64'),
|
|
1465
1653
|
snapshots:{
|
|
@@ -1467,6 +1655,7 @@ async function resumeSlotUpdate({handoffPath}){
|
|
|
1467
1655
|
},
|
|
1468
1656
|
installed:phaseRank(phase)>=phaseRank('launcher_installed')
|
|
1469
1657
|
};
|
|
1658
|
+
assertCanonicalLaunchers(stateRoot,launchers);
|
|
1470
1659
|
if(!HEX64.test(launchers.launcher_sha256)||sha256Bytes(launchers.ps1Bytes)!==launchers.launcher_sha256) throw new Error('handoff launcher bytes/hash mismatch');
|
|
1471
1660
|
const workspace={
|
|
1472
1661
|
source:path.resolve(String(body.workspace?.source??releaseState.workspace?.source??'')),
|
|
@@ -1657,13 +1846,15 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
|
|
|
1657
1846
|
return {ok:true,launched,updates};
|
|
1658
1847
|
}
|
|
1659
1848
|
|
|
1660
|
-
export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
|
|
1849
|
+
export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,autonomousPolicyBootstrap=null,ownerApprovalBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
|
|
1661
1850
|
// R64 grants have no signed revocation epoch. The historical boolean restore
|
|
1662
1851
|
// cannot prove continuity, and must never materialize a successor grant.
|
|
1663
1852
|
if(restoreStandingGrants)throw new Error('R65 standing grant auto-restore disabled: explicit reauthorization required');
|
|
1853
|
+
if(Boolean(autonomousPolicyBootstrap)!==Boolean(ownerApprovalBootstrap)) throw new Error('autonomous policy bootstrap requires pinned owner approval');
|
|
1664
1854
|
stateRoot=resolveStateRoot(stateRoot);
|
|
1665
1855
|
if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
|
|
1666
1856
|
const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
|
|
1857
|
+
assertStagingBootstrapInputs({stateRoot:state,sourceRoot,bridgeBootstrap,cloudTransport,processPolicyBootstrap,hostPolicyBootstrap,codingPolicyBootstrap,autonomousPolicyBootstrap,ownerApprovalBootstrap,desktopPolicyBootstrap,semanticLspBootstrap});
|
|
1667
1858
|
const key=await loadUpdateKey(state),updateId='r27-'+expectedManifest.slice(0,16)+'-'+Date.now().toString(36)+'-'+randomBytes(4).toString('hex');
|
|
1668
1859
|
const evidenceDir=path.join(state,'evidence','updates',updateId),statePath=path.join(evidenceDir,'state.json'),failurePath=path.join(evidenceDir,'failure-receipt.json');
|
|
1669
1860
|
await mkdir(evidenceDir,{recursive:true});
|
|
@@ -1679,13 +1870,27 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1679
1870
|
const stageVerified=await verifyPackageRoot(stageRoot,expectedVersion,expectedManifest,'stage');
|
|
1680
1871
|
const developmentVerified=await verifyPackageRoot(development,expectedVersion,expectedManifest,'development');
|
|
1681
1872
|
control=await loadControlInputs({stateRoot:state,sourceRoot,pointerInfo,bridgeBootstrap});
|
|
1873
|
+
const codingBootstrap=await prepareCodingPolicyBootstrap(codingPolicyBootstrap);
|
|
1874
|
+
const autonomousBootstrap=await prepareAutonomousPolicyBootstrap(autonomousPolicyBootstrap);
|
|
1875
|
+
let ownerApprovalProof=null;
|
|
1876
|
+
if(autonomousBootstrap){
|
|
1877
|
+
if(hostCommandPins.length>0) throw new Error('autonomous bootstrap cannot combine with command repinning');
|
|
1878
|
+
const selectedCoding=codingBootstrap?.policy??control.codingPolicy;
|
|
1879
|
+
const selectedCodingSha256=codingBootstrap?.sha256??await sha256File(control.config.CodingPolicyFile);
|
|
1880
|
+
ownerApprovalProof=await verifyAutonomousOwnerBootstrap({
|
|
1881
|
+
stateRoot:state,bootstrap:ownerApprovalBootstrap,autonomousPolicy:autonomousBootstrap.policy,
|
|
1882
|
+
codingPolicy:selectedCoding,codingPolicySha256:selectedCodingSha256,
|
|
1883
|
+
autonomousPolicySha256:autonomousBootstrap.sha256,expectedManifest
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1682
1886
|
// Read-only predecessor authority and process identity must be safe before
|
|
1683
1887
|
// materializing a slot or changing any stable runtime file.
|
|
1684
1888
|
await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});
|
|
1685
1889
|
mutationBegan=true;
|
|
1686
1890
|
const slot=await materializeReleaseSlot({source:stageRoot,stateRoot:state,version:expectedVersion,manifestSha256:expectedManifest});
|
|
1687
1891
|
const hostBootstrap=await prepareHostPolicyBootstrap(hostPolicyBootstrap);if(hostBootstrap) control.hostPolicy=hostBootstrap.policy;
|
|
1688
|
-
|
|
1892
|
+
if(codingBootstrap) control.codingPolicy=codingBootstrap.policy;
|
|
1893
|
+
if(autonomousBootstrap) control.autonomousPolicy=autonomousBootstrap.policy;
|
|
1689
1894
|
const processBootstrap=await prepareProcessPolicyBootstrap(processPolicyBootstrap);
|
|
1690
1895
|
if(processBootstrap){ control.processPolicy=processBootstrap.policy;control.config.ProcessRuntimeEnabled=true; }
|
|
1691
1896
|
const desktopBootstrap=await prepareDesktopPolicyBootstrap(desktopPolicyBootstrap);if(desktopBootstrap) control.desktopPolicy=desktopBootstrap.policy;
|
|
@@ -1730,6 +1935,8 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1730
1935
|
stable_mcp_url:stableMcpUrl?{path:stableMcpUrl.path,sha256:stableMcpUrl.sha256,origin:stableMcpUrl.origin,changed:stableMcpUrl.changed}:null,
|
|
1731
1936
|
bridge:{working_directory:control.config.Bridge.WorkingDirectory,script_path:control.config.Bridge.ScriptPath,script_sha256:control.config.Bridge.ScriptSha256},
|
|
1732
1937
|
host_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.hostRepinned},coding_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.codingRepinned},
|
|
1938
|
+
owner_approval:ownerApprovalProof?{subject_sha256:ownerApprovalProof.subject_sha256,owner_key_id:ownerApprovalProof.owner_key_id,
|
|
1939
|
+
root_id:ownerApprovalProof.root_id,scope:ownerApprovalProof.scope,expires_at_utc:ownerApprovalProof.expires_at_utc}:null,
|
|
1733
1940
|
new_pointer:publicPointer(newPointer)});
|
|
1734
1941
|
predecessor=await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});oldRuntime=predecessor.runtime;priorStatus=predecessor.status;
|
|
1735
1942
|
await writeState(statePath,journal,'old_runtime_verified',{predecessor_mode:predecessor.mode,old_runtime:generationTuple(oldRuntime),
|
|
@@ -1865,7 +2072,8 @@ export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEF
|
|
|
1865
2072
|
stage:args.stage,developmentRoot:args.developmentRoot,expectedVersion:args.expectedVersion,expectedManifest:args.expectedManifest,
|
|
1866
2073
|
sourceRoot:args.sourceRoot,stateRoot,restoreStandingGrants:args.restoreStandingGrants,recoverOfflinePredecessor:args.recoverOfflinePredecessor,
|
|
1867
2074
|
cloudTransport:args.cloudTransport,bridgeBootstrap:args.bridgeBootstrap,processPolicyBootstrap:args.processPolicyBootstrap,
|
|
1868
|
-
hostPolicyBootstrap:args.hostPolicyBootstrap,codingPolicyBootstrap:args.codingPolicyBootstrap,
|
|
2075
|
+
hostPolicyBootstrap:args.hostPolicyBootstrap,codingPolicyBootstrap:args.codingPolicyBootstrap,
|
|
2076
|
+
autonomousPolicyBootstrap:args.autonomousPolicyBootstrap,ownerApprovalBootstrap:args.ownerApprovalBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,
|
|
1869
2077
|
semanticLspBootstrap:args.semanticLspBootstrap,enableMachineFs:args.enableMachineFs,enableObservation:args.enableObservation,
|
|
1870
2078
|
enableDesktop:args.enableDesktop,enableAutonomousSupervisor:args.enableAutonomousSupervisor,hostCommandPins:args.hostCommandPins
|
|
1871
2079
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {lstatSync} from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const canonical = value => process.platform === 'win32' ? value.toLowerCase() : value;
|
|
6
|
+
function within(parent,child){
|
|
7
|
+
const relative=path.relative(canonical(parent),canonical(child));
|
|
8
|
+
return relative===''||(!path.isAbsolute(relative)&&relative!=='..'&&!relative.startsWith('..'+path.sep));
|
|
9
|
+
}
|
|
10
|
+
function productionRoot(){return path.resolve(os.homedir(),'.deadbyte-mcp');}
|
|
11
|
+
function isStage(stateRoot){
|
|
12
|
+
const state=path.resolve(stateRoot),production=productionRoot();
|
|
13
|
+
if(state===production||canonical(state)===canonical(production))return false;
|
|
14
|
+
if(within(production,state)||within(state,production))throw new Error('STAGING_STATE_OVERLAPS_PRODUCTION');
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
function noReparseComponents(file,label){
|
|
18
|
+
let current=file;
|
|
19
|
+
for(;;){
|
|
20
|
+
try {if(lstatSync(current).isSymbolicLink())throw new Error('STAGING_REPARSE_INPUT: '+label);}
|
|
21
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
22
|
+
const parent=path.dirname(current);
|
|
23
|
+
if(parent===current)break;
|
|
24
|
+
current=parent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** A stage never reads policy, token, executable or config bytes from a production state. */
|
|
28
|
+
export function assertStageOwnedPath(file,stateRoot,label){
|
|
29
|
+
if(!isStage(stateRoot))return;
|
|
30
|
+
if(typeof file!=='string'||!path.isAbsolute(file)||file.includes('\0'))throw new Error('STAGING_ABSOLUTE_PATH_REQUIRED: '+label);
|
|
31
|
+
const state=path.resolve(stateRoot),target=path.resolve(file);
|
|
32
|
+
if(!within(state,target)||canonical(state)===canonical(target))throw new Error('STAGING_EXTERNAL_INPUT: '+label);
|
|
33
|
+
noReparseComponents(target,label);
|
|
34
|
+
}
|
|
35
|
+
export function assertStageNoProductionPath(file,stateRoot,label){
|
|
36
|
+
if(!isStage(stateRoot))return;
|
|
37
|
+
if(typeof file!=='string'||!path.isAbsolute(file)||file.includes('\0'))throw new Error('STAGING_ABSOLUTE_PATH_REQUIRED: '+label);
|
|
38
|
+
const target=path.resolve(file),production=productionRoot();
|
|
39
|
+
if(within(production,target))throw new Error('STAGING_PRODUCTION_INPUT: '+label);
|
|
40
|
+
noReparseComponents(target,label);
|
|
41
|
+
}
|
|
42
|
+
export function assertStagingBootstrapInputs({stateRoot,sourceRoot=null,bridgeBootstrap=null,cloudTransport=null,
|
|
43
|
+
processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,autonomousPolicyBootstrap=null,
|
|
44
|
+
ownerApprovalBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null}={}){
|
|
45
|
+
if(!isStage(stateRoot))return;
|
|
46
|
+
noReparseComponents(path.resolve(stateRoot),'state root');
|
|
47
|
+
if(sourceRoot!==null)assertStageOwnedPath(sourceRoot,stateRoot,'legacy source root');
|
|
48
|
+
if(bridgeBootstrap)assertStageOwnedPath(bridgeBootstrap.root,stateRoot,'bridge bootstrap root');
|
|
49
|
+
for(const [label,item] of Object.entries({processPolicyBootstrap,hostPolicyBootstrap,codingPolicyBootstrap,
|
|
50
|
+
autonomousPolicyBootstrap,ownerApprovalBootstrap,desktopPolicyBootstrap,semanticLspBootstrap})){
|
|
51
|
+
if(item)assertStageOwnedPath(item.path,stateRoot,label);
|
|
52
|
+
}
|
|
53
|
+
if(cloudTransport){
|
|
54
|
+
assertStageOwnedPath(cloudTransport.tokenFile?.path,stateRoot,'named tunnel token file');
|
|
55
|
+
assertStageOwnedPath(cloudTransport.childWorkingDirectory,stateRoot,'named tunnel child directory');
|
|
56
|
+
assertStageNoProductionPath(cloudTransport.cloudflared?.path,stateRoot,'named tunnel executable');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function assertStagingControlConfig(config,stateRoot,{configPath}={}){
|
|
60
|
+
if(!isStage(stateRoot))return;
|
|
61
|
+
assertStageOwnedPath(configPath,stateRoot,'controller config file');
|
|
62
|
+
const owned=['CoreRoot','WorkspaceRoot','CodingPolicyFile','AutonomousPolicyFile','HostPolicyFile',
|
|
63
|
+
'ProcessPolicyFile','DesktopPolicyFile','SemanticLspConfigFile','McpPathFile','McpUrlFile'];
|
|
64
|
+
for(const key of owned)if(config[key]!==undefined&&config[key]!==null&&config[key]!=='')
|
|
65
|
+
assertStageOwnedPath(config[key],stateRoot,key);
|
|
66
|
+
if(!config.CodingPolicyFile||!config.AutonomousPolicyFile)throw new Error('STAGING_REQUIRED_POLICIES_MISSING');
|
|
67
|
+
for(const [name,component] of [['Bridge',config.Bridge],['Cloud',config.Cloud]]){
|
|
68
|
+
if(!component||typeof component!=='object')throw new Error('STAGING_COMPONENT_MISSING: '+name);
|
|
69
|
+
// A hash-pinned executable (e.g. system Node) may be shared. Its script,
|
|
70
|
+
// cwd, policy inputs and secrets may not be shared with production.
|
|
71
|
+
if(component.FilePath)assertStageNoProductionPath(component.FilePath,stateRoot,name+'.FilePath');
|
|
72
|
+
for(const key of ['WorkingDirectory','ScriptPath'])if(component[key])
|
|
73
|
+
assertStageOwnedPath(component[key],stateRoot,name+'.'+key);
|
|
74
|
+
if(Array.isArray(component.Dependencies))for(const dep of component.Dependencies){
|
|
75
|
+
if(dep?.Name==='cloudflared'&&name==='Cloud')assertStageNoProductionPath(dep.Path,stateRoot,'Cloud.cloudflared');
|
|
76
|
+
else assertStageOwnedPath(dep?.Path,stateRoot,name+'.dependency.'+String(dep?.Name??''));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/tool-audit.mjs
CHANGED
|
@@ -116,11 +116,13 @@ function summarizeOutcome(result,error,durationMs){
|
|
|
116
116
|
const structured=result?.structuredContent;
|
|
117
117
|
const status=typeof structured?.status==='string'?structured.status:(result?.isError===true?'error':'ok');
|
|
118
118
|
const isError=result?.isError===true;
|
|
119
|
+
const unknownSideEffect=structured?.side_effect_state==='unknown'||structured?.result?.side_effect_state==='unknown';
|
|
119
120
|
const capability=structured?.result;
|
|
120
121
|
const snapshot=capability?.schema==='deadbyte.capability-manifest.v3'&&HEX64.test(String(capability.snapshot_id??''))&&
|
|
121
122
|
Number.isInteger(capability.state_epoch)&&capability.state_epoch>0
|
|
122
123
|
?{snapshot_id:capability.snapshot_id,state_epoch:capability.state_epoch}:{};
|
|
123
124
|
return {status:String(status).slice(0,128),is_error:isError,duration_ms:duration,receipt_sha256s:outcomeReceiptHashes(result),
|
|
125
|
+
...(unknownSideEffect?{side_effect_state:'unknown'}:{}),
|
|
124
126
|
...snapshot,
|
|
125
127
|
...(isError?{reason:resultOutcomeReason(result)}:{})};
|
|
126
128
|
}
|
package/src/version.mjs
CHANGED
|
@@ -53,7 +53,8 @@ async function createProviderHostFixture(){
|
|
|
53
53
|
planner:{backend:'openai_compatible',endpoint:`http://127.0.0.1:${planner.address().port}/v1/chat/completions`,model:'r34-host-test',max_tokens:2048,timeout_ms:30000,max_response_bytes:262144},
|
|
54
54
|
limits:{max_iterations:8,max_mutations:8,max_wall_ms:null,max_context_bytes:262144,max_diff_bytes:262144,max_files_per_diff:4},profiles:{}}));
|
|
55
55
|
const autonomousPolicy=await loadAutonomousPolicy(autonomousPath,codingPolicy);
|
|
56
|
-
await writeBoundStandingGrant('autonomous', autonomousPolicy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
|
|
56
|
+
await writeBoundStandingGrant('autonomous', autonomousPolicy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
|
|
57
|
+
coding_policy_sha256:codingPolicy.policy_sha256,
|
|
57
58
|
instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()});
|
|
58
59
|
const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
59
60
|
const nativeDiffRuntime=createNativeDiffRuntime({policy:autonomousPolicy,codingRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
@@ -2,10 +2,26 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {
|
|
4
4
|
autonomousGoalApproveOutputSchema, autonomousGoalRunOutputSchema, autonomousGoalStatusOutputSchema,
|
|
5
|
-
autonomousGoalVerifyOutputSchema,
|
|
5
|
+
autonomousGoalVerifyOutputSchema, nativeDiffPreviewOutputSchema,
|
|
6
6
|
autonomousStructuredResult
|
|
7
7
|
} from '../src/autonomous-output-contracts.mjs';
|
|
8
8
|
|
|
9
|
+
test('R71 preview-only MCP output cannot masquerade as a signed ready-to-apply diff',()=>{
|
|
10
|
+
const base={root_id:'preview',operation_count:1,payload_bytes:0,paths:['a.txt'],
|
|
11
|
+
operations:[{kind:'delete',path:'a.txt',expected_sha256:'a'.repeat(64)}]};
|
|
12
|
+
const unsigned=autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
|
|
13
|
+
status:'preview_only',schema:'deadbyte.native-diff.v1',...base
|
|
14
|
+
});
|
|
15
|
+
assert.equal(unsigned.structuredContent.status,'preview_only');
|
|
16
|
+
assert.equal(unsigned.structuredContent.diff_id,undefined);
|
|
17
|
+
assert.throws(()=>autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
|
|
18
|
+
status:'preview_only',diff_id:'b'.repeat(64),...base
|
|
19
|
+
}),/autonomous output contract violation/);
|
|
20
|
+
assert.throws(()=>autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
|
|
21
|
+
status:'ready',...base
|
|
22
|
+
}),/autonomous output contract violation/);
|
|
23
|
+
});
|
|
24
|
+
|
|
9
25
|
test('autonomous_goal_verify structured contract preserves delegated action evidence count', () => {
|
|
10
26
|
const value = {
|
|
11
27
|
ok:true,
|
|
@@ -73,7 +73,8 @@ async function startPlanner() {
|
|
|
73
73
|
}));
|
|
74
74
|
const autonomousSha = sha(await readFile(autonomousPath));
|
|
75
75
|
await writeBoundStandingGrant('autonomous', path.join(root, 'runtime', 'autonomous-grant.json'), {
|
|
76
|
-
schema:'deadbyte.autonomous-grant.v2', policy_sha256:autonomousSha,
|
|
76
|
+
schema:'deadbyte.autonomous-grant.v2', policy_sha256:autonomousSha,
|
|
77
|
+
coding_policy_sha256:codingSha,
|
|
77
78
|
instance_nonce:nonce(), root_ids:['project'], granted_at_utc:new Date().toISOString()
|
|
78
79
|
});
|
|
79
80
|
const client = new Client({ name:'deadbyte-supervisor-integration', version:'0.8.0' },
|