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.
Files changed (89) hide show
  1. package/DEADBYTE-MCP.cmd +8 -4
  2. package/MANIFEST.SHA256 +88 -43
  3. package/PROVENANCE.json +66 -15
  4. package/README.txt +27 -18
  5. package/RELEASE.json +55 -15
  6. package/SBOM.cdx.json +2 -2
  7. package/TEST-MATRIX.json +117 -14
  8. package/bridge/package.json +1 -1
  9. package/controller/deadbyte-controller.ps1 +132 -12
  10. package/controller/deadbyte-process-policy.json +4 -4
  11. package/docs/R65-AUTHORITY-CONTINUITY-DESIGN.json +27 -0
  12. package/docs/R65-REFERENCE-GAP-MATRIX.json +22 -0
  13. package/package.json +6 -4
  14. package/scripts/deploy-r48.mjs +123 -7
  15. package/scripts/gate-windows-r65-prepublish.ps1 +65 -0
  16. package/scripts/gate-windows-r66-prepublish.ps1 +65 -0
  17. package/scripts/npm-publication-r65.mjs +139 -0
  18. package/scripts/npm-release-r48.mjs +56 -37
  19. package/scripts/r65-canonical-full.mjs +45 -0
  20. package/scripts/r65-focused.mjs +37 -0
  21. package/scripts/verify-r65-parent-source.mjs +75 -0
  22. package/src/activation-continuity-engine-r65.mjs +225 -0
  23. package/src/activation-lock-r65.mjs +155 -0
  24. package/src/activation-preflight-r65.mjs +197 -0
  25. package/src/authority-continuity-r65.mjs +541 -0
  26. package/src/authority-epoch-cli-r65.mjs +10 -0
  27. package/src/autonomous-policy.mjs +7 -3
  28. package/src/coding-plane.mjs +12 -3
  29. package/src/desktop-policy-r40.mjs +6 -3
  30. package/src/doctor-runtime-r58.mjs +3 -1
  31. package/src/doctor-transactions-r58.mjs +1 -1
  32. package/src/host-plane.mjs +9 -3
  33. package/src/process-policy.mjs +7 -4
  34. package/src/remote-authority.mjs +10 -1
  35. package/src/runtime-update.mjs +15 -5
  36. package/src/version.mjs +1 -1
  37. package/test/audit-context.test.mjs +15 -4
  38. package/test/autonomous-local-supervisor.test.mjs +7 -6
  39. package/test/autonomous-supervisor-integration.test.mjs +8 -7
  40. package/test/capability-resolver.test.mjs +11 -5
  41. package/test/codex-autonomous-r44.test.mjs +7 -6
  42. package/test/coding-observation.test.mjs +4 -3
  43. package/test/commander-real-parity-r47.test.mjs +5 -4
  44. package/test/commander-torture-r47.test.mjs +5 -4
  45. package/test/desktop-policy-r40.test.mjs +6 -4
  46. package/test/desktop-runtime-r40.test.mjs +8 -3
  47. package/test/host-inline-r45.test.mjs +4 -3
  48. package/test/machine-fs-runtime.test.mjs +4 -3
  49. package/test/process-longrun-r36.test.mjs +3 -2
  50. package/test/process-policy.test.mjs +10 -6
  51. package/test/process-runtime.test.mjs +9 -6
  52. package/test/r52-host-revocation.test.mjs +1 -0
  53. package/test/r58-doctor-runtime.test.mjs +18 -0
  54. package/test/r59-tool-surface-compat.test.mjs +1 -1
  55. package/test/r64-release-contract.test.mjs +2 -1
  56. package/test/r65-activation-lock-recovery.test.mjs +72 -0
  57. package/test/r65-activation-lock.test.mjs +59 -0
  58. package/test/r65-activation-orchestration.test.mjs +179 -0
  59. package/test/r65-activation-preflight.test.mjs +35 -0
  60. package/test/r65-authority-continuity-decision.test.mjs +58 -0
  61. package/test/r65-authority-durable-policy-binding.test.mjs +34 -0
  62. package/test/r65-authority-epoch-cli.test.mjs +125 -0
  63. package/test/r65-authority-epoch-races.test.mjs +87 -0
  64. package/test/r65-authority-epoch-recovery.test.mjs +82 -0
  65. package/test/r65-authority-epoch-store.test.mjs +94 -0
  66. package/test/r65-authority-fixture.mjs +41 -0
  67. package/test/r65-authority-grant-capture.test.mjs +37 -0
  68. package/test/r65-authority-observation-epoch.test.mjs +25 -0
  69. package/test/r65-authority-policy-envelope.test.mjs +46 -0
  70. package/test/r65-authority-restored-safe.test.mjs +75 -0
  71. package/test/r65-authority-runtime-epoch.test.mjs +115 -0
  72. package/test/r65-authority-runtime-policy-binding.test.mjs +29 -0
  73. package/test/r65-authority-test-trust.mjs +20 -0
  74. package/test/r65-cli-invocation.test.mjs +38 -0
  75. package/test/r65-controller-authority-epoch-live.test.mjs +100 -0
  76. package/test/r65-controller-authority-epoch.test.mjs +37 -0
  77. package/test/r65-deploy-preflight.test.mjs +55 -0
  78. package/test/r65-epoch-native-activation-integration.test.mjs +141 -0
  79. package/test/r65-epoch-native-continuity-engine.test.mjs +112 -0
  80. package/test/r65-epoch-native-cross-plane.test.mjs +95 -0
  81. package/test/r65-legacy-authority-migration.test.mjs +63 -0
  82. package/test/r65-legacy-auto-restore-rejection.test.mjs +17 -0
  83. package/test/r65-npm-publication.test.mjs +114 -0
  84. package/test/r65-parent-source-verifier.test.mjs +63 -0
  85. package/test/r65-release-contract.test.mjs +75 -0
  86. package/test/r65-transaction-precondition.test.mjs +36 -0
  87. package/test/r66-release-contract.test.mjs +56 -0
  88. package/test/remote-session-cli.test.mjs +6 -6
  89. 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{grant=JSON.parse(decodeUtf8Strict(await readFile(policy.grant_file)));}
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
- object(item,['configured','armed','reason']);
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
  }
@@ -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
- arm = JSON.parse(decodeUtf8Strict(await readFile(policy.arm_file)));
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) return { arm, standing:true, remaining_ms:null };
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() };
@@ -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(await readFile(policy.grant_file))); }
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
+ }
@@ -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
- authority[plane]=Object.freeze({configured:entry.configured,armed:entry.armed,reason:entry.reason.slice(0,128)});
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
  }
@@ -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
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.41';
1
+ export const DEADBYTE_VERSION = '0.15.43';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -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 processPolicy={grant_file:path.join(root,'process-grant.json'),policy_sha256:H('4'),roots:{core:{}},profiles:{dev:{}}};
23
- await writeFile(processPolicy.grant_file,JSON.stringify({schema:'deadbyte.process-grant.v1',policy_sha256:processPolicy.policy_sha256,instance_nonce:H('5'),profile_ids:['dev'],root_ids:['core']}));
24
- const hostPolicy={arm_file:path.join(root,'host-grant.json'),policy_sha256:H('6')};
25
- await writeFile(hostPolicy.arm_file,JSON.stringify({schema:'deadbyte.host-grant.v2',policy_sha256:hostPolicy.policy_sha256,instance_nonce:H('7')}));
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 writeFile(codingPolicy.lease_file,JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:codingPolicy.policy_sha256,
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 writeFile(autonomousPolicy.lease_file,JSON.stringify({schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
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 writeFile(path.join(root, 'coding-grant.json'), JSON.stringify({
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 writeFile(path.join(root, 'autonomous-grant.json'), JSON.stringify({
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 policySha='b'.repeat(64);
192
- await writeFile(armFile,JSON.stringify({
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 writeFile(codingPolicy.lease_file,JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:codingPolicy.policy_sha256,
39
- instance_nonce:nonce(),root_ids:['project'],armed_at_utc:new Date().toISOString()}));
40
- await writeFile(policy.lease_file,JSON.stringify({schema:'deadbyte.autonomous-grant.v2',policy_sha256:policy.policy_sha256,
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 writeFile(leasePath, JSON.stringify({
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 writeFile(grantPath,JSON.stringify({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
+ 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 writeFile(grantFile,JSON.stringify({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
+ 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 writeFile(grantPath,JSON.stringify({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
+ 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 writeFile(grantFile,JSON.stringify({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
+ 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
- const grant=path.join(root,'desktop-grant.json');
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 writeFile(grant,JSON.stringify({
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');