deadbyte-mcp 0.15.41 → 0.15.43
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/DEADBYTE-MCP.cmd +8 -4
- package/MANIFEST.SHA256 +88 -43
- package/PROVENANCE.json +66 -15
- package/README.txt +27 -18
- package/RELEASE.json +55 -15
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +117 -14
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +132 -12
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R65-AUTHORITY-CONTINUITY-DESIGN.json +27 -0
- package/docs/R65-REFERENCE-GAP-MATRIX.json +22 -0
- package/package.json +6 -4
- package/scripts/deploy-r48.mjs +123 -7
- package/scripts/gate-windows-r65-prepublish.ps1 +65 -0
- package/scripts/gate-windows-r66-prepublish.ps1 +65 -0
- package/scripts/npm-publication-r65.mjs +139 -0
- package/scripts/npm-release-r48.mjs +56 -37
- package/scripts/r65-canonical-full.mjs +45 -0
- package/scripts/r65-focused.mjs +37 -0
- package/scripts/verify-r65-parent-source.mjs +75 -0
- package/src/activation-continuity-engine-r65.mjs +225 -0
- package/src/activation-lock-r65.mjs +155 -0
- package/src/activation-preflight-r65.mjs +197 -0
- package/src/authority-continuity-r65.mjs +541 -0
- package/src/authority-epoch-cli-r65.mjs +10 -0
- package/src/autonomous-policy.mjs +7 -3
- package/src/coding-plane.mjs +12 -3
- package/src/desktop-policy-r40.mjs +6 -3
- package/src/doctor-runtime-r58.mjs +3 -1
- package/src/doctor-transactions-r58.mjs +1 -1
- package/src/host-plane.mjs +9 -3
- package/src/process-policy.mjs +7 -4
- package/src/remote-authority.mjs +10 -1
- package/src/runtime-update.mjs +15 -5
- package/src/version.mjs +1 -1
- package/test/audit-context.test.mjs +15 -4
- package/test/autonomous-local-supervisor.test.mjs +7 -6
- package/test/autonomous-supervisor-integration.test.mjs +8 -7
- package/test/capability-resolver.test.mjs +11 -5
- package/test/codex-autonomous-r44.test.mjs +7 -6
- package/test/coding-observation.test.mjs +4 -3
- package/test/commander-real-parity-r47.test.mjs +5 -4
- package/test/commander-torture-r47.test.mjs +5 -4
- package/test/desktop-policy-r40.test.mjs +6 -4
- package/test/desktop-runtime-r40.test.mjs +8 -3
- package/test/host-inline-r45.test.mjs +4 -3
- package/test/machine-fs-runtime.test.mjs +4 -3
- package/test/process-longrun-r36.test.mjs +3 -2
- package/test/process-policy.test.mjs +10 -6
- package/test/process-runtime.test.mjs +9 -6
- package/test/r52-host-revocation.test.mjs +1 -0
- package/test/r58-doctor-runtime.test.mjs +18 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r64-release-contract.test.mjs +2 -1
- package/test/r65-activation-lock-recovery.test.mjs +72 -0
- package/test/r65-activation-lock.test.mjs +59 -0
- package/test/r65-activation-orchestration.test.mjs +179 -0
- package/test/r65-activation-preflight.test.mjs +35 -0
- package/test/r65-authority-continuity-decision.test.mjs +58 -0
- package/test/r65-authority-durable-policy-binding.test.mjs +34 -0
- package/test/r65-authority-epoch-cli.test.mjs +125 -0
- package/test/r65-authority-epoch-races.test.mjs +87 -0
- package/test/r65-authority-epoch-recovery.test.mjs +82 -0
- package/test/r65-authority-epoch-store.test.mjs +94 -0
- package/test/r65-authority-fixture.mjs +41 -0
- package/test/r65-authority-grant-capture.test.mjs +37 -0
- package/test/r65-authority-observation-epoch.test.mjs +25 -0
- package/test/r65-authority-policy-envelope.test.mjs +46 -0
- package/test/r65-authority-restored-safe.test.mjs +75 -0
- package/test/r65-authority-runtime-epoch.test.mjs +115 -0
- package/test/r65-authority-runtime-policy-binding.test.mjs +29 -0
- package/test/r65-authority-test-trust.mjs +20 -0
- package/test/r65-cli-invocation.test.mjs +38 -0
- package/test/r65-controller-authority-epoch-live.test.mjs +100 -0
- package/test/r65-controller-authority-epoch.test.mjs +37 -0
- package/test/r65-deploy-preflight.test.mjs +55 -0
- package/test/r65-epoch-native-activation-integration.test.mjs +141 -0
- package/test/r65-epoch-native-continuity-engine.test.mjs +112 -0
- package/test/r65-epoch-native-cross-plane.test.mjs +95 -0
- package/test/r65-legacy-authority-migration.test.mjs +63 -0
- package/test/r65-legacy-auto-restore-rejection.test.mjs +17 -0
- package/test/r65-npm-publication.test.mjs +114 -0
- package/test/r65-parent-source-verifier.test.mjs +63 -0
- package/test/r65-release-contract.test.mjs +75 -0
- package/test/r65-transaction-precondition.test.mjs +36 -0
- package/test/r66-release-contract.test.mjs +56 -0
- package/test/remote-session-cli.test.mjs +6 -6
- package/test/standing-grants.test.mjs +20 -4
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { mkdir, readFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
5
|
+
import { verifyStandingGrantEpoch } from './authority-continuity-r65.mjs';
|
|
5
6
|
|
|
6
7
|
export const DESKTOP_POLICY_SCHEMA='deadbyte.desktop-policy.v1';
|
|
7
8
|
export const DESKTOP_GRANT_SCHEMA='deadbyte.desktop-grant.v1';
|
|
@@ -84,8 +85,8 @@ export async function loadDesktopPolicy(policyPath){
|
|
|
84
85
|
|
|
85
86
|
export async function desktopSessionStatus(policy){
|
|
86
87
|
if(!policy||policy.schema!==DESKTOP_POLICY_SCHEMA) throw new Error('desktop policy invalid');
|
|
87
|
-
let grant;
|
|
88
|
-
try{
|
|
88
|
+
let grant,grantBytes;
|
|
89
|
+
try{grantBytes=await readFile(policy.grant_file);grant=JSON.parse(decodeUtf8Strict(grantBytes));}
|
|
89
90
|
catch(error){
|
|
90
91
|
return Object.freeze({configured:true,armed:false,reason:error?.code==='ENOENT'?'disarmed':'invalid-grant',standing:false,action_kinds:[]});
|
|
91
92
|
}
|
|
@@ -96,8 +97,10 @@ export async function desktopSessionStatus(policy){
|
|
|
96
97
|
try{granted=actions(grant.action_kinds,'grant.action_kinds');}
|
|
97
98
|
catch{return Object.freeze({configured:true,armed:false,reason:'actions-invalid',standing:false,action_kinds:[]});}
|
|
98
99
|
if(granted.some(item=>!policy.allowed_actions.includes(item))) return Object.freeze({configured:true,armed:false,reason:'actions-outside-policy',standing:false,action_kinds:[]});
|
|
100
|
+
const epoch=await verifyStandingGrantEpoch({plane:'desktop',grantFile:policy.grant_file,grantBytes,grantInstanceNonce:grant.instance_nonce,policyPath:policy.policy_path});
|
|
101
|
+
if(!epoch.trusted)return Object.freeze({configured:true,armed:false,reason:epoch.reason,standing:false,instance_nonce:grant.instance_nonce,action_kinds:granted});
|
|
99
102
|
return Object.freeze({
|
|
100
|
-
configured:true,armed:true,reason:'standing-grant',standing:true,instance_nonce:grant.instance_nonce,action_kinds:granted
|
|
103
|
+
configured:true,armed:true,reason:'standing-grant',standing:true,instance_nonce:grant.instance_nonce,action_kinds:granted,authority_epoch:epoch
|
|
101
104
|
});
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -28,8 +28,10 @@ export function validateDoctorControllerObservation(value,expectedCoreRoot){
|
|
|
28
28
|
if(typeof value.supervisor.configured!=='boolean'||typeof value.supervisor.running!=='boolean'||!reason(value.supervisor.reason)||(!value.supervisor.configured&&value.supervisor.running))throw new Error('supervisor observation invalid');
|
|
29
29
|
object(value.authority,['process','coding','autonomous','desktop','host']);
|
|
30
30
|
for(const item of Object.values(value.authority)){
|
|
31
|
-
|
|
31
|
+
const hasEpoch=Object.hasOwn(item,'epoch_seq')||Object.hasOwn(item,'epoch_sha256');
|
|
32
|
+
object(item,hasEpoch?['configured','armed','reason','epoch_seq','epoch_sha256']:['configured','armed','reason']);
|
|
32
33
|
if(typeof item.configured!=='boolean'||typeof item.armed!=='boolean'||!reason(item.reason)||(!item.configured&&item.armed)||(item.armed&&item.reason!=='standing-grant'))throw new Error('authority observation invalid');
|
|
34
|
+
if(hasEpoch&&(!item.armed||!Number.isSafeInteger(item.epoch_seq)||item.epoch_seq<1||typeof item.epoch_sha256!=='string'||!/^[0-9a-f]{64}$/.test(item.epoch_sha256)))throw new Error('authority epoch observation invalid');
|
|
33
35
|
}
|
|
34
36
|
return value;
|
|
35
37
|
}
|
|
@@ -87,7 +87,7 @@ export async function inspectDoctorTransactions(explicitRoot,{maxEntries=1024}={
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if(body.phase==='superseded'){terminal++;continue;}
|
|
90
|
-
if(body.phase==='failed_receipted'){
|
|
90
|
+
if(body.phase==='failed_receipted'||body.phase==='precondition_rejected'){
|
|
91
91
|
add('FAIL','Transaction journal claims a signed failure receipt but the receipt is missing.',{state_sha256:digest,check:'missing_failure_receipt',recorded_phase:body.phase});
|
|
92
92
|
continue;
|
|
93
93
|
}
|
package/src/host-plane.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { spawn } from 'node:child_process';
|
|
|
11
11
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
12
12
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
13
13
|
import { atomicMutation } from './atomic-mutation.mjs';
|
|
14
|
+
import { verifyStandingGrantEpoch } from './authority-continuity-r65.mjs';
|
|
14
15
|
|
|
15
16
|
export const HOST_POLICY_SCHEMA = 'deadbyte.host-policy.v1';
|
|
16
17
|
export const HOST_POLICY_SCHEMA_V2 = 'deadbyte.host-policy.v2';
|
|
@@ -164,9 +165,10 @@ export async function loadHostPolicy(policyPath) {
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
export async function assertHostArmed(policy, now = new Date()) {
|
|
167
|
-
let arm;
|
|
168
|
+
let arm,armBytes;
|
|
168
169
|
try {
|
|
169
|
-
|
|
170
|
+
armBytes=await readFile(policy.arm_file);
|
|
171
|
+
arm = JSON.parse(decodeUtf8Strict(armBytes));
|
|
170
172
|
} catch (error) {
|
|
171
173
|
throw new Error(`privileged host plane is disarmed: ${error?.code === 'ENOENT' ? 'arm file missing' : 'invalid arm file'}`);
|
|
172
174
|
}
|
|
@@ -175,7 +177,11 @@ export async function assertHostArmed(policy, now = new Date()) {
|
|
|
175
177
|
if (typeof arm.instance_nonce !== 'string' || !SHA256_RE.test(arm.instance_nonce)) {
|
|
176
178
|
throw new Error('privileged host plane is disarmed: nonce invalid');
|
|
177
179
|
}
|
|
178
|
-
if (arm.schema === HOST_GRANT_SCHEMA)
|
|
180
|
+
if (arm.schema === HOST_GRANT_SCHEMA) {
|
|
181
|
+
const epoch=await verifyStandingGrantEpoch({plane:'host',grantFile:policy.arm_file,grantBytes:armBytes,grantInstanceNonce:arm.instance_nonce,policyPath:policy.policy_path});
|
|
182
|
+
if(!epoch.trusted)throw new Error(`privileged host plane is disarmed: ${epoch.reason}`);
|
|
183
|
+
return { arm, standing:true, remaining_ms:null, authority_epoch:epoch };
|
|
184
|
+
}
|
|
179
185
|
const expires = Date.parse(arm.expires_at_utc);
|
|
180
186
|
if (!Number.isFinite(expires) || expires <= now.getTime()) throw new Error('privileged host plane is disarmed: arm expired');
|
|
181
187
|
return { arm, standing:false, remaining_ms: expires - now.getTime() };
|
package/src/process-policy.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { lstat, mkdir, readFile, realpath, stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
5
|
+
import { verifyStandingGrantEpoch } from './authority-continuity-r65.mjs';
|
|
5
6
|
|
|
6
7
|
export const PROCESS_POLICY_SCHEMA = 'deadbyte.process-policy.v1';
|
|
7
8
|
export const PROCESS_GRANT_SCHEMA = 'deadbyte.process-grant.v1';
|
|
@@ -124,8 +125,8 @@ function configuredStatus(policy, reason) {
|
|
|
124
125
|
policy_sha256: policy.policy_sha256
|
|
125
126
|
};
|
|
126
127
|
}export async function processSessionStatus(policy) {
|
|
127
|
-
let grant;
|
|
128
|
-
try { grant = JSON.parse(decodeUtf8Strict(
|
|
128
|
+
let grant,grantBytes;
|
|
129
|
+
try { grantBytes=await readFile(policy.grant_file); grant = JSON.parse(decodeUtf8Strict(grantBytes)); }
|
|
129
130
|
catch (error) { return configuredStatus(policy, error?.code === 'ENOENT' ? 'disarmed' : 'invalid-grant'); }
|
|
130
131
|
if (!grant || grant.schema !== PROCESS_GRANT_SCHEMA) return configuredStatus(policy, 'grant-schema');
|
|
131
132
|
if (grant.policy_sha256 !== policy.policy_sha256) return configuredStatus(policy, 'policy-digest');
|
|
@@ -136,10 +137,12 @@ function configuredStatus(policy, reason) {
|
|
|
136
137
|
grant.root_ids.some(id => !ID_RE.test(id) || !policy.roots[id])) return configuredStatus(policy, 'roots-invalid');
|
|
137
138
|
const profileIds = [...new Set(grant.profile_ids)].sort();
|
|
138
139
|
const rootIds = [...new Set(grant.root_ids)].sort();
|
|
140
|
+
const epoch=await verifyStandingGrantEpoch({plane:'process',grantFile:policy.grant_file,grantBytes,grantInstanceNonce:grant.instance_nonce,policyPath:policy.policy_path});
|
|
141
|
+
if(!epoch.trusted)return configuredStatus(policy,epoch.reason);
|
|
139
142
|
return {
|
|
140
143
|
configured: true, armed: true, reason: 'standing-grant', expires_at_utc: null,
|
|
141
144
|
root_ids: rootIds, profile_ids: profileIds, policy_sha256: policy.policy_sha256,
|
|
142
|
-
instance_nonce: grant.instance_nonce, standing: true
|
|
145
|
+
instance_nonce: grant.instance_nonce, standing: true, authority_epoch:epoch
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
|
|
@@ -168,4 +171,4 @@ export async function recheckProcessAuthority(policy, initial, profileId = null,
|
|
|
168
171
|
current.root_ids.some((id, index) => id !== initial.root_ids[index]);
|
|
169
172
|
if (changed) throw new Error('process authority changed during operation');
|
|
170
173
|
return current;
|
|
171
|
-
}
|
|
174
|
+
}
|
package/src/remote-authority.mjs
CHANGED
|
@@ -32,7 +32,16 @@ export function parseAuthorityObservation(raw){
|
|
|
32
32
|
const entry=value.authority[plane];
|
|
33
33
|
if(!entry||typeof entry!=='object'||typeof entry.armed!=='boolean'||typeof entry.configured!=='boolean'||typeof entry.reason!=='string')
|
|
34
34
|
throw new Error(`controller authority ${plane} observation invalid`);
|
|
35
|
-
|
|
35
|
+
const hasEpochSeq=Object.hasOwn(entry,'epoch_seq'),hasEpochSha=Object.hasOwn(entry,'epoch_sha256');
|
|
36
|
+
if(hasEpochSeq!==hasEpochSha)throw new Error(`controller authority ${plane} epoch observation incomplete`);
|
|
37
|
+
let epochSeq=null,epochSha256=null;
|
|
38
|
+
if(hasEpochSeq){
|
|
39
|
+
if(!Number.isSafeInteger(entry.epoch_seq)||entry.epoch_seq<1||typeof entry.epoch_sha256!=='string'||!/^[0-9a-f]{64}$/.test(entry.epoch_sha256))
|
|
40
|
+
throw new Error(`controller authority ${plane} epoch observation invalid`);
|
|
41
|
+
epochSeq=entry.epoch_seq;epochSha256=entry.epoch_sha256;
|
|
42
|
+
}
|
|
43
|
+
authority[plane]=Object.freeze({configured:entry.configured,armed:entry.armed,reason:entry.reason.slice(0,128),
|
|
44
|
+
epoch_seq:epochSeq,epoch_sha256:epochSha256});
|
|
36
45
|
}
|
|
37
46
|
return Object.freeze({...value,authority:Object.freeze(authority)});
|
|
38
47
|
}
|
package/src/runtime-update.mjs
CHANGED
|
@@ -859,7 +859,7 @@ async function captureRuntime(ctx){
|
|
|
859
859
|
const bridge=await readLedger(ctx,'bridge'); const cloud=await readLedger(ctx,'cloud'); const core=coreProcess(ctx.core_root,bridge);
|
|
860
860
|
return {status,status_text:statusText.trim(),bridge,cloud,core};
|
|
861
861
|
}
|
|
862
|
-
export async function capturePredecessor(ctx,recoverOfflinePredecessor){
|
|
862
|
+
export async function capturePredecessor(ctx,recoverOfflinePredecessor,{allowHostDisarm=true}={}){
|
|
863
863
|
let statusText,status;
|
|
864
864
|
try{
|
|
865
865
|
statusText=controllerCaptured(ctx,'status',15000);
|
|
@@ -894,6 +894,7 @@ export async function capturePredecessor(ctx,recoverOfflinePredecessor){
|
|
|
894
894
|
if(!recoverOfflinePredecessor) throw new Error(`slot update requires ONLINE runtime: ${statusText}`);
|
|
895
895
|
if(!status.offline||status.bridge_pid||status.cloud_pid||status.local_ready) throw new Error(`offline predecessor recovery requires clean OFFLINE controller state: ${statusText}`);
|
|
896
896
|
if(status.host_armed){
|
|
897
|
+
if(!allowHostDisarm) throw new Error('offline predecessor recovery requires host plane DISARMED');
|
|
897
898
|
controllerCaptured(ctx,'disarmhost',15000);
|
|
898
899
|
statusText=controllerCaptured(ctx,'status',15000);
|
|
899
900
|
status=parseControllerStatus(statusText);
|
|
@@ -1235,7 +1236,7 @@ async function restoreRollbackLaunchers(plan){
|
|
|
1235
1236
|
const UPDATE_PHASE_RANK=Object.freeze({
|
|
1236
1237
|
initial:0,candidate_prepared:10,old_runtime_verified:20,shutdown_handoff_armed:25,supervisor_spawned:30,
|
|
1237
1238
|
authority_revoked:40,old_runtime_stopped:50,workspace_ready:60,launcher_installed:70,pointer_committed:80,
|
|
1238
|
-
fresh_runtime_verified:90,authority_verified:100,completed:110,superseded:120,failed:1000,failed_receipted:1001,failed_unreceipted:1002
|
|
1239
|
+
fresh_runtime_verified:90,authority_verified:100,completed:110,superseded:120,failed:1000,failed_receipted:1001,failed_unreceipted:1002,precondition_rejected:1003
|
|
1239
1240
|
});
|
|
1240
1241
|
function phaseRank(phase){ return UPDATE_PHASE_RANK[String(phase)]??-1; }
|
|
1241
1242
|
function activeTransitionPhase(phase){
|
|
@@ -1657,6 +1658,9 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
|
|
|
1657
1658
|
}
|
|
1658
1659
|
|
|
1659
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=[]}){
|
|
1661
|
+
// R64 grants have no signed revocation epoch. The historical boolean restore
|
|
1662
|
+
// cannot prove continuity, and must never materialize a successor grant.
|
|
1663
|
+
if(restoreStandingGrants)throw new Error('R65 standing grant auto-restore disabled: explicit reauthorization required');
|
|
1660
1664
|
stateRoot=resolveStateRoot(stateRoot);
|
|
1661
1665
|
if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
|
|
1662
1666
|
const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
|
|
@@ -1669,12 +1673,17 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1669
1673
|
await writeState(statePath,journal,'initial');
|
|
1670
1674
|
let pointerInfo=null,control=null,predecessor=null,oldRuntime=null,priorStatus=null,authorityPreparation=null,workspace=null,launchers=null,newPointer=null;
|
|
1671
1675
|
let stableMcpUrl=null,mcpUrlSnapshot=null;
|
|
1676
|
+
let mutationBegan=false;
|
|
1672
1677
|
try{
|
|
1673
1678
|
pointerInfo=await loadPointerIfPresent(state);
|
|
1674
1679
|
const stageVerified=await verifyPackageRoot(stageRoot,expectedVersion,expectedManifest,'stage');
|
|
1675
1680
|
const developmentVerified=await verifyPackageRoot(development,expectedVersion,expectedManifest,'development');
|
|
1676
|
-
const slot=await materializeReleaseSlot({source:stageRoot,stateRoot:state,version:expectedVersion,manifestSha256:expectedManifest});
|
|
1677
1681
|
control=await loadControlInputs({stateRoot:state,sourceRoot,pointerInfo,bridgeBootstrap});
|
|
1682
|
+
// Read-only predecessor authority and process identity must be safe before
|
|
1683
|
+
// materializing a slot or changing any stable runtime file.
|
|
1684
|
+
await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});
|
|
1685
|
+
mutationBegan=true;
|
|
1686
|
+
const slot=await materializeReleaseSlot({source:stageRoot,stateRoot:state,version:expectedVersion,manifestSha256:expectedManifest});
|
|
1678
1687
|
const hostBootstrap=await prepareHostPolicyBootstrap(hostPolicyBootstrap);if(hostBootstrap) control.hostPolicy=hostBootstrap.policy;
|
|
1679
1688
|
const codingBootstrap=await prepareCodingPolicyBootstrap(codingPolicyBootstrap);if(codingBootstrap) control.codingPolicy=codingBootstrap.policy;
|
|
1680
1689
|
const processBootstrap=await prepareProcessPolicyBootstrap(processPolicyBootstrap);
|
|
@@ -1722,7 +1731,7 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1722
1731
|
bridge:{working_directory:control.config.Bridge.WorkingDirectory,script_path:control.config.Bridge.ScriptPath,script_sha256:control.config.Bridge.ScriptSha256},
|
|
1723
1732
|
host_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.hostRepinned},coding_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.codingRepinned},
|
|
1724
1733
|
new_pointer:publicPointer(newPointer)});
|
|
1725
|
-
predecessor=await capturePredecessor(control.ctx,recoverOfflinePredecessor);oldRuntime=predecessor.runtime;priorStatus=predecessor.status;
|
|
1734
|
+
predecessor=await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});oldRuntime=predecessor.runtime;priorStatus=predecessor.status;
|
|
1726
1735
|
await writeState(statePath,journal,'old_runtime_verified',{predecessor_mode:predecessor.mode,old_runtime:generationTuple(oldRuntime),
|
|
1727
1736
|
prior_authority:{process_armed:priorStatus.process_armed,coding_armed:priorStatus.coding_armed,autonomous_armed:priorStatus.autonomous_armed,desktop_armed:priorStatus.desktop_armed,host_armed:priorStatus.host_armed}});
|
|
1728
1737
|
const handoff=await writeHandoff({stateRoot:state,state:journal,journal,statePath,evidenceDir,key,pointerInfo,newPointer,predecessor,priorStatus,workspace,launchers,stableMcpUrl,mcpUrlSnapshot,controlCtx:control.ctx});
|
|
@@ -1730,12 +1739,13 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1730
1739
|
return {ok:true,handed_off:true,completed:false,update_id:updateId,state_path:statePath,handoff_path:handoff.path,handoff_receipt_sha256:handoff.receipt.receipt_sha256,supervisor_pid:supervisor.pid,pointer:newPointer};
|
|
1731
1740
|
}catch(error){
|
|
1732
1741
|
const failure={schema:FAILURE_SCHEMA,update_id:updateId,release_version:expectedVersion,candidate_manifest_sha256:expectedManifest,failed_at_utc:new Date().toISOString(),
|
|
1742
|
+
result:mutationBegan?'MUTATION_OUTCOME_REQUIRES_RECONCILIATION':'PRECONDITION_REJECTED',
|
|
1733
1743
|
phase:journal.phase??'unknown',error:{name:error?.name??'Error',code:error?.code??null,message:String(error?.message??error)},
|
|
1734
1744
|
predecessor_mode:predecessor?.mode??null,prior_pointer:publicPointer(pointerInfo?.pointer??null),attempted_pointer:publicPointer(newPointer),old_runtime:generationTuple(oldRuntime),
|
|
1735
1745
|
rollback:{attempted:false,new_runtime_stopped:false,pointer_restored:false,launchers_restored:false,workspace_restored:false,mcp_url_restored:false,old_runtime_restarted:false,authority_restored:false,errors:[]}};
|
|
1736
1746
|
await writeState(statePath,journal,'failed',{failure}).catch(()=>{});
|
|
1737
1747
|
let failureReceipt=null;
|
|
1738
|
-
try{ failureReceipt=await writeEnvelope(failurePath,failure,key);await writeState(statePath,journal,'failed_receipted',{failure_receipt_path:failurePath,failure_receipt_sha256:failureReceipt.receipt_sha256});}
|
|
1748
|
+
try{ failureReceipt=await writeEnvelope(failurePath,failure,key);await writeState(statePath,journal,mutationBegan?'failed_receipted':'precondition_rejected',{failure_receipt_path:failurePath,failure_receipt_sha256:failureReceipt.receipt_sha256});}
|
|
1739
1749
|
catch(receiptError){ await writeState(statePath,journal,'failed_unreceipted',{failure_receipt_error:String(receiptError?.message??receiptError)}).catch(()=>{}); }
|
|
1740
1750
|
const wrapped=new Error('R27 slot update handoff failed: '+(error?.message??error));wrapped.cause=error;wrapped.statePath=statePath;wrapped.failureReceiptPath=failureReceipt?failurePath:null;throw wrapped;
|
|
1741
1751
|
}
|
package/src/version.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import os from 'node:os';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { releaseId } from '../src/release-slot.mjs';
|
|
8
8
|
import { createAuditContextProvider } from '../src/audit-context.mjs';
|
|
9
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
9
10
|
|
|
10
11
|
const H=x=>x.repeat(64);
|
|
11
12
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
@@ -19,10 +20,20 @@ test('audit context binds verified active release, generation, machine and all a
|
|
|
19
20
|
const manifestSha=sha(manifest);const generationRoot=path.join(root,'generations','g-test');await mkdir(generationRoot,{recursive:true});
|
|
20
21
|
const generationSha=H('2');const rid=releaseId('0.8.7',manifestSha);
|
|
21
22
|
await writeFile(path.join(root,'active-release.json'),JSON.stringify({schema:'deadbyte.active-release.v1',release_id:rid,version:'0.8.7',manifest_sha256:manifestSha,release_root:pkg,generation_root:generationRoot,generation_manifest_sha256:generationSha,launcher_sha256:H('3'),config_path:path.join(generationRoot,'controller-config.psd1')}));
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const processPolicyFile=path.join(root,'process-policy.json');
|
|
24
|
+
const processPolicyBytes=Buffer.from(JSON.stringify({schema:'deadbyte.process-policy.v1',enabled:true,
|
|
25
|
+
grant_file:path.join(root,'runtime','process-grant.json'),state_dir:path.join(root,'process-state'),
|
|
26
|
+
native_host:{path:process.execPath,sha256:H('a')},roots:{core:{path:root,deny_paths:[]}},
|
|
27
|
+
profiles:{dev:{executable:process.execPath,executable_sha256:H('a'),root_ids:['core'],prefix_args:[],transport:'pipe',allow_raw_input:false,max_args:8,max_arg_bytes:1024,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:4096}}}));
|
|
28
|
+
await writeFile(processPolicyFile,processPolicyBytes);
|
|
29
|
+
const processPolicy={grant_file:path.join(root,'runtime','process-grant.json'),policy_path:processPolicyFile,policy_sha256:sha(processPolicyBytes),roots:{core:{}},profiles:{dev:{}}};
|
|
30
|
+
await writeBoundStandingGrant('process',processPolicy.grant_file,{schema:'deadbyte.process-grant.v1',policy_sha256:processPolicy.policy_sha256,instance_nonce:H('5'),profile_ids:['dev'],root_ids:['core']});
|
|
31
|
+
const hostPolicyFile=path.join(root,'host-policy.json');
|
|
32
|
+
const hostPolicyBytes=Buffer.from(JSON.stringify({schema:'deadbyte.host-policy.v2',enabled:true,
|
|
33
|
+
arm_file:path.join(root,'runtime','host-grant.json'),roots:{core:{path:root,read:true,write:false,exec:false,deny_paths:[]}},commands:{},max_read_bytes:4096,max_write_bytes:4096}));
|
|
34
|
+
await writeFile(hostPolicyFile,hostPolicyBytes);
|
|
35
|
+
const hostPolicy={arm_file:path.join(root,'runtime','host-grant.json'),policy_path:hostPolicyFile,policy_sha256:sha(hostPolicyBytes)};
|
|
36
|
+
await writeBoundStandingGrant('host',hostPolicy.arm_file,{schema:'deadbyte.host-grant.v2',policy_sha256:hostPolicy.policy_sha256,instance_nonce:H('7')});
|
|
26
37
|
const machineRuntime={status:async()=>({status:'ok',release_id:rid,release_manifest_sha256:manifestSha,generation_manifest_sha256:generationSha,machine_id:H('8'),identity_generation:9,online_epoch:H('9')})};
|
|
27
38
|
const codingRuntime={sessionStatus:async()=>({status:'ok',configured:true,armed:true,reason:'standing-grant'})};
|
|
28
39
|
const autonomousRuntime={sessionStatus:async()=>({status:'ok',configured:true,armed:true,autonomous_armed:true,autonomous_reason:'standing-grant'})};
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
startLocalSupervisorHost, recoverManagedRuntime, runPinnedControllerOperation
|
|
19
19
|
} from '../src/autonomous-local-supervisor.mjs';
|
|
20
20
|
import { localSupervisorFixture } from './helpers/autonomous-r34-fixture.mjs';
|
|
21
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
21
22
|
|
|
22
23
|
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
23
24
|
const repoRoot=path.resolve(here,'..');
|
|
@@ -32,11 +33,11 @@ async function createProviderHostFixture(){
|
|
|
32
33
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
33
34
|
const codingPath=path.join(root,'coding.json');
|
|
34
35
|
await writeFile(codingPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
|
|
35
|
-
lease_file:path.join(root,'coding-grant.json'),evidence_dir:path.join(root,'coding-evidence'),
|
|
36
|
+
lease_file:path.join(root,'runtime','coding-grant.json'),evidence_dir:path.join(root,'coding-evidence'),
|
|
36
37
|
roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:[]}},commands:{}}));
|
|
37
38
|
const codingPolicy=await loadCodingPolicy(codingPath);
|
|
38
|
-
await
|
|
39
|
-
instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()})
|
|
39
|
+
await writeBoundStandingGrant('coding', codingPolicy.lease_file, {schema:'deadbyte.coding-grant.v2',policy_sha256:codingPolicy.policy_sha256,
|
|
40
|
+
instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()});
|
|
40
41
|
let round=0;
|
|
41
42
|
const planner=createServer(async(req,res)=>{ let body=''; for await(const chunk of req) body+=chunk; JSON.parse(body); round+=1;
|
|
42
43
|
const decision=round===1
|
|
@@ -47,13 +48,13 @@ async function createProviderHostFixture(){
|
|
|
47
48
|
await new Promise(resolve=>planner.listen(0,'127.0.0.1',resolve));
|
|
48
49
|
const autonomousPath=path.join(root,'autonomous.json');
|
|
49
50
|
await writeFile(autonomousPath,JSON.stringify({schema:'deadbyte.autonomous-policy.v1',enabled:true,
|
|
50
|
-
lease_file:path.join(root,'autonomous-grant.json'),evidence_dir:path.join(root,'autonomous-evidence'),
|
|
51
|
+
lease_file:path.join(root,'runtime','autonomous-grant.json'),evidence_dir:path.join(root,'autonomous-evidence'),
|
|
51
52
|
coding_policy_sha256:codingPolicy.policy_sha256,root_ids:['project'],
|
|
52
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},
|
|
53
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:{}}));
|
|
54
55
|
const autonomousPolicy=await loadAutonomousPolicy(autonomousPath,codingPolicy);
|
|
55
|
-
await
|
|
56
|
-
instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()})
|
|
56
|
+
await writeBoundStandingGrant('autonomous', autonomousPolicy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
|
|
57
|
+
instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()});
|
|
57
58
|
const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
58
59
|
const nativeDiffRuntime=createNativeDiffRuntime({policy:autonomousPolicy,codingRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
59
60
|
const runtime=createAutonomousRuntime({policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
@@ -10,6 +10,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
10
10
|
import { Client } from '@modelcontextprotocol/client';
|
|
11
11
|
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
|
|
12
12
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
13
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
13
14
|
|
|
14
15
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const repoRoot = path.resolve(here, '..');
|
|
@@ -52,18 +53,18 @@ async function startPlanner() {
|
|
|
52
53
|
const codingPath = path.join(root, 'coding.json');
|
|
53
54
|
await writeFile(codingPath, JSON.stringify({
|
|
54
55
|
schema:'deadbyte.coding-policy.v1', enabled:true,
|
|
55
|
-
lease_file:path.join(root, 'coding-grant.json'), evidence_dir:path.join(root, 'coding-evidence'),
|
|
56
|
+
lease_file:path.join(root, 'runtime', 'coding-grant.json'), evidence_dir:path.join(root, 'coding-evidence'),
|
|
56
57
|
roots:{ project:{ path:project, read:true, write:true, exec:false, deny_paths:[] } }, commands:{}
|
|
57
58
|
}));
|
|
58
59
|
const codingSha = sha(await readFile(codingPath));
|
|
59
|
-
await
|
|
60
|
+
await writeBoundStandingGrant('coding', path.join(root, 'runtime', 'coding-grant.json'), {
|
|
60
61
|
schema:'deadbyte.coding-grant.v2', policy_sha256:codingSha,
|
|
61
62
|
instance_nonce:nonce(), root_ids:['project'], granted_at_utc:new Date().toISOString()
|
|
62
|
-
})
|
|
63
|
+
});
|
|
63
64
|
const planner = await startPlanner();
|
|
64
65
|
const autonomousPath = path.join(root, 'autonomous.json'); await writeFile(autonomousPath, JSON.stringify({
|
|
65
66
|
schema:'deadbyte.autonomous-policy.v1', enabled:true,
|
|
66
|
-
lease_file:path.join(root, 'autonomous-grant.json'), evidence_dir:path.join(root, 'autonomous-evidence'),
|
|
67
|
+
lease_file:path.join(root, 'runtime', 'autonomous-grant.json'), evidence_dir:path.join(root, 'autonomous-evidence'),
|
|
67
68
|
coding_policy_sha256:codingSha, root_ids:['project'],
|
|
68
69
|
planner:{ backend:'openai_compatible', endpoint:planner.endpoint, model:'fake-supervisor', max_tokens:2048,
|
|
69
70
|
timeout_ms:30000, max_response_bytes:262144 },
|
|
@@ -71,10 +72,10 @@ async function startPlanner() {
|
|
|
71
72
|
max_context_bytes:262144, max_diff_bytes:262144, max_files_per_diff:4 }, profiles:{}
|
|
72
73
|
}));
|
|
73
74
|
const autonomousSha = sha(await readFile(autonomousPath));
|
|
74
|
-
await
|
|
75
|
+
await writeBoundStandingGrant('autonomous', path.join(root, 'runtime', 'autonomous-grant.json'), {
|
|
75
76
|
schema:'deadbyte.autonomous-grant.v2', policy_sha256:autonomousSha,
|
|
76
77
|
instance_nonce:nonce(), root_ids:['project'], granted_at_utc:new Date().toISOString()
|
|
77
|
-
})
|
|
78
|
+
});
|
|
78
79
|
const client = new Client({ name:'deadbyte-supervisor-integration', version:'0.8.0' },
|
|
79
80
|
{ versionNegotiation:{ mode:{ pin:'2026-07-28' } } });
|
|
80
81
|
const transport = new StdioClientTransport({
|
|
@@ -148,4 +149,4 @@ test('external supervisor mode leaves provider goal durable until standalone hos
|
|
|
148
149
|
await new Promise(resolve=>f.planner.server.close(resolve));
|
|
149
150
|
await rm(f.root,{recursive:true,force:true});
|
|
150
151
|
}
|
|
151
|
-
});
|
|
152
|
+
});
|
|
@@ -2,6 +2,7 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
5
6
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
6
7
|
import {
|
|
7
8
|
CAPABILITY_STATES,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
resolveCapabilityStates
|
|
10
11
|
} from '../src/capability-resolver.mjs';
|
|
11
12
|
import { R36_CAPABILITY_LEDGER } from '../src/capability-ledger.mjs';
|
|
13
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
12
14
|
|
|
13
15
|
test('capability resolver enforces 5 canonical states', () => {
|
|
14
16
|
assert.equal(CAPABILITY_STATES.AVAILABLE, 'AVAILABLE');
|
|
@@ -187,14 +189,18 @@ test('capability resolver reports actual disarmed authority across coding proces
|
|
|
187
189
|
test('capability resolver reports armed Host capability when the standing grant is valid', async () => {
|
|
188
190
|
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-capability-host-'));
|
|
189
191
|
try{
|
|
190
|
-
const armFile=path.join(root,'host-grant.json');
|
|
191
|
-
const
|
|
192
|
-
|
|
192
|
+
const armFile=path.join(root,'runtime','host-grant.json');
|
|
193
|
+
const policyFile=path.join(root,'host-policy.json');
|
|
194
|
+
const policyBytes=Buffer.from(JSON.stringify({schema:'deadbyte.host-policy.v2',enabled:true,arm_file:armFile,
|
|
195
|
+
roots:{core:{path:root,read:true,write:false,exec:false,deny_paths:[]}},commands:{},max_read_bytes:4096,max_write_bytes:4096}));
|
|
196
|
+
await writeFile(policyFile,policyBytes);
|
|
197
|
+
const policySha=createHash('sha256').update(policyBytes).digest('hex');
|
|
198
|
+
await writeBoundStandingGrant('host',armFile,{
|
|
193
199
|
schema:'deadbyte.host-grant.v2',
|
|
194
200
|
policy_sha256:policySha,
|
|
195
201
|
instance_nonce:'c'.repeat(64),
|
|
196
202
|
armed_at_utc:new Date().toISOString()
|
|
197
|
-
})
|
|
203
|
+
});
|
|
198
204
|
const result=await resolveCapabilityStates({
|
|
199
205
|
ledger:{entries:[{
|
|
200
206
|
id:'host.file_read',compact_tool:'host_file_read',full_tool:'host_file_read',compact_op:'direct',
|
|
@@ -203,7 +209,7 @@ test('capability resolver reports armed Host capability when the standing grant
|
|
|
203
209
|
toolCatalog:['host_file_read'],
|
|
204
210
|
profile:'compact',
|
|
205
211
|
env:{},
|
|
206
|
-
hostPolicy:{arm_file:armFile,policy_sha256:policySha}
|
|
212
|
+
hostPolicy:{arm_file:armFile,policy_path:policyFile,policy_sha256:policySha}
|
|
207
213
|
});
|
|
208
214
|
const hostRead=result.capabilities[0];
|
|
209
215
|
assert.equal(hostRead.state,CAPABILITY_STATES.AVAILABLE);
|
|
@@ -10,6 +10,7 @@ import { loadAutonomousPolicy } from '../src/autonomous-policy.mjs';
|
|
|
10
10
|
import { createNativeDiffRuntime } from '../src/native-diff.mjs';
|
|
11
11
|
import { createAutonomousRuntime } from '../src/autonomous-runtime.mjs';
|
|
12
12
|
import { providerCircuitId } from '../src/provider-circuit.mjs';
|
|
13
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
13
14
|
|
|
14
15
|
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
15
16
|
const nonce=()=>randomBytes(32).toString('hex');
|
|
@@ -21,13 +22,13 @@ async function fixture(){
|
|
|
21
22
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
22
23
|
const codingPath=path.join(root,'coding.json');
|
|
23
24
|
await writeFile(codingPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
|
|
24
|
-
lease_file:path.join(root,'coding-grant.json'),evidence_dir:path.join(root,'coding-evidence'),
|
|
25
|
+
lease_file:path.join(root,'runtime','coding-grant.json'),evidence_dir:path.join(root,'coding-evidence'),
|
|
25
26
|
roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:[]}},commands:{}},null,2));
|
|
26
27
|
const codingPolicy=await loadCodingPolicy(codingPath);
|
|
27
28
|
const nodeSha=sha(await readFile(process.execPath));
|
|
28
29
|
const autonomousPath=path.join(root,'autonomous.json');
|
|
29
30
|
await writeFile(autonomousPath,JSON.stringify({schema:'deadbyte.autonomous-policy.v1',enabled:true,
|
|
30
|
-
lease_file:path.join(root,'autonomous-grant.json'),evidence_dir:path.join(root,'autonomous-evidence'),
|
|
31
|
+
lease_file:path.join(root,'runtime','autonomous-grant.json'),evidence_dir:path.join(root,'autonomous-evidence'),
|
|
31
32
|
coding_policy_sha256:codingPolicy.policy_sha256,root_ids:['project'],
|
|
32
33
|
planner:{backend:'codex_app_server',model:'gpt-test',root_id:'project',executable:process.execPath,
|
|
33
34
|
executable_sha256:nodeSha,args:['app-server'],max_tokens:4096,timeout_ms:30000,
|
|
@@ -35,10 +36,10 @@ async function fixture(){
|
|
|
35
36
|
profiles:{},limits:{max_iterations:8,max_mutations:4,max_wall_ms:600000,max_context_bytes:262144,
|
|
36
37
|
max_diff_bytes:262144,max_files_per_diff:4,max_stalled_cycles:2,max_post_replan_stalls:2,max_replans:4}},null,2));
|
|
37
38
|
const policy=await loadAutonomousPolicy(autonomousPath,codingPolicy);
|
|
38
|
-
await
|
|
39
|
-
instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()})
|
|
40
|
-
await
|
|
41
|
-
instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()})
|
|
39
|
+
await writeBoundStandingGrant('coding', codingPolicy.lease_file, {schema:'deadbyte.coding-grant.v2',policy_sha256:codingPolicy.policy_sha256,
|
|
40
|
+
instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()});
|
|
41
|
+
await writeBoundStandingGrant('autonomous', policy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:policy.policy_sha256,
|
|
42
|
+
instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()});
|
|
42
43
|
const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
43
44
|
const nativeDiffRuntime=createNativeDiffRuntime({policy,codingRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
44
45
|
return {root,project,keys,codingPolicy,policy,codingRuntime,nativeDiffRuntime,cleanup:()=>rm(root,{recursive:true,force:true})};
|
|
@@ -6,6 +6,7 @@ import os from 'node:os';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
8
8
|
import { createCodingRuntime, loadCodingPolicy } from '../src/coding-plane.mjs';
|
|
9
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
9
10
|
|
|
10
11
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
11
12
|
|
|
@@ -18,7 +19,7 @@ async function fixture() {
|
|
|
18
19
|
await writeFile(path.join(project, 'src', 'b.txt'), 'beta NEEDLE two\n');
|
|
19
20
|
await writeFile(path.join(project, 'node_modules', 'hidden.txt'), 'needle hidden\n');
|
|
20
21
|
const policyPath = path.join(root, 'coding-policy.json');
|
|
21
|
-
const leasePath = path.join(root, 'coding-grant.json');
|
|
22
|
+
const leasePath = path.join(root, 'runtime', 'coding-grant.json');
|
|
22
23
|
await writeFile(policyPath, JSON.stringify({
|
|
23
24
|
schema: 'deadbyte.coding-policy.v1', enabled: true,
|
|
24
25
|
lease_file: leasePath, evidence_dir: path.join(root, 'evidence'),
|
|
@@ -26,10 +27,10 @@ async function fixture() {
|
|
|
26
27
|
commands: {}
|
|
27
28
|
}));
|
|
28
29
|
const policy = await loadCodingPolicy(policyPath);
|
|
29
|
-
await
|
|
30
|
+
await writeBoundStandingGrant('coding', leasePath, {
|
|
30
31
|
schema: 'deadbyte.coding-grant.v2', policy_sha256: policy.policy_sha256,
|
|
31
32
|
instance_nonce: 'a'.repeat(64), root_ids: ['project'], granted_at_utc: new Date().toISOString()
|
|
32
|
-
})
|
|
33
|
+
});
|
|
33
34
|
const keys = await generateTrustKeypair({
|
|
34
35
|
privateKeyPath: path.join(root, 'private.pem'), publicKeyPath: path.join(root, 'public.pem')
|
|
35
36
|
});
|
|
@@ -10,6 +10,7 @@ import { createMachineFsRuntime, verifyMachineFsReceiptFile } from '../src/machi
|
|
|
10
10
|
import { PROCESS_GRANT_SCHEMA, PROCESS_POLICY_SCHEMA, loadProcessPolicy } from '../src/process-policy.mjs';
|
|
11
11
|
import { createProcessRuntime } from '../src/process-runtime.mjs';
|
|
12
12
|
import { createCommanderWireAdapter } from '../src/commander-wire-adapter-r47.mjs';
|
|
13
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
13
14
|
|
|
14
15
|
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
15
16
|
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
|
|
@@ -17,13 +18,13 @@ const sleep=ms=>new Promise(r=>setTimeout(r,ms));
|
|
|
17
18
|
async function fsFixture(){
|
|
18
19
|
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-commander-real-fs-'));
|
|
19
20
|
const project=path.join(root,'project'); await mkdir(project);
|
|
20
|
-
const policyPath=path.join(root,'policy.json'), grantPath=path.join(root,'grant.json');
|
|
21
|
+
const policyPath=path.join(root,'policy.json'), grantPath=path.join(root,'runtime','grant.json');
|
|
21
22
|
const policy=await (async()=>{await writeFile(policyPath,JSON.stringify({
|
|
22
23
|
schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:grantPath,evidence_dir:path.join(root,'evidence'),
|
|
23
24
|
roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:['blocked']}},
|
|
24
25
|
commands:{},max_read_bytes:65536,max_write_bytes:65536
|
|
25
26
|
})); return loadCodingPolicy(policyPath)})();
|
|
26
|
-
await
|
|
27
|
+
await writeBoundStandingGrant('coding', grantPath, {schema:'deadbyte.coding-grant.v2',policy_sha256:policy.policy_sha256,instance_nonce:'b'.repeat(64),root_ids:['project'],granted_at_utc:new Date().toISOString()});
|
|
27
28
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
28
29
|
return {root,project,policy,keys,runtime:createMachineFsRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath})};
|
|
29
30
|
}
|
|
@@ -34,7 +35,7 @@ async function processFixture(){
|
|
|
34
35
|
const host=path.resolve('bin/deadbyte-process-host.exe'), exe='C:\\Windows\\System32\\cmd.exe';
|
|
35
36
|
const powershell='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
36
37
|
const h=sha(await readFile(host)), e=sha(await readFile(exe)),ps=sha(await readFile(powershell));
|
|
37
|
-
const grantFile=path.join(root,'grant.json'), stateDir=path.join(root,'state'), policyPath=path.join(root,'policy.json');
|
|
38
|
+
const grantFile=path.join(root,'runtime','grant.json'), stateDir=path.join(root,'state'), policyPath=path.join(root,'policy.json');
|
|
38
39
|
await writeFile(policyPath,JSON.stringify({schema:PROCESS_POLICY_SCHEMA,enabled:true,grant_file:grantFile,state_dir:stateDir,
|
|
39
40
|
native_host:{path:host,sha256:h},roots:{core:{path:project,deny_paths:[]}},profiles:{
|
|
40
41
|
echo:{executable:exe,executable_sha256:e,root_ids:['core'],prefix_args:['/d','/c'],max_args:4,max_arg_bytes:256,max_sessions:2,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}},
|
|
@@ -42,7 +43,7 @@ async function processFixture(){
|
|
|
42
43
|
powershell_repl:{executable:powershell,executable_sha256:ps,root_ids:['core'],transport:'pipe',prefix_args:['-NoLogo','-NoProfile','-NonInteractive','-Command','-'],allow_raw_input:true,max_args:0,max_arg_bytes:256,max_sessions:2,max_input_bytes:8192,max_retained_output_bytes:2*1024*1024,env_allow:['PATH','TEMP','TMP'],input_actions:{}}
|
|
43
44
|
}}));
|
|
44
45
|
const policy=await loadProcessPolicy(policyPath);
|
|
45
|
-
await
|
|
46
|
+
await writeBoundStandingGrant('process', grantFile, {schema:PROCESS_GRANT_SCHEMA,policy_sha256:policy.policy_sha256,instance_nonce:'a'.repeat(64),profile_ids:['echo','hold','powershell_repl'],root_ids:['core'],granted_at_utc:new Date().toISOString()});
|
|
46
47
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
47
48
|
return {root,policy,keys,runtime:createProcessRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath})};
|
|
48
49
|
}
|
|
@@ -9,6 +9,7 @@ import { createMachineFsRuntime } from '../src/machine-fs.mjs';
|
|
|
9
9
|
import { PROCESS_GRANT_SCHEMA, PROCESS_POLICY_SCHEMA, loadProcessPolicy } from '../src/process-policy.mjs';
|
|
10
10
|
import { createProcessRuntime } from '../src/process-runtime.mjs';
|
|
11
11
|
import { createHash } from 'node:crypto';
|
|
12
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
12
13
|
|
|
13
14
|
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
14
15
|
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
|
|
@@ -16,10 +17,10 @@ const sleep=ms=>new Promise(r=>setTimeout(r,ms));
|
|
|
16
17
|
async function fsFixture(){
|
|
17
18
|
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-torture-fs-')), project=path.join(root,'project');
|
|
18
19
|
await mkdir(project);
|
|
19
|
-
const policyPath=path.join(root,'policy.json'), grantPath=path.join(root,'grant.json');
|
|
20
|
+
const policyPath=path.join(root,'policy.json'), grantPath=path.join(root,'runtime','grant.json');
|
|
20
21
|
await writeFile(policyPath,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:grantPath,evidence_dir:path.join(root,'evidence'),roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:['blocked']}},commands:{},max_read_bytes:65536,max_write_bytes:65536}));
|
|
21
22
|
const policy=await loadCodingPolicy(policyPath);
|
|
22
|
-
await
|
|
23
|
+
await writeBoundStandingGrant('coding', grantPath, {schema:'deadbyte.coding-grant.v2',policy_sha256:policy.policy_sha256,instance_nonce:'c'.repeat(64),root_ids:['project'],granted_at_utc:new Date().toISOString()});
|
|
23
24
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
24
25
|
return {root,project,runtime:createMachineFsRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath})};
|
|
25
26
|
}
|
|
@@ -28,13 +29,13 @@ async function processFixture(){
|
|
|
28
29
|
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-torture-proc-')), project=path.join(root,'project'),trust=path.join(root,'trust');
|
|
29
30
|
await mkdir(project); await mkdir(trust);
|
|
30
31
|
const host=path.resolve('bin/deadbyte-process-host.exe'), exe='C:\\Windows\\System32\\cmd.exe';
|
|
31
|
-
const grantFile=path.join(root,'grant.json'), stateDir=path.join(root,'state'), policyPath=path.join(root,'policy.json');
|
|
32
|
+
const grantFile=path.join(root,'runtime','grant.json'), stateDir=path.join(root,'state'), policyPath=path.join(root,'policy.json');
|
|
32
33
|
await writeFile(policyPath,JSON.stringify({schema:PROCESS_POLICY_SCHEMA,enabled:true,grant_file:grantFile,state_dir:stateDir,native_host:{path:host,sha256:sha(await readFile(host))},roots:{core:{path:project,deny_paths:[]}},profiles:{
|
|
33
34
|
echo:{executable:exe,executable_sha256:sha(await readFile(exe)),root_ids:['core'],prefix_args:['/d','/c'],max_args:4,max_arg_bytes:256,max_sessions:8,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}},
|
|
34
35
|
hold:{executable:exe,executable_sha256:sha(await readFile(exe)),root_ids:['core'],transport:'pipe',prefix_args:['/d','/q'],max_args:0,max_arg_bytes:256,max_sessions:8,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}}
|
|
35
36
|
}}));
|
|
36
37
|
const policy=await loadProcessPolicy(policyPath);
|
|
37
|
-
await
|
|
38
|
+
await writeBoundStandingGrant('process', grantFile, {schema:PROCESS_GRANT_SCHEMA,policy_sha256:policy.policy_sha256,instance_nonce:'d'.repeat(64),profile_ids:['echo','hold'],root_ids:['core'],granted_at_utc:new Date().toISOString()});
|
|
38
39
|
const keys=await generateTrustKeypair({privateKeyPath:path.join(trust,'private.pem'),publicKeyPath:path.join(trust,'public.pem')});
|
|
39
40
|
return {root,runtime:createProcessRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath})};
|
|
40
41
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtemp,writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdtemp,mkdir,writeFile } from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
@@ -8,12 +8,14 @@ import {
|
|
|
8
8
|
DESKTOP_POLICY_SCHEMA,DESKTOP_GRANT_SCHEMA,DESKTOP_ACTION_KINDS,
|
|
9
9
|
loadDesktopPolicy,desktopSessionStatus,assertDesktopArmed
|
|
10
10
|
} from '../src/desktop-policy-r40.mjs';
|
|
11
|
+
import { writeBoundStandingGrant } from './r65-authority-fixture.mjs';
|
|
11
12
|
|
|
12
13
|
const H=value=>createHash('sha256').update(String(value)).digest('hex');
|
|
13
14
|
|
|
14
15
|
async function fixture(){
|
|
15
16
|
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r40-desktop-policy-'));
|
|
16
|
-
|
|
17
|
+
await mkdir(path.join(root,'runtime'));
|
|
18
|
+
const grant=path.join(root,'runtime','desktop-grant.json');
|
|
17
19
|
const evidence=path.join(root,'evidence');
|
|
18
20
|
const observer=path.join(root,'observer.exe');
|
|
19
21
|
const input=path.join(root,'input.exe');
|
|
@@ -57,10 +59,10 @@ test('desktop plane is fail-closed when no grant exists',async()=>{
|
|
|
57
59
|
|
|
58
60
|
test('desktop standing grant is policy-hash-bound and action-scoped',async()=>{
|
|
59
61
|
const {policy,grant}=await fixture();
|
|
60
|
-
await
|
|
62
|
+
await writeBoundStandingGrant('desktop', grant, {
|
|
61
63
|
schema:DESKTOP_GRANT_SCHEMA,policy_sha256:policy.policy_sha256,instance_nonce:H('nonce'),
|
|
62
64
|
action_kinds:['pointer_move','click_primary']
|
|
63
|
-
})
|
|
65
|
+
});
|
|
64
66
|
const status=await desktopSessionStatus(policy);
|
|
65
67
|
assert.equal(status.armed,true);
|
|
66
68
|
assert.equal(status.reason,'standing-grant');
|