deadbyte-mcp 0.15.0 → 0.15.1

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 (77) hide show
  1. package/MANIFEST.SHA256 +76 -41
  2. package/PROVENANCE.json +10 -0
  3. package/R45-PARENT.json +18 -0
  4. package/README.txt +14 -14
  5. package/RELEASE.json +37 -0
  6. package/SBOM.cdx.json +46 -0
  7. package/TEST-MATRIX.json +31 -0
  8. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  9. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  10. package/controller/README.TXT +2 -2
  11. package/controller/deadbyte-controller.ps1 +2 -2
  12. package/docs/ARCHITECTURE.md +1 -1
  13. package/native/deadbyte-hardware-observer.cpp +308 -0
  14. package/package.json +12 -7
  15. package/scripts/autonomous-tests.ps1 +2 -2
  16. package/scripts/build-hardware-r45.ps1 +71 -0
  17. package/scripts/deferred-slot-operation-r45.mjs +126 -0
  18. package/scripts/derive-r45-parent.mjs +24 -0
  19. package/scripts/derive-r45-release.mjs +72 -0
  20. package/scripts/derive-r45-tests.mjs +20 -0
  21. package/scripts/final-closure-r45.mjs +121 -0
  22. package/scripts/final-closure-verify-r45.mjs +220 -0
  23. package/scripts/finalize-r45.mjs +219 -0
  24. package/scripts/full-regression-r45.mjs +20 -0
  25. package/scripts/full-regression-summary-r45.mjs +14 -0
  26. package/scripts/gate-windows-r45.ps1 +44 -0
  27. package/scripts/init-host-policy.ps1 +39 -1
  28. package/scripts/inspect-active-release-r45.mjs +20 -0
  29. package/scripts/release-manifest.mjs +3 -1
  30. package/scripts/release-parity-r45.mjs +253 -0
  31. package/scripts/release-parity-tests-r45.ps1 +8 -0
  32. package/scripts/verify-r45-parent.mjs +142 -0
  33. package/scripts/windows-gate-evidence-r45.mjs +60 -0
  34. package/src/autonomous-runtime.mjs +12 -7
  35. package/src/capability-ledger.mjs +1 -0
  36. package/src/compact-gateway-mcp-tools.mjs +4 -2
  37. package/src/deadbyte-cli.mjs +17 -9
  38. package/src/doctor-r45.mjs +65 -0
  39. package/src/host-plane.mjs +133 -85
  40. package/src/mcp-server.mjs +64 -23
  41. package/src/native-diff-v2.mjs +9 -4
  42. package/src/native-diff.mjs +29 -7
  43. package/src/observation-mcp-tools.mjs +5 -1
  44. package/src/observation-output-contracts.mjs +8 -0
  45. package/src/observation-runtime.mjs +33 -2
  46. package/src/observation-smoke-client.mjs +6 -2
  47. package/src/output-contracts.mjs +27 -11
  48. package/src/r45-compact-disarmed-probe.mjs +74 -0
  49. package/src/r45-full-surface-probe.mjs +34 -0
  50. package/src/release-generation.mjs +1 -0
  51. package/src/remote-mcp-r45-parity-client.mjs +206 -0
  52. package/src/runtime-update.mjs +36 -8
  53. package/src/version.mjs +1 -1
  54. package/test/agent-runtime.test.mjs +1 -0
  55. package/test/autonomous-loop.test.mjs +24 -0
  56. package/test/autonomous-runtime.test.mjs +1 -0
  57. package/test/cli-entrypoint.test.mjs +4 -4
  58. package/test/coding-plane.test.mjs +1 -0
  59. package/test/compact-max-catalog-r45.test.mjs +99 -0
  60. package/test/contained.test.mjs +23 -11
  61. package/test/controller.test.mjs +5 -1
  62. package/test/core.test.mjs +1 -1
  63. package/test/full-max-catalog-r45.test.mjs +91 -0
  64. package/test/hardware-observation-r45.test.mjs +44 -0
  65. package/test/host-inline-r45.test.mjs +67 -0
  66. package/test/machine-mcp-integration.test.mjs +1 -0
  67. package/test/observation-mcp-tools.test.mjs +4 -3
  68. package/test/output-schema.test.mjs +1 -0
  69. package/test/package-boundary.test.mjs +1 -1
  70. package/test/production-docs.test.mjs +3 -3
  71. package/test/r33-closeout-regression.test.mjs +20 -19
  72. package/test/r33-finalization.test.mjs +3 -3
  73. package/test/r45-autonomous-quick-test.test.mjs +10 -0
  74. package/test/r45-finalization.test.mjs +84 -0
  75. package/test/release-generation.test.mjs +1 -0
  76. package/test/release-version.test.mjs +52 -41
  77. package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
@@ -0,0 +1,65 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { lstat, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { DEADBYTE_VERSION, MCP_PROTOCOL_VERSION } from './version.mjs';
7
+
8
+ const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
9
+ const stateRoot=()=>path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
10
+ const sha256=b=>createHash('sha256').update(b).digest('hex');
11
+ async function jsonOrNull(file){try{return JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));}catch(e){if(e?.code==='ENOENT')return null;throw e;}}
12
+ async function fileHash(file){return sha256(await readFile(file));}
13
+ function parseManifest(text){
14
+ const rows=[];
15
+ for(const [index,line] of text.split(/\r?\n/).entries()){
16
+ if(!line) continue;
17
+ const m=/^([0-9a-f]{64}) (.+)$/.exec(line);
18
+ if(!m) throw new Error(`manifest line ${index+1} malformed`);
19
+ if(path.isAbsolute(m[2])||m[2].includes('..')||m[2].includes('\\')) throw new Error(`manifest line ${index+1} path unsafe`);
20
+ rows.push({sha256:m[1],relative:m[2]});
21
+ }
22
+ return rows;
23
+ }
24
+ export async function doctor(){
25
+ const manifestPath=path.join(root,'MANIFEST.SHA256');
26
+ const manifestBytes=await readFile(manifestPath);
27
+ const manifestSha256=sha256(manifestBytes);
28
+ const rows=parseManifest(manifestBytes.toString('utf8'));
29
+ let verified=0;
30
+ for(const row of rows){
31
+ const file=path.join(root,...row.relative.split('/'));
32
+ const st=await lstat(file);
33
+ if(!st.isFile()||st.isSymbolicLink()) throw new Error(`manifest entry not regular file: ${row.relative}`);
34
+ const actual=await fileHash(file);
35
+ if(actual!==row.sha256) throw new Error(`manifest hash mismatch: ${row.relative}`);
36
+ verified+=1;
37
+ }
38
+ const metadata={};
39
+ for(const name of ['RELEASE.json','TEST-MATRIX.json','PROVENANCE.json','SBOM.cdx.json']){
40
+ const file=path.join(root,name),body=await jsonOrNull(file);
41
+ if(!body) throw new Error(`required release metadata missing: ${name}`);
42
+ metadata[name]={sha256:await fileHash(file),schema:body.schema??body.bomFormat??null};
43
+ }
44
+ const release=await jsonOrNull(path.join(root,'RELEASE.json'));
45
+ if(release?.version!==DEADBYTE_VERSION) throw new Error('RELEASE.json version mismatch');
46
+ const hardware=path.join(root,'bin','deadbyte-hardware-observer-r45.exe');
47
+ const hardwareStat=await lstat(hardware);
48
+ if(!hardwareStat.isFile()||hardwareStat.isSymbolicLink()) throw new Error('hardware observer must be a regular file');
49
+ const hardwareSha256=await fileHash(hardware);
50
+ const active=await jsonOrNull(path.join(stateRoot(),'active-release.json'));
51
+ const hostPolicy=await jsonOrNull(path.join(root,'controller','deadbyte-host-policy.json'));
52
+ if(hostPolicy&&release?.train==='R45'&&process.platform==='win32'&&hostPolicy.schema!=='deadbyte.host-policy.v2') throw new Error('R45 doctor rejects non-v2 generated host policy');
53
+ return {
54
+ schema:'deadbyte.doctor.v1',status:'ok',package_version:DEADBYTE_VERSION,protocol_revision:MCP_PROTOCOL_VERSION,
55
+ platform:{current:process.platform,architecture:process.arch,primary:'win32',supported:process.platform==='win32'},
56
+ manifest:{sha256:manifestSha256,entries:rows.length,verified_entries:verified,verified:true},
57
+ release_metadata:metadata,
58
+ native:{hardware_observer:{path:'bin/deadbyte-hardware-observer-r45.exe',sha256:hardwareSha256,bytes:hardwareStat.size}},
59
+ policy:{host_schema:hostPolicy?.schema??null,host_v2_ready:hostPolicy?.schema==='deadbyte.host-policy.v2'},
60
+ active_release:active?{release_id:active.release_id,version:active.version,manifest_sha256:active.manifest_sha256,generation_manifest_sha256:active.generation_manifest_sha256,
61
+ candidate_active:active.version===DEADBYTE_VERSION&&active.manifest_sha256===manifestSha256}:null,
62
+ known_unsupported:process.platform==='win32'?[]:['native_hardware_observation','windows_controller','desktop_native']
63
+ };
64
+ }
65
+ export async function printDoctor(){const value=await doctor();process.stdout.write(JSON.stringify(value,null,2)+'\n');return value;}
@@ -16,6 +16,7 @@ import { decodeUtf8Strict } from './text-policy.mjs';
16
16
  import { isInside, validatePortableRelativePath } from './path-policy.mjs';
17
17
 
18
18
  export const HOST_POLICY_SCHEMA = 'deadbyte.host-policy.v1';
19
+ export const HOST_POLICY_SCHEMA_V2 = 'deadbyte.host-policy.v2';
19
20
  export const HOST_ARM_SCHEMA = 'deadbyte.host-arm.v1';
20
21
  export const HOST_GRANT_SCHEMA = 'deadbyte.host-grant.v2';
21
22
  export const HOST_DEFAULT_MAX_READ_BYTES = 256 * 1024;
@@ -45,7 +46,7 @@ function finiteInteger(value, name, min, max, fallback) {
45
46
 
46
47
  function normalizePolicyObject(raw, policyPath) {
47
48
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('host policy must be a JSON object');
48
- if (raw.schema !== HOST_POLICY_SCHEMA) throw new Error('host policy schema mismatch');
49
+ if (![HOST_POLICY_SCHEMA, HOST_POLICY_SCHEMA_V2].includes(raw.schema)) throw new Error('host policy schema mismatch');
49
50
  if (raw.enabled !== true) throw new Error('host policy is not enabled');
50
51
  if (!raw.arm_file || typeof raw.arm_file !== 'string' || !path.isAbsolute(raw.arm_file)) {
51
52
  throw new Error('host policy arm_file must be absolute');
@@ -75,7 +76,10 @@ function normalizePolicyObject(raw, policyPath) {
75
76
  for (const [id, value] of Object.entries(raw.commands)) {
76
77
  if (!COMMAND_ID_RE.test(id)) throw new Error(`invalid host command id '${id}'`);
77
78
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`host command '${id}' malformed`);
78
- if (value.kind !== 'script') throw new Error(`host command '${id}' kind must be script`);
79
+ const kind=value.kind;
80
+ if (kind!=='script' && !(raw.schema===HOST_POLICY_SCHEMA_V2 && kind==='inline')) {
81
+ throw new Error(`host command '${id}' kind must be script${raw.schema===HOST_POLICY_SCHEMA_V2?' or inline':''}`);
82
+ }
79
83
  if (typeof value.executable !== 'string' || !path.isAbsolute(value.executable)) {
80
84
  throw new Error(`host command '${id}' executable must be absolute`);
81
85
  }
@@ -86,37 +90,60 @@ function normalizePolicyObject(raw, policyPath) {
86
90
  if (rootIds.length === 0 || rootIds.some(rootId => !ROOT_ID_RE.test(rootId) || !roots[rootId])) {
87
91
  throw new Error(`host command '${id}' root_ids invalid`);
88
92
  }
89
- if (!Array.isArray(value.prefix_args) || value.prefix_args.some(x => typeof x !== 'string')) {
93
+ if (!Array.isArray(value.prefix_args) || value.prefix_args.some(x => typeof x !== 'string' || x.includes('\0'))) {
90
94
  throw new Error(`host command '${id}' prefix_args must be strings`);
91
95
  }
92
- if (!Array.isArray(value.extensions) || value.extensions.length === 0 ||
93
- value.extensions.some(x => typeof x !== 'string' || !/^\.[A-Za-z0-9._-]+$/.test(x))) {
94
- throw new Error(`host command '${id}' extensions invalid`);
95
- }
96
- commands[id] = {
97
- kind: 'script',
96
+ const base={
97
+ kind,
98
98
  executable: path.resolve(value.executable),
99
99
  executable_sha256: value.executable_sha256.toLowerCase(),
100
100
  root_ids: [...new Set(rootIds)],
101
101
  prefix_args: [...value.prefix_args],
102
- extensions: value.extensions.map(x => x.toLowerCase()),
103
102
  timeout_ms: finiteInteger(value.timeout_ms, `commands.${id}.timeout_ms`, 100, 300_000, HOST_DEFAULT_TIMEOUT_MS),
104
103
  max_output_bytes: finiteInteger(value.max_output_bytes, `commands.${id}.max_output_bytes`, 1024, 4 * 1024 * 1024, HOST_DEFAULT_MAX_OUTPUT_BYTES)
105
104
  };
105
+ if(kind==='script'){
106
+ if (!Array.isArray(value.extensions) || value.extensions.length === 0 ||
107
+ value.extensions.some(x => typeof x !== 'string' || !/^\.[A-Za-z0-9._-]+$/.test(x))) {
108
+ throw new Error(`host command '${id}' extensions invalid`);
109
+ }
110
+ base.extensions=value.extensions.map(x => x.toLowerCase());
111
+ } else {
112
+ base.max_command_chars=finiteInteger(value.max_command_chars,`commands.${id}.max_command_chars`,1,262144,32768);
113
+ }
114
+ commands[id]=base;
115
+ }
116
+
117
+ let nativeExec={enabled:false,root_ids:[],timeout_ms:HOST_DEFAULT_TIMEOUT_MS,max_output_bytes:HOST_DEFAULT_MAX_OUTPUT_BYTES,max_args:64,max_arg_chars:8192};
118
+ if(raw.schema===HOST_POLICY_SCHEMA_V2 && raw.native_exec!==undefined){
119
+ const value=raw.native_exec;
120
+ if(!value||typeof value!=='object'||Array.isArray(value)) throw new Error('host native_exec malformed');
121
+ if(value.enabled===true){
122
+ const rootIds=Array.isArray(value.root_ids)?value.root_ids:[];
123
+ if(rootIds.length===0||rootIds.some(rootId=>!ROOT_ID_RE.test(rootId)||!roots[rootId])) throw new Error('host native_exec root_ids invalid');
124
+ nativeExec={
125
+ enabled:true,
126
+ root_ids:[...new Set(rootIds)],
127
+ timeout_ms:finiteInteger(value.timeout_ms,'native_exec.timeout_ms',100,300000,HOST_DEFAULT_TIMEOUT_MS),
128
+ max_output_bytes:finiteInteger(value.max_output_bytes,'native_exec.max_output_bytes',1024,4*1024*1024,HOST_DEFAULT_MAX_OUTPUT_BYTES),
129
+ max_args:finiteInteger(value.max_args,'native_exec.max_args',0,64,64),
130
+ max_arg_chars:finiteInteger(value.max_arg_chars,'native_exec.max_arg_chars',1,32768,8192)
131
+ };
132
+ }
106
133
  }
107
134
 
108
135
  return {
109
- schema: HOST_POLICY_SCHEMA,
136
+ schema: raw.schema,
110
137
  enabled: true,
111
138
  policy_path: path.resolve(policyPath),
112
139
  arm_file: path.resolve(raw.arm_file),
113
140
  roots,
114
141
  commands,
142
+ native_exec:nativeExec,
115
143
  max_read_bytes: finiteInteger(raw.max_read_bytes, 'max_read_bytes', 1, 4 * 1024 * 1024, HOST_DEFAULT_MAX_READ_BYTES),
116
144
  max_write_bytes: finiteInteger(raw.max_write_bytes, 'max_write_bytes', 1, 8 * 1024 * 1024, HOST_DEFAULT_MAX_WRITE_BYTES)
117
145
  };
118
146
  }
119
-
120
147
  export async function loadHostPolicy(policyPath) {
121
148
  if (!policyPath) throw new Error('missing DEADBYTE privileged host policy path');
122
149
  const absolute = path.resolve(policyPath);
@@ -345,6 +372,93 @@ function collectBounded(stream, limit, state, label, onExceeded) {
345
372
  });
346
373
  }
347
374
 
375
+ function validateHostArgs(args,maxArgs=64,maxChars=2048,label='host exec args'){
376
+ if(!Array.isArray(args)||args.length>maxArgs||args.some(x=>typeof x!=='string'||x.length>maxChars||x.includes('\0'))){
377
+ throw new Error(`${label} must be <=${maxArgs} strings of <=${maxChars} characters`);
378
+ }
379
+ }
380
+ async function resolveExecCwd(policy,rootId,cwd){
381
+ const root=getRoot(policy,rootId,'exec');
382
+ let cwdResolved=root.real_path;
383
+ if(cwd!=='.'){
384
+ validatePortableRelativePath(cwd);
385
+ cwdResolved=path.resolve(root.real_path,cwd);
386
+ if(!isInside(root.real_path,cwdResolved)) throw new Error('host exec cwd escapes allowed root');
387
+ const realCwd=await realpath(cwdResolved);
388
+ if(!isInside(root.real_path,realCwd)) throw new Error('host exec cwd symlink escapes allowed root');
389
+ const cwdStat=await stat(realCwd);
390
+ if(!cwdStat.isDirectory()) throw new Error('host exec cwd must be a directory');
391
+ cwdResolved=realCwd;
392
+ }
393
+ return cwdResolved;
394
+ }
395
+ async function runHostChild(policy,armed,{executable,argv,cwd,timeoutMs,maxTimeoutMs,maxOutputBytes,authorityPollMs=100}){
396
+ const activeArm=await recheckHostAuthority(policy,armed);
397
+ const grantBound=Number.isFinite(activeArm.remaining_ms)?Math.floor(activeArm.remaining_ms):maxTimeoutMs;
398
+ if(grantBound<100) throw new Error('privileged host plane arm lease expires too soon');
399
+ const requested=timeoutMs===null||timeoutMs===undefined?maxTimeoutMs:finiteInteger(timeoutMs,'timeout_ms',100,maxTimeoutMs,maxTimeoutMs);
400
+ const effectiveTimeout=Math.min(requested,maxTimeoutMs,grantBound);
401
+ const child=spawn(executable,argv,{cwd,env:scrubHostEnvironment(),shell:false,windowsHide:true,detached:false,stdio:['ignore','pipe','pipe']});
402
+ const stdoutState={chunks:[],bytes:0,exceeded:null};
403
+ const stderrState={chunks:[],bytes:0,exceeded:null};
404
+ const kill=()=>{try{child.kill('SIGKILL');}catch{}};
405
+ collectBounded(child.stdout,maxOutputBytes,stdoutState,'stdout',kill);
406
+ collectBounded(child.stderr,maxOutputBytes,stderrState,'stderr',kill);
407
+ let timedOut=false,authorityError=null,timer=null,poller=null;
408
+ const completion=new Promise((resolve,reject)=>{
409
+ child.once('error',reject);
410
+ child.once('close',(code,signal)=>resolve({code,signal}));
411
+ timer=setTimeout(()=>{timedOut=true;kill();},effectiveTimeout); timer.unref?.();
412
+ const poll=Math.max(25,Math.min(1000,Number.isInteger(authorityPollMs)?authorityPollMs:100));
413
+ poller=setInterval(()=>{void recheckHostAuthority(policy,armed).catch(error=>{if(!authorityError){authorityError=error;kill();}});},poll); poller.unref?.();
414
+ });
415
+ let result;
416
+ try{result=await completion;}finally{if(timer)clearTimeout(timer);if(poller)clearInterval(poller);}
417
+ if(authorityError) throw authorityError;
418
+ if(stdoutState.exceeded||stderrState.exceeded) throw new Error(stdoutState.exceeded??stderrState.exceeded);
419
+ const stdoutBytes=Buffer.concat(stdoutState.chunks),stderrBytes=Buffer.concat(stderrState.chunks);
420
+ return {
421
+ exit_code:result.code,signal:result.signal,timed_out:timedOut,timeout_ms:effectiveTimeout,
422
+ stdout:decodeUtf8Strict(stdoutBytes),stderr:decodeUtf8Strict(stderrBytes),
423
+ stdout_sha256:sha256Bytes(stdoutBytes),stderr_sha256:sha256Bytes(stderrBytes),
424
+ argv_sha256:sha256Bytes(Buffer.from(JSON.stringify(argv),'utf8'))
425
+ };
426
+ }
427
+
428
+ export async function hostExecInline(policy,{commandId,rootId,command,cwd='.',timeoutMs=null,authorityPollMs=100}){
429
+ const armed=await assertHostArmed(policy);
430
+ if(!COMMAND_ID_RE.test(commandId)) throw new Error('invalid host command id');
431
+ const spec=policy.commands[commandId];
432
+ if(!spec||spec.kind!=='inline') throw new Error(`host inline command '${commandId}' is not allowed`);
433
+ if(!ROOT_ID_RE.test(rootId)||!spec.root_ids.includes(rootId)) throw new Error(`host command '${commandId}' is not allowed for root '${rootId}'`);
434
+ getRoot(policy,rootId,'exec');
435
+ if(typeof command!=='string'||command.length<1||command.length>spec.max_command_chars||command.includes('\0')) throw new Error('host inline command exceeds policy bounds');
436
+ const actualExecutableHash=await sha256File(spec.executable);
437
+ if(actualExecutableHash!==spec.executable_sha256) throw new Error(`host command '${commandId}' executable hash changed`);
438
+ const cwdResolved=await resolveExecCwd(policy,rootId,cwd);
439
+ const argv=[...spec.prefix_args,command];
440
+ const result=await runHostChild(policy,armed,{executable:spec.executable,argv,cwd:cwdResolved,timeoutMs,maxTimeoutMs:spec.timeout_ms,maxOutputBytes:spec.max_output_bytes,authorityPollMs});
441
+ return {command_id:commandId,mode:'inline',executable_sha256:actualExecutableHash,command_sha256:sha256Bytes(Buffer.from(command,'utf8')),...result};
442
+ }
443
+
444
+ export async function hostExecNative(policy,{rootId,executable,executableSha256,args=[],cwd='.',timeoutMs=null,authorityPollMs=100}){
445
+ const armed=await assertHostArmed(policy);
446
+ const spec=policy.native_exec;
447
+ if(!spec?.enabled) throw new Error('host native exec is not enabled by policy');
448
+ if(!ROOT_ID_RE.test(rootId)||!spec.root_ids.includes(rootId)) throw new Error(`host native exec is not allowed for root '${rootId}'`);
449
+ getRoot(policy,rootId,'exec');
450
+ if(typeof executable!=='string'||!path.isAbsolute(executable)) throw new Error('host native executable must be absolute');
451
+ if(typeof executableSha256!=='string'||!SHA256_RE.test(executableSha256)) throw new Error('executable_sha256 must be a lowercase SHA-256 digest');
452
+ validateHostArgs(args,spec.max_args,spec.max_arg_chars,'host native args');
453
+ const resolved=path.resolve(executable); const info=await stat(resolved);
454
+ if(!info.isFile()) throw new Error('host native executable is not a file');
455
+ const actualExecutableHash=await sha256File(resolved);
456
+ if(actualExecutableHash!==executableSha256) throw new Error(`host native executable hash mismatch: expected ${executableSha256}, found ${actualExecutableHash}`);
457
+ const cwdResolved=await resolveExecCwd(policy,rootId,cwd);
458
+ const result=await runHostChild(policy,armed,{executable:resolved,argv:[...args],cwd:cwdResolved,timeoutMs,maxTimeoutMs:spec.timeout_ms,maxOutputBytes:spec.max_output_bytes,authorityPollMs});
459
+ return {command_id:'native',mode:'native',executable_sha256:actualExecutableHash,...result};
460
+ }
461
+
348
462
  export async function hostExec(policy, {
349
463
  commandId,
350
464
  rootId,
@@ -357,14 +471,12 @@ export async function hostExec(policy, {
357
471
  const armed = await assertHostArmed(policy);
358
472
  if (!COMMAND_ID_RE.test(commandId)) throw new Error('invalid host command id');
359
473
  const command = policy.commands[commandId];
360
- if (!command) throw new Error(`host command '${commandId}' is not allowed`);
361
- if (!Array.isArray(args) || args.length > 64 || args.some(x => typeof x !== 'string' || x.length > 2048 || x.includes('\0'))) {
362
- throw new Error('host exec args must be <=64 strings of <=2048 characters');
363
- }
474
+ if (!command || command.kind!=='script') throw new Error(`host command '${commandId}' is not allowed as a script command`);
475
+ validateHostArgs(args,64,2048,'host exec args');
364
476
  if (typeof scriptSha256 !== 'string' || !SHA256_RE.test(scriptSha256)) throw new Error('script_sha256 must be a lowercase SHA-256 digest');
365
477
 
366
478
  if (!ROOT_ID_RE.test(rootId) || !command.root_ids.includes(rootId)) throw new Error(`host command '${commandId}' is not allowed for root '${rootId}'`);
367
- const root = getRoot(policy, rootId, 'exec');
479
+ getRoot(policy, rootId, 'exec');
368
480
  const script = await resolveHostExisting(policy, rootId, scriptPath, 'exec');
369
481
  const extension = path.extname(script.resolved).toLowerCase();
370
482
  if (!command.extensions.includes(extension)) throw new Error(`script extension '${extension}' is not allowed for '${commandId}'`);
@@ -373,72 +485,8 @@ export async function hostExec(policy, {
373
485
 
374
486
  const actualExecutableHash = await sha256File(command.executable);
375
487
  if (actualExecutableHash !== command.executable_sha256) throw new Error(`host command '${commandId}' executable hash changed`);
376
-
377
- let cwdResolved = root.real_path;
378
- if (cwd !== '.') {
379
- validatePortableRelativePath(cwd);
380
- cwdResolved = path.resolve(root.real_path, cwd);
381
- if (!isInside(root.real_path, cwdResolved)) throw new Error('host exec cwd escapes allowed root');
382
- const realCwd = await realpath(cwdResolved);
383
- if (!isInside(root.real_path, realCwd)) throw new Error('host exec cwd symlink escapes allowed root');
384
- const cwdStat = await stat(realCwd);
385
- if (!cwdStat.isDirectory()) throw new Error('host exec cwd must be a directory');
386
- cwdResolved = realCwd;
387
- }
388
-
389
- const requestedTimeout = timeoutMs === null ? command.timeout_ms : finiteInteger(timeoutMs, 'timeout_ms', 100, command.timeout_ms, command.timeout_ms);
390
- const activeArm = await recheckHostAuthority(policy, armed);
391
- const grantBound = Number.isFinite(activeArm.remaining_ms) ? Math.floor(activeArm.remaining_ms) : command.timeout_ms;
392
- if (grantBound < 100) throw new Error('privileged host plane arm lease expires too soon');
393
- const effectiveTimeout = Math.min(requestedTimeout, command.timeout_ms, grantBound);
394
- const argv = [...command.prefix_args, script.resolved, ...args];
395
-
396
- const child = spawn(command.executable, argv, {
397
- cwd: cwdResolved,
398
- env: scrubHostEnvironment(),
399
- shell: false,
400
- windowsHide: true,
401
- detached: false,
402
- stdio: ['ignore', 'pipe', 'pipe']
403
- });
404
-
405
- const stdoutState = { chunks: [], bytes: 0, exceeded: null };
406
- const stderrState = { chunks: [], bytes: 0, exceeded: null };
407
- collectBounded(child.stdout, command.max_output_bytes, stdoutState, 'stdout', () => child.kill('SIGKILL'));
408
- collectBounded(child.stderr, command.max_output_bytes, stderrState, 'stderr', () => child.kill('SIGKILL'));
409
-
410
- let timedOut = false;
411
- let timer;
412
- const completion = new Promise((resolve, reject) => {
413
- child.once('error', reject);
414
- child.once('close', (code, signal) => resolve({ code, signal }));
415
- timer = setTimeout(() => {
416
- timedOut = true;
417
- child.kill('SIGKILL');
418
- }, effectiveTimeout);
419
- timer.unref?.();
420
- });
421
- let result;
422
- try {
423
- result = await completion;
424
- } finally {
425
- if (timer) clearTimeout(timer);
426
- }
427
-
428
- if (stdoutState.exceeded || stderrState.exceeded) {
429
- throw new Error(stdoutState.exceeded ?? stderrState.exceeded);
430
- }
431
- const stdoutBytes = Buffer.concat(stdoutState.chunks);
432
- const stderrBytes = Buffer.concat(stderrState.chunks);
433
- return {
434
- command_id: commandId,
435
- executable_sha256: actualExecutableHash,
436
- script_sha256: actualScriptHash,
437
- exit_code: result.code,
438
- signal: result.signal,
439
- timed_out: timedOut,
440
- timeout_ms: effectiveTimeout,
441
- stdout: decodeUtf8Strict(stdoutBytes),
442
- stderr: decodeUtf8Strict(stderrBytes)
443
- };
488
+ const cwdResolved=await resolveExecCwd(policy,rootId,cwd);
489
+ const argv=[...command.prefix_args,script.resolved,...args];
490
+ const result=await runHostChild(policy,armed,{executable:command.executable,argv,cwd:cwdResolved,timeoutMs,maxTimeoutMs:command.timeout_ms,maxOutputBytes:command.max_output_bytes});
491
+ return {command_id:commandId,mode:'script',executable_sha256:actualExecutableHash,script_sha256:actualScriptHash,...result};
444
492
  }
@@ -15,7 +15,7 @@ import { DEADBYTE_VERSION } from './version.mjs';
15
15
  import { currentIngressAdapterMode, TUNNEL_STDIO_PROBE_COMPAT_MODE } from './ingress-mode.mjs';
16
16
  import { installTunnelStdioProbeCompat } from './tunnel-stdio-compat.mjs';
17
17
  import { decodeUtf8Strict } from './text-policy.mjs';
18
- import { assertHostArmed, hostExec, hostFileRead, hostFileWrite, loadHostPolicy } from './host-plane.mjs';
18
+ import { assertHostArmed, hostExec, hostExecInline, hostExecNative, hostFileRead, hostFileWrite, loadHostPolicy } from './host-plane.mjs';
19
19
  import { createMachineRuntime } from './machine-identity.mjs';
20
20
  import { registerMachineTools } from './machine-mcp-tools.mjs';
21
21
  import { createAgentRuntime } from './agent-runtime.mjs';
@@ -75,8 +75,9 @@ if (!['0','1'].includes(auditRuntimeMode)) throw new Error('DEADBYTE_AUDIT_RUNTI
75
75
  const runtimeStateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(),'.deadbyte-mcp'));
76
76
  const surfaceProfile = capabilityProfileFromEnv(process.env);
77
77
  const localPrivilegedPolicyPath = path.join(packageRoot, 'controller', 'deadbyte-host-policy.json');
78
+ const privilegedPolicyExplicit = Object.hasOwn(process.env, 'DEADBYTE_PRIVILEGED_POLICY');
78
79
  let privilegedPolicyPath = process.env.DEADBYTE_PRIVILEGED_POLICY ?? '';
79
- if (!privilegedPolicyPath) {
80
+ if (!privilegedPolicyExplicit) {
80
81
  try {
81
82
  await access(localPrivilegedPolicyPath);
82
83
  privilegedPolicyPath = localPrivilegedPolicyPath;
@@ -505,31 +506,71 @@ serveStdio(() => {
505
506
  toolServer.registerTool(
506
507
  'host_exec',
507
508
  {
508
- description: 'Privileged host-plane script execution through a locally allowlisted and SHA-256-pinned executable. No raw shell command string is accepted. Requires a live local arm lease.',
509
- inputSchema: z.strictObject({
510
- command_id: z.string().min(1).max(64),
511
- root_id: z.string().min(1).max(64),
512
- script_path: z.string().min(1).max(512),
513
- script_sha256: z.string().regex(/^[0-9a-f]{64}$/),
514
- args: z.array(z.string().max(2048)).max(64).default([]),
515
- cwd: z.string().min(1).max(512).default('.'),
516
- timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
517
- }),
509
+ description: 'Privileged Host execution with three bounded modes: legacy hash-pinned script, policy-approved inline argv memory, or absolute hash-pinned native executable argv. shell=false; live Host revocation is enforced.',
510
+ inputSchema: z.union([
511
+ z.strictObject({
512
+ mode: z.literal('script').optional().default('script'),
513
+ command_id: z.string().min(1).max(64),
514
+ root_id: z.string().min(1).max(64),
515
+ script_path: z.string().min(1).max(512),
516
+ script_sha256: z.string().regex(/^[0-9a-f]{64}$/),
517
+ args: z.array(z.string().max(2048)).max(64).default([]),
518
+ cwd: z.string().min(1).max(512).default('.'),
519
+ timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
520
+ }),
521
+ z.strictObject({
522
+ mode: z.literal('inline'),
523
+ command_id: z.string().min(1).max(64),
524
+ root_id: z.string().min(1).max(64),
525
+ command: z.string().min(1).max(262144),
526
+ cwd: z.string().min(1).max(512).default('.'),
527
+ timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
528
+ }),
529
+ z.strictObject({
530
+ mode: z.literal('native'),
531
+ root_id: z.string().min(1).max(64),
532
+ executable: z.string().min(1).max(2048),
533
+ executable_sha256: z.string().regex(/^[0-9a-f]{64}$/),
534
+ args: z.array(z.string().max(32768)).max(64).default([]),
535
+ cwd: z.string().min(1).max(512).default('.'),
536
+ timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
537
+ })
538
+ ]),
518
539
  outputSchema: hostExecOutputSchema
519
540
  },
520
- async ({ command_id, root_id, script_path, script_sha256, args, cwd, timeout_ms }) => {
541
+ async args => {
521
542
  try {
522
- const result = await hostExec(hostPolicy, {
523
- commandId: command_id,
524
- rootId: root_id,
525
- scriptPath: script_path,
526
- scriptSha256: script_sha256,
527
- args,
528
- cwd,
529
- timeoutMs: timeout_ms
530
- });
543
+ let result;
544
+ if (args.mode === 'inline') {
545
+ result = await hostExecInline(hostPolicy, {
546
+ commandId: args.command_id,
547
+ rootId: args.root_id,
548
+ command: args.command,
549
+ cwd: args.cwd,
550
+ timeoutMs: args.timeout_ms
551
+ });
552
+ } else if (args.mode === 'native') {
553
+ result = await hostExecNative(hostPolicy, {
554
+ rootId: args.root_id,
555
+ executable: args.executable,
556
+ executableSha256: args.executable_sha256,
557
+ args: args.args,
558
+ cwd: args.cwd,
559
+ timeoutMs: args.timeout_ms
560
+ });
561
+ } else {
562
+ result = await hostExec(hostPolicy, {
563
+ commandId: args.command_id,
564
+ rootId: args.root_id,
565
+ scriptPath: args.script_path,
566
+ scriptSha256: args.script_sha256,
567
+ args: args.args,
568
+ cwd: args.cwd,
569
+ timeoutMs: args.timeout_ms
570
+ });
571
+ }
531
572
  const failed = result.timed_out || result.exit_code !== 0;
532
- return textResult(hostExecOutputSchema, { status: failed ? 'failed' : 'passed', root_id, ...result }, failed);
573
+ return textResult(hostExecOutputSchema, { status: failed ? 'failed' : 'passed', root_id: args.root_id, ...result }, failed);
533
574
  } catch (error) {
534
575
  return textResult(hostExecOutputSchema, { status: 'denied', error: String(error) }, true);
535
576
  }
@@ -321,17 +321,22 @@ export function createNativeDiffV2Runtime({policy,codingRuntime,machineFsRuntime
321
321
  const receipt=await createApplyReceipt(ctx,diffId,identity,preimage,events);return {status:'committed',diff_id:diffId,receipt_sha256:receipt.receipt_sha256,operation_count:identity.operations.length,operation_receipts:receipt.operation_receipts.map(x=>({index:x.index,kind:x.kind,receipt_class:x.receipt_class,receipt_id:x.receipt_id,receipt_sha256:x.receipt_sha256}))};
322
322
  }
323
323
 
324
- async function verify({diffId}){
324
+ async function verifyEvidence({diffId}){
325
325
  try{
326
326
  const {ctx,identity}=await loadDiff(diffId);const verifyKey=await loadPublicVerifyKey(verifyKeyPath);const preimage=await readEnvelope(ctx.preimagePath,verifyKey,PREIMAGE_DOMAIN,'native diff V2 preimage');const events=await readEvents(ctx);const receipt=await readEnvelope(ctx.receiptPath,verifyKey,RECEIPT_DOMAIN,'native diff V2 receipt');const {attestation:_a,receipt_sha256:claimed,...body}=receipt;
327
327
  if(body.schema!==NATIVE_DIFF_V2_RECEIPT_SCHEMA||body.diff_id!==diffId||body.policy_sha256!==policy.policy_sha256||body.coding_policy_sha256!==policy.coding_policy_sha256||!SHA256_RE.test(claimed??'')||sha256Object(body)!==claimed)return {ok:false,reason:'native diff V2 receipt identity/hash mismatch'};
328
328
  const completion=events.at(-1);if(completion?.type!=='transaction_completed'||completion.prev_sha256!==receipt.event_head_sha256||completion.payload?.receipt_sha256!==claimed||receipt.preimage_sha256!==preimage.preimage_sha256)return {ok:false,reason:'native diff V2 receipt/event/preimage binding mismatch'};
329
329
  if(!Array.isArray(receipt.operation_receipts)||receipt.operation_receipts.length!==identity.operations.length)return {ok:false,reason:'native diff V2 operation receipt count mismatch'};
330
330
  let verified=0,codingVerified=0;for(let index=0;index<identity.operations.length;index+=1){const op=identity.operations[index];const bound=receipt.operation_receipts.find(x=>x.index===index);if(!bound||bound.kind!==op.kind||bound.receipt_class!==receiptClass(op))return {ok:false,reason:`native diff V2 receipt binding mismatch index ${index}`};const checked=await verifyOperationReceipt(op,bound.receipt_id);if(!checked.ok||checked.receipt_sha256!==bound.receipt_sha256||checked.request_sha256!==bound.request_sha256)return {ok:false,reason:`native diff V2 operation receipt invalid index ${index}`};verified+=1;if(bound.receipt_class==='coding')codingVerified+=1;}
331
- let currentMatches=true;for(const op of identity.operations)if(!(await terminalStateMatches(identity.root_id,op)))currentMatches=false;
332
- return {ok:true,diff_id:diffId,receipt_sha256:claimed,signature_valid:true,root_id:identity.root_id,operation_count:identity.operations.length,coding_receipts_verified:codingVerified,operation_receipts_verified:verified,final_states:receipt.final_states,current_state_matches:currentMatches,journal_head_sha256:completion.event_sha256};
331
+ return {ok:true,diff_id:diffId,receipt_sha256:claimed,signature_valid:true,root_id:identity.root_id,operation_count:identity.operations.length,coding_receipts_verified:codingVerified,operation_receipts_verified:verified,final_states:receipt.final_states,journal_head_sha256:completion.event_sha256};
333
332
  }catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
334
333
  }
335
334
 
336
- return Object.freeze({preview,apply,verify,policy});
335
+ async function verify({diffId}){
336
+ const checked=await verifyEvidence({diffId});if(!checked.ok)return checked;
337
+ try{const {identity}=await loadDiff(diffId);let currentMatches=true;for(const op of identity.operations)if(!(await terminalStateMatches(identity.root_id,op)))currentMatches=false;return {...checked,current_state_matches:currentMatches};}
338
+ catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
339
+ }
340
+
341
+ return Object.freeze({preview,apply,verify,verifyEvidence,policy});
337
342
  }
@@ -361,7 +361,7 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
361
361
  coding_receipts:receipt.coding_receipts.map(item => ({ index:item.index, path:item.path, receipt_id:item.receipt_id, receipt_sha256:item.receipt_sha256 })) };
362
362
  }
363
363
 
364
- async function verify({ diffId }) {
364
+ async function verifyEvidence({ diffId }) {
365
365
  try {
366
366
  const { ctx, identity } = await loadDiff(diffId);
367
367
  const verifyKey = await loadPublicVerifyKey(verifyKeyPath);
@@ -390,21 +390,32 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
390
390
  }
391
391
  verifiedReceipts += 1;
392
392
  }
393
+ return { ok:true, diff_id:diffId, receipt_sha256:claimed, signature_valid:true,
394
+ root_id:identity.root_id, operation_count:identity.operations.length,
395
+ coding_receipts_verified:verifiedReceipts, final_states:receipt.final_states,
396
+ journal_head_sha256:completion.event_sha256 };
397
+ } catch (error) {
398
+ return { ok:false, reason:String(error).slice(0,2048) };
399
+ }
400
+ }
401
+
402
+ async function verify({ diffId }) {
403
+ const checked = await verifyEvidence({ diffId });
404
+ if (!checked.ok) return checked;
405
+ try {
406
+ const { identity } = await loadDiff(diffId);
393
407
  let currentMatches = true;
394
408
  for (const op of identity.operations) {
395
409
  const current = await codingRuntime.fileState({ rootId:identity.root_id, relativePath:op.path });
396
410
  if (!stateMatches(current, op.after_sha256)) currentMatches = false;
397
411
  }
398
- return { ok:true, diff_id:diffId, receipt_sha256:claimed, signature_valid:true,
399
- root_id:identity.root_id, operation_count:identity.operations.length,
400
- coding_receipts_verified:verifiedReceipts, final_states:receipt.final_states,
401
- current_state_matches:currentMatches, journal_head_sha256:completion.event_sha256 };
412
+ return { ...checked, current_state_matches:currentMatches };
402
413
  } catch (error) {
403
414
  return { ok:false, reason:String(error).slice(0,2048) };
404
415
  }
405
416
  }
406
417
 
407
- return Object.freeze({ preview, apply, verify, policy });
418
+ return Object.freeze({ preview, apply, verify, verifyEvidence, policy });
408
419
  }
409
420
 
410
421
  export { NATIVE_DIFF_V2_SCHEMA };
@@ -448,5 +459,16 @@ export function createNativeDiffRuntime(options) {
448
459
  return {ok:false,reason:String(error).slice(0,2048)};
449
460
  }
450
461
  }
451
- return Object.freeze({preview,apply,verify,policy:options.policy});
462
+ async function verifyEvidence(args){
463
+ try {
464
+ const schema=await schemaFor(args?.diffId);
465
+ if(schema===NATIVE_DIFF_SCHEMA) return v1.verifyEvidence(args);
466
+ if(schema===NATIVE_DIFF_V2_SCHEMA){
467
+ if(!v2) return {ok:false,reason:'native diff V2 topology requires machine fs runtime'};
468
+ return v2.verifyEvidence(args);
469
+ }
470
+ return {ok:false,reason:`unsupported native diff schema: ${String(schema)}`};
471
+ }catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
472
+ }
473
+ return Object.freeze({preview,apply,verify,verifyEvidence,policy:options.policy});
452
474
  }
@@ -1,6 +1,6 @@
1
1
  import * as z from 'zod/v4';
2
2
  import {
3
- observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
3
+ observationHardwareQueryOutputSchema,observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
4
4
  observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema
5
5
  } from './observation-output-contracts.mjs';
6
6
 
@@ -41,4 +41,8 @@ export function registerObservationTools(server,{runtime}){
41
41
  description:'Read bounded timestamped system status with signed observation evidence. This tool cannot mutate or arm any authority.',
42
42
  inputSchema:z.strictObject({}),outputSchema:observationSystemStatusOutputSchema
43
43
  },async()=>{try{return ok(observationSystemStatusOutputSchema,await runtime.systemStatus());}catch(error){return fail(observationSystemStatusOutputSchema,error);}});
44
+ server.registerTool('machine_hardware_query',{
45
+ description:'Read one typed native hardware snapshot (CPU, GPU, memory, storage, network, or PCI) with signed observation evidence. No shell or Host authority is used.',
46
+ inputSchema:z.strictObject({query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),outputSchema:observationHardwareQueryOutputSchema
47
+ },async args=>{try{return ok(observationHardwareQueryOutputSchema,await runtime.hardwareQuery({query:args.query}));}catch(error){return fail(observationHardwareQueryOutputSchema,error);}});
44
48
  }
@@ -45,6 +45,14 @@ export const observationSystemStatusOutputSchema=statusContract({
45
45
  ...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
46
46
  },['observed_at_utc','hostname','platform','architecture','uptime_seconds','total_memory_bytes','free_memory_bytes','cpu_count','load_average','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
47
47
 
48
+ export const observationHardwareQueryOutputSchema=statusContract({
49
+ observed_at_utc:z.string().min(1).optional(),
50
+ query:z.enum(['cpu','gpu','memory','storage','network','pci']).optional(),
51
+ data:z.record(z.string(),z.json()).optional(),
52
+ observer_sha256:hex64.optional(),
53
+ ...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
54
+ },['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
55
+
48
56
  export function observationStructuredResult(schema,value,isError=false){
49
57
  return structuredToolResult(schema,value,isError);
50
58
  }