deadbyte-mcp 0.15.53 → 0.15.55

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.
@@ -45,7 +45,12 @@ export function classifyActivationPreflight({pointer,expectedParent,candidate,ex
45
45
  if(!expectedParent||!expectedCandidate||!candidate||!pointer)throw new Error('activation preflight subject missing');
46
46
  const authority=vector(observation);
47
47
  if(PLANES.some(plane=>typeof authority[plane]!=='boolean'))return Object.freeze({state:'UNKNOWN',safe_to_activate:false,reason:'authority observation incomplete'});
48
- const subject={pointer,expectedParent,candidate,expectedCandidate,observation};
48
+ // Observed time is freshness metadata, not a runtime or authority identity.
49
+ // Rebinding to it makes a second read reject an otherwise identical signed approval.
50
+ // Keep every other observed field pinned, including runtime IDs and grant proofs.
51
+ const stableObservation={...observation};
52
+ delete stableObservation.observed_at_utc;
53
+ const subject={pointer,expectedParent,candidate,expectedCandidate,observation:stableObservation};
49
54
  const bound={subject_sha256:digest(subject),authority:Object.freeze(authority)};
50
55
  for(const plane of PLANES){
51
56
  const entry=observation?.authority?.[plane],proof=observation?.authority_proof?.[plane];
@@ -85,12 +90,25 @@ export function classifyActivationPreflight({pointer,expectedParent,candidate,ex
85
90
  return Object.freeze({state:'ONLINE_READY',safe_to_activate:true,...bound});
86
91
  }
87
92
 
88
- export async function orchestrateActivation({pointer,expectedParent,candidate,expectedCandidate,observe,command,activate,claim=null,continuityEngine=null,recordOutcome=null}){
93
+ function validateLegacyOfflineRecoveryProof(proof,{preflightSubjectSha256,candidate}){
94
+ if(!proof||proof.state!=='VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR'||
95
+ proof.preflight_subject_sha256!==preflightSubjectSha256||
96
+ proof.candidate_manifest_sha256!==candidate.manifest_sha256||
97
+ proof.owner_approval_verified!==true||!HEX64.test(String(proof.subject_sha256??'')))
98
+ throw new Error('legacy offline scope recovery proof invalid or unbound');
99
+ return proof;
100
+ }
101
+
102
+ export async function orchestrateActivation({pointer,expectedParent,candidate,expectedCandidate,observe,command,activate,claim=null,continuityEngine=null,recordOutcome=null,legacyOfflineRecovery=null}){
89
103
  if([observe,command,activate].some(fn=>typeof fn!=='function'))throw new Error('activation orchestrator callbacks invalid');
90
104
  const input={pointer,expectedParent,candidate,expectedCandidate};
91
105
  const firstObservation=await observe();
92
106
  const initial=classifyActivationPreflight({...input,observation:firstObservation});
93
- if(!['ONLINE_READY','ONLINE_HOST_ARMED','PARKED_DIRTY','OFFLINE_CLEAN'].includes(initial.state))
107
+ const legacyScopeRepair=initial.state==='AUTHORITY_UNVERIFIED'&&typeof legacyOfflineRecovery==='function'&&
108
+ firstObservation?.state==='OFFLINE'&&firstObservation?.local_ready===false&&
109
+ !firstObservation?.bridge?.alive&&!firstObservation?.core?.alive&&!firstObservation?.cloud?.alive&&
110
+ firstObservation?.supervisor?.running===false&&PLANES.every(plane=>initial.authority?.[plane]===false);
111
+ if(!legacyScopeRepair&&!['ONLINE_READY','ONLINE_HOST_ARMED','PARKED_DIRTY','OFFLINE_CLEAN'].includes(initial.state))
94
112
  return Object.freeze({state:'ACTIVATION_REJECTED',preflight:initial.state,subject_sha256:initial.subject_sha256??null});
95
113
  const continuity=classifyAuthorityContinuity(firstObservation);
96
114
  if(continuity.state==='UNVERIFIED')
@@ -112,6 +130,12 @@ export async function orchestrateActivation({pointer,expectedParent,candidate,ex
112
130
  let continuityCapture=null;
113
131
  const outcome=await (async()=>{
114
132
  try{
133
+ let legacyProof=null;
134
+ if(legacyScopeRepair){
135
+ legacyProof=validateLegacyOfflineRecoveryProof(await legacyOfflineRecovery({phase:'claimed',observation:firstObservation,
136
+ preflight_subject_sha256:initial.subject_sha256,pointer,expectedParent,candidate,expectedCandidate}),
137
+ {preflightSubjectSha256:initial.subject_sha256,candidate});
138
+ }
115
139
  const requiresReauth=continuity.state==='LEGACY_REAUTH_REQUIRED'||continuity.state==='MIXED_REAUTH_REQUIRED';
116
140
  const epochNative=continuity.state==='EPOCH_NATIVE';
117
141
  if(epochNative){
@@ -134,8 +158,18 @@ export async function orchestrateActivation({pointer,expectedParent,candidate,ex
134
158
  await command('supervisorstop');
135
159
  await command('stop');
136
160
  }
137
- const ready=classifyActivationPreflight({...input,observation:await observe()});
138
- if(!ready.safe_to_activate)
161
+ const readyObservation=await observe();
162
+ const ready=classifyActivationPreflight({...input,observation:readyObservation});
163
+ let readySafe=ready.safe_to_activate;
164
+ if(legacyScopeRepair){
165
+ if(ready.subject_sha256!==initial.subject_sha256)throw new Error('legacy offline scope recovery observation drift');
166
+ const second=validateLegacyOfflineRecoveryProof(await legacyOfflineRecovery({phase:'preactivate',observation:readyObservation,
167
+ preflight_subject_sha256:initial.subject_sha256,pointer,expectedParent,candidate,expectedCandidate,prior_proof:legacyProof}),
168
+ {preflightSubjectSha256:initial.subject_sha256,candidate});
169
+ if(second.subject_sha256!==legacyProof.subject_sha256)throw new Error('legacy offline scope recovery proof drift');
170
+ readySafe=true;
171
+ }
172
+ if(!readySafe)
139
173
  return Object.freeze({state:transitionAttempted?'ACTIVATION_OUTCOME_UNKNOWN':'ACTIVATION_REJECTED',
140
174
  preflight:ready.state,subject_sha256:initial.subject_sha256});
141
175
  if(PLANES.some(plane=>ready.authority[plane]!==false))
@@ -180,7 +214,8 @@ export async function orchestrateActivation({pointer,expectedParent,candidate,ex
180
214
  reauth_planes:Object.freeze([...(restored.reauth_planes??[])])});
181
215
  }
182
216
  const final=beforeRestore;
183
- const authorityOutcome=continuity.state==='NOT_APPLICABLE'?'NOT_APPLICABLE':'REAUTH_REQUIRED';
217
+ const authorityOutcome=legacyScopeRepair?'LEGACY_SCOPE_REPAIRED_DISARMED':
218
+ continuity.state==='NOT_APPLICABLE'?'NOT_APPLICABLE':'REAUTH_REQUIRED';
184
219
  return Object.freeze({
185
220
  state:'ACTIVATED',runtime_activation:'ACTIVATED',authority_continuity:authorityOutcome,
186
221
  subject_sha256:initial.subject_sha256,release_id:activation.release_id,
@@ -1,4 +1,5 @@
1
1
  import * as z from 'zod/v4';
2
+ import { payloadHash } from './remote-device-r44.mjs';
2
3
 
3
4
  const HEX64=/^[0-9a-f]{64}$/;
4
5
  const CAP=/^[A-Za-z0-9._-]{1,128}$/;
@@ -9,12 +10,25 @@ function requirePlane(plane,name){
9
10
  if(!plane) return {status:'unavailable',tool:name,error:'remote-device-r44 plane is not configured'};
10
11
  return null;
11
12
  }
13
+ async function probeBoundConnection(plane,{device_id,epoch,connection_id,nonce}){
14
+ if(!HEX64.test(device_id??''))throw new Error('remote readiness device identity invalid');
15
+ if(!Number.isInteger(epoch)||epoch<1)throw new Error('remote readiness epoch invalid');
16
+ if(typeof connection_id!=='string'||connection_id.length<1||connection_id.length>256)throw new Error('remote readiness connection identity invalid');
17
+ if(!HEX64.test(nonce??''))throw new Error('remote readiness nonce invalid');
18
+ const result=await plane.probe({deviceId:device_id,epoch});
19
+ if(!result||result.device_id!==device_id)throw new Error('remote readiness device identity mismatch');
20
+ if(result.epoch!==epoch)throw new Error('remote readiness epoch mismatch');
21
+ if(result.connection_id!==connection_id)throw new Error('remote readiness connection identity mismatch');
22
+ if(result.reachable!==true)throw new Error('remote readiness probe not reachable');
23
+ return {...result,nonce,ready:true};
24
+ }
12
25
  export function registerCommanderRemoteBridge(server,{remoteDevicePlane=null}={}){
13
26
  register(server,'remote_device',z.strictObject({
14
27
  op:z.enum(['pair','status','report_online','probe','dispatch','revoke','rotate_credential','list']),
15
28
  device_id:z.string().regex(HEX64).optional(),epoch:z.number().int().min(1).optional(),
16
29
  public_key_spki_base64:z.string().min(1).max(4096).optional(),display_name:z.string().min(1).max(128).optional(),
17
30
  connection_id:z.string().min(1).max(256).optional(),capability_id:z.string().regex(CAP).optional(),
31
+ nonce:z.string().regex(HEX64).optional(),
18
32
  payload:z.record(z.string(),z.unknown()).optional(),request_id:z.string().regex(HEX64).optional(),
19
33
  encrypted_bundle_base64:z.string().min(1).max(1024*1024).optional(),expected_previous_sha256:z.string().regex(HEX64).nullable().optional()
20
34
  }),async a=>{
@@ -23,7 +37,7 @@ export function registerCommanderRemoteBridge(server,{remoteDevicePlane=null}={}
23
37
  case 'pair': return remoteDevicePlane.pair({publicKeySpkiBase64:a.public_key_spki_base64,displayName:a.display_name});
24
38
  case 'status': return remoteDevicePlane.status({deviceId:a.device_id});
25
39
  case 'report_online': return remoteDevicePlane.reportOnline({deviceId:a.device_id,epoch:a.epoch,connectionId:a.connection_id});
26
- case 'probe': return remoteDevicePlane.probe({deviceId:a.device_id,epoch:a.epoch});
40
+ case 'probe': return probeBoundConnection(remoteDevicePlane,a);
27
41
  case 'dispatch': return remoteDevicePlane.dispatch({deviceId:a.device_id,epoch:a.epoch,capabilityId:a.capability_id,payload:a.payload??{},requestId:a.request_id});
28
42
  case 'revoke': return remoteDevicePlane.revoke({deviceId:a.device_id,epoch:a.epoch});
29
43
  case 'rotate_credential': return remoteDevicePlane.rotateCredential({deviceId:a.device_id,epoch:a.epoch,encryptedBundleBase64:a.encrypted_bundle_base64,expectedPreviousSha256:a.expected_previous_sha256??null});
@@ -33,9 +47,13 @@ export function registerCommanderRemoteBridge(server,{remoteDevicePlane=null}={}
33
47
  });
34
48
  register(server,'remote_mcp',z.strictObject({
35
49
  device_id:z.string().regex(HEX64),epoch:z.number().int().min(1),capability_id:z.string().regex(CAP),
36
- payload:z.record(z.string(),z.unknown()).default({}),request_id:z.string().regex(HEX64)
50
+ payload:z.record(z.string(),z.unknown()).default({}),request_id:z.string().regex(HEX64),
51
+ request_sha256:z.string().regex(HEX64).optional()
37
52
  }),async a=>{
38
53
  const unavailable=requirePlane(remoteDevicePlane,'remote_mcp');if(unavailable)return unavailable;
54
+ if(a.request_sha256&&payloadHash({deviceId:a.device_id,epoch:a.epoch,capabilityId:a.capability_id,payload:a.payload})!==a.request_sha256){
55
+ throw new Error('remote request hash mismatch');
56
+ }
39
57
  return remoteDevicePlane.dispatch({deviceId:a.device_id,epoch:a.epoch,capabilityId:a.capability_id,payload:a.payload,requestId:a.request_id});
40
58
  });
41
59
  }
@@ -1,4 +1,5 @@
1
1
  import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
2
+ import { DEADBYTE_VERSION } from './version.mjs';
2
3
 
3
4
  function insist(ok,message){if(!ok)throw new Error(message);}
4
5
  function structured(result,label){
@@ -8,11 +9,21 @@ function structured(result,label){
8
9
  return value;
9
10
  }
10
11
 
12
+ export function validateRemoteProbeResponseR47(result,{deviceId,epoch,connectionId,nonce}){
13
+ insist(result&&typeof result==='object','remote readiness response missing');
14
+ insist(result.device_id===deviceId,'remote readiness device identity mismatch');
15
+ insist(result.epoch===epoch,'remote readiness epoch mismatch');
16
+ insist(result.connection_id===connectionId,'remote readiness connection identity mismatch');
17
+ insist(result.nonce===nonce,'remote readiness nonce mismatch');
18
+ insist(result.ready===true,'remote readiness response not ready');
19
+ return result;
20
+ }
21
+
11
22
  export async function createCommanderRemoteRelayR47({endpoint,protocolVersion='2026-07-28',timeoutMs=15000}={}){
12
23
  const url=new URL(String(endpoint??''));
13
24
  insist(url.protocol==='https:','remote relay endpoint must use HTTPS');
14
25
  insist(Number.isInteger(timeoutMs)&&timeoutMs>=100&&timeoutMs<=120000,'remote relay timeout invalid');
15
- const client=new Client({name:'deadbyte-r47-remote-relay',version:'0.15.15'},{versionNegotiation:{mode:{pin:protocolVersion}}});
26
+ const client=new Client({name:'deadbyte-r47-remote-relay',version:DEADBYTE_VERSION},{versionNegotiation:{mode:{pin:protocolVersion}}});
16
27
  const transport=new StreamableHTTPClientTransport(url);
17
28
  let connected=false;
18
29
  let connecting=null;
@@ -25,7 +36,7 @@ export async function createCommanderRemoteRelayR47({endpoint,protocolVersion='2
25
36
  async function probe({device_id,epoch,connection_id,nonce}){
26
37
  const value=await call('remote_device',{op:'probe',device_id,epoch,connection_id,nonce},'remote readiness probe');
27
38
  const result=value.result??value;
28
- return {device_id,epoch,nonce,ready:result.ready===true||result.reachable===true};
39
+ return validateRemoteProbeResponseR47(result,{deviceId:device_id,epoch,connectionId:connection_id,nonce});
29
40
  }
30
41
  async function request({device_id,epoch,capability_id,payload,request_id,request_sha256}){
31
42
  insist(typeof request_sha256==='string'&&/^[0-9a-f]{64}$/.test(request_sha256),'remote request hash invalid');
@@ -635,7 +635,11 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
635
635
  const wasAborted=aborter.signal.aborted;
636
636
  aborter.abort();
637
637
  if(logTask) await logTask.catch(()=>{});
638
- timelineWriter.flush();
638
+ timelineWriter.flush();
639
+ const liveLogDelivery=timelineWriter.retention();
640
+ if(liveLogDelivery.suppressed_since_drain>0){
641
+ process.stderr.write(`[WARN] LIVELOG ${liveLogDelivery.suppressed_since_drain} display lines still suppressed at disconnect due to stdout backpressure; inspect signed audit and runtime logs.\n`);
642
+ }
639
643
  for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
640
644
  const filterRetention=timelineFilter?.retention();
641
645
  if(filterRetention?.evicted>0) process.stdout.write(`[WARN] LIVELOG filter evicted ${filterRetention.evicted} presentation identities; full audit remains on disk.\n`);
@@ -0,0 +1,204 @@
1
+ import {createHash,createPublicKey,verify as verifySignature} from 'node:crypto';
2
+ import {lstat,open,readFile} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {verifyGenerationBundle} from './release-generation.mjs';
5
+ import {canonicalJson} from './canonical-json.mjs';
6
+
7
+ const PLANES=['process','coding','autonomous','desktop','host'];
8
+ const GRANT_FIELDS={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
9
+ const HEX=/^[0-9a-f]{64}$/;
10
+ const APPROVAL_DOMAIN=Buffer.from('DEADBYTE-R146-POLICY-MIGRATION-APPROVAL\0','utf8');
11
+ const APPROVAL_SCHEMA='deadbyte.r146-policy-migration-approval.v1';
12
+ const SUBJECT_SCHEMA='deadbyte.r146-policy-migration-subject.v1';
13
+ const APPROVAL_FIELDS=['schema','subject_sha256','owner_key_id','subject','signature_base64'];
14
+ const SUBJECT_FIELDS=['schema','action','predecessor_release_id','predecessor_version','predecessor_manifest_sha256',
15
+ 'predecessor_generation_manifest_sha256','candidate_version','candidate_manifest_sha256','predecessor_policy_sha256',
16
+ 'preflight_subject_sha256','issued_at_utc','expires_at_utc'];
17
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
18
+
19
+ function sameFile(a,b){
20
+ return a.isFile()&&b.isFile()&&!a.isSymbolicLink()&&!b.isSymbolicLink()&&
21
+ a.dev===b.dev&&a.ino===b.ino&&a.size===b.size&&a.mtimeMs===b.mtimeMs&&a.ctimeMs===b.ctimeMs;
22
+ }
23
+ async function readPinned(file,expected,label){
24
+ if(!path.isAbsolute(file)||!HEX.test(expected??''))throw new Error(label+' identity incomplete');
25
+ const before=await lstat(file);
26
+ if(!before.isFile()||before.isSymbolicLink())throw new Error(label+' file invalid');
27
+ const handle=await open(file,'r');
28
+ try{
29
+ const opened=await handle.stat();
30
+ if(!sameFile(before,opened))throw new Error(label+' identity moved');
31
+ const bytes=await handle.readFile();
32
+ const after=await handle.stat(),onPath=await lstat(file);
33
+ if(!sameFile(opened,after)||!sameFile(after,onPath))throw new Error(label+' changed');
34
+ if(sha(bytes)!==expected)throw new Error(label+' hash mismatch');
35
+ return JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
36
+ }finally{await handle.close();}
37
+ }
38
+ async function readPinnedBytes(file,expected,label){
39
+ if(!path.isAbsolute(file)||!HEX.test(expected??''))throw new Error(label+' identity incomplete');
40
+ const before=await lstat(file);
41
+ if(!before.isFile()||before.isSymbolicLink())throw new Error(label+' file invalid');
42
+ const handle=await open(file,'r');
43
+ try{
44
+ const opened=await handle.stat();
45
+ if(!sameFile(before,opened))throw new Error(label+' identity moved');
46
+ const bytes=await handle.readFile();
47
+ const after=await handle.stat(),onPath=await lstat(file);
48
+ if(!sameFile(opened,after)||!sameFile(after,onPath))throw new Error(label+' changed');
49
+ if(sha(bytes)!==expected)throw new Error(label+' hash mismatch');
50
+ return bytes;
51
+ }finally{await handle.close();}
52
+ }
53
+ async function readRegularBytes(file,label){
54
+ if(!path.isAbsolute(file))throw new Error(label+' path invalid');
55
+ const before=await lstat(file);
56
+ if(!before.isFile()||before.isSymbolicLink())throw new Error(label+' file invalid');
57
+ const handle=await open(file,'r');
58
+ try{
59
+ const opened=await handle.stat();
60
+ if(!sameFile(before,opened))throw new Error(label+' identity moved');
61
+ const bytes=await handle.readFile();
62
+ const after=await handle.stat(),onPath=await lstat(file);
63
+ if(!sameFile(opened,after)||!sameFile(after,onPath))throw new Error(label+' changed');
64
+ return bytes;
65
+ }finally{await handle.close();}
66
+ }
67
+ async function mustBeAbsent(file,label){
68
+ try{await lstat(file);throw new Error(label+' present');}
69
+ catch(error){if(error?.code!=='ENOENT')throw error;}
70
+ }
71
+ function exactDrive(root,drive){
72
+ return typeof root?.path==='string'&&path.win32.normalize(root.path).toLowerCase()===drive.toLowerCase();
73
+ }
74
+ function exactObject(value,fields,label){
75
+ if(!value||typeof value!=='object'||Array.isArray(value))throw new Error(label+' invalid');
76
+ const keys=Object.keys(value);
77
+ if(keys.some(key=>!fields.includes(key))||fields.some(key=>!Object.hasOwn(value,key)))throw new Error(label+' shape invalid');
78
+ return value;
79
+ }
80
+ function instant(value,label){
81
+ if(typeof value!=='string'||!/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/.test(value))throw new Error(label+' invalid');
82
+ const parsed=Date.parse(value);
83
+ if(!Number.isFinite(parsed)||new Date(parsed).toISOString()!==value)throw new Error(label+' invalid');
84
+ return parsed;
85
+ }
86
+ async function verifyMigrationApproval({approvalPath,approvalSha256,ownerPublicKeyPath,expected,now}){
87
+ const approval=JSON.parse((await readPinnedBytes(approvalPath,approvalSha256,'policy migration approval')).toString('utf8').replace(/^\uFEFF/,''));
88
+ exactObject(approval,APPROVAL_FIELDS,'policy migration approval');
89
+ const subject=exactObject(approval.subject,SUBJECT_FIELDS,'policy migration approval subject');
90
+ if(approval.schema!==APPROVAL_SCHEMA||subject.schema!==SUBJECT_SCHEMA||subject.action!=='legacy_offline_scope_repair')throw new Error('policy migration approval scope invalid');
91
+ for(const [key,value] of Object.entries(expected)){
92
+ if(key==='predecessor_policy_sha256'){
93
+ if(canonicalJson(subject[key])!==canonicalJson(value))throw new Error('policy migration approval policy binding mismatch');
94
+ }else if(subject[key]!==value)throw new Error('policy migration approval subject mismatch: '+key.replaceAll('_',' '));
95
+ }
96
+ const issued=instant(subject.issued_at_utc,'policy migration approval issued time');
97
+ const expires=instant(subject.expires_at_utc,'policy migration approval expiry');
98
+ const observed=now instanceof Date?now.getTime():NaN;
99
+ if(!Number.isFinite(observed)||expires<=issued||expires-issued>3_600_000||observed<issued||observed>=expires)throw new Error('policy migration approval expired or invalid');
100
+ const subjectSha256=sha(Buffer.from(canonicalJson(subject),'utf8'));
101
+ if(approval.subject_sha256!==subjectSha256)throw new Error('policy migration approval subject hash mismatch');
102
+ const keyBytes=await readRegularBytes(ownerPublicKeyPath,'policy migration owner key');
103
+ let publicKey;
104
+ try{publicKey=createPublicKey(keyBytes);}catch{throw new Error('policy migration owner key invalid');}
105
+ if(publicKey.asymmetricKeyType!=='ed25519')throw new Error('policy migration owner key must be Ed25519');
106
+ const ownerKeyId=sha(publicKey.export({type:'spki',format:'der'}));
107
+ if(approval.owner_key_id!==ownerKeyId)throw new Error('policy migration owner key identity mismatch');
108
+ if(typeof approval.signature_base64!=='string')throw new Error('policy migration approval signature invalid');
109
+ const signature=Buffer.from(approval.signature_base64,'base64');
110
+ const signed={schema:APPROVAL_SCHEMA,subject_sha256:subjectSha256,owner_key_id:ownerKeyId};
111
+ if(signature.length!==64||!verifySignature(null,Buffer.concat([APPROVAL_DOMAIN,Buffer.from(canonicalJson(signed),'utf8')]),publicKey,signature))throw new Error('policy migration approval signature invalid');
112
+ return {subject_sha256:subjectSha256,owner_key_id:ownerKeyId,expires_at_utc:subject.expires_at_utc};
113
+ }
114
+ export async function proveLegacyOfflineScopeRepair({
115
+ stateRoot,pointer,expectedParent,candidate,expectedCandidate,preflightSubjectSha256,
116
+ observation,policyPaths,policySha256,approvalPath,approvalSha256,ownerPublicKeyPath,
117
+ now=new Date()
118
+ }){
119
+ if(!stateRoot||!path.isAbsolute(stateRoot))throw new Error('state root invalid');
120
+ if(!pointer||!expectedParent||!observation)throw new Error('legacy scope proof subject missing');
121
+ if(expectedParent.version!=='0.15.45'||pointer.version!==expectedParent.version||
122
+ pointer.release_id!==expectedParent.release_id||
123
+ pointer.manifest_sha256!==expectedParent.manifest_sha256||
124
+ pointer.generation_manifest_sha256!==expectedParent.generation_manifest_sha256)
125
+ throw new Error('legacy scope proof predecessor mismatch');
126
+ if(!candidate||!expectedCandidate||candidate.version!==expectedCandidate.version||
127
+ candidate.manifest_sha256!==expectedCandidate.manifest_sha256||!HEX.test(candidate.manifest_sha256??''))
128
+ throw new Error('legacy scope proof candidate mismatch');
129
+ if(!HEX.test(preflightSubjectSha256??''))throw new Error('legacy scope proof preflight subject invalid');
130
+ if(observation.state!=='OFFLINE'||observation.local_ready!==false||
131
+ ['bridge','core','cloud'].some(key=>observation[key]?.alive!==false)||
132
+ observation.supervisor?.running!==false)
133
+ throw new Error('legacy scope proof runtime is not cleanly offline');
134
+ if(observation.active_release_id!==pointer.release_id||
135
+ observation.active_manifest_sha256!==pointer.manifest_sha256||
136
+ observation.active_generation_manifest_sha256!==pointer.generation_manifest_sha256)
137
+ throw new Error('legacy scope proof observation generation drift');
138
+ if(!observation.authority||!observation.authority_proof||!observation.policy_sha256)
139
+ throw new Error('legacy scope proof missing authority evidence');
140
+ for(const plane of PLANES){
141
+ const entry=observation.authority[plane],proof=observation.authority_proof[plane];
142
+ const reason=plane==='autonomous'?'unverified':'disarmed';
143
+ const state=plane==='autonomous'?'UNVERIFIED':'VERIFIED_DISARMED';
144
+ if(entry?.configured!==true||entry.armed!==false||entry.reason!==reason||
145
+ proof?.state!==state||proof.grant_absent!==true||
146
+ proof.policy_sha256!==observation.policy_sha256[plane]||
147
+ policySha256?.[plane]!==proof.policy_sha256)
148
+ throw new Error('legacy scope proof authority not physically inactive: '+plane);
149
+ }
150
+ const storedPointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
151
+ if(storedPointer.release_id!==pointer.release_id||
152
+ storedPointer.generation_manifest_sha256!==pointer.generation_manifest_sha256||
153
+ storedPointer.manifest_sha256!==pointer.manifest_sha256)
154
+ throw new Error('legacy scope proof pointer changed');
155
+ await verifyGenerationBundle(pointer.generation_root,pointer.generation_manifest_sha256);
156
+ await mustBeAbsent(path.join(stateRoot,'runtime','remote-session.json'),'foreground ownership marker');
157
+ for(const plane of PLANES)await mustBeAbsent(path.join(stateRoot,'runtime','authority-epochs',plane,'.lock'),plane+' authority epoch lock');
158
+ const policies={};
159
+ for(const plane of PLANES){
160
+ const file=policyPaths?.[plane],digest=policySha256?.[plane];
161
+ if(path.resolve(path.dirname(file??''))!==path.resolve(pointer.generation_root))throw new Error('legacy scope proof policy outside predecessor generation: '+plane);
162
+ policies[plane]=await readPinned(file,digest,plane+' policy');
163
+ const grant=policies[plane][GRANT_FIELDS[plane]];
164
+ if(typeof grant!=='string'||!path.isAbsolute(grant)||
165
+ path.win32.resolve(path.win32.dirname(grant)).toLowerCase()!==
166
+ path.win32.resolve(path.join(stateRoot,'runtime')).toLowerCase())
167
+ throw new Error('legacy scope proof grant path invalid: '+plane);
168
+ await mustBeAbsent(grant,plane+' grant');
169
+ }
170
+ const coding=policies.coding,autonomous=policies.autonomous;
171
+ if(coding.schema!=='deadbyte.coding-policy.v1'||autonomous.schema!=='deadbyte.autonomous-policy.v1'||
172
+ coding.enabled!==true||autonomous.enabled!==true||
173
+ autonomous.coding_policy_sha256!==policySha256.coding)
174
+ throw new Error('legacy scope proof Coding/Autonomous binding invalid');
175
+ if(!exactDrive(coding.roots?.local_c,'C:\\')||!exactDrive(coding.roots?.local_d,'D:\\'))
176
+ throw new Error('legacy scope proof expected drive roots missing');
177
+ for(const [id,root] of Object.entries(coding.roots)){
178
+ const normalized=path.win32.normalize(String(root?.path??''));
179
+ if(/^[a-z]:\\$/i.test(normalized)&&!['local_c','local_d'].includes(id))
180
+ throw new Error('legacy scope proof unexpected drive root: '+id);
181
+ }
182
+ if(!Array.isArray(autonomous.root_ids)||autonomous.root_ids.length===0||
183
+ autonomous.root_ids.some(id=>id==='local_c'||id==='local_d'||!Object.hasOwn(coding.roots,id)))
184
+ throw new Error('legacy scope proof autonomous roots unsafe');
185
+ const approval=await verifyMigrationApproval({approvalPath,approvalSha256,ownerPublicKeyPath,now,expected:{
186
+ predecessor_release_id:pointer.release_id,predecessor_version:pointer.version,
187
+ predecessor_manifest_sha256:pointer.manifest_sha256,
188
+ predecessor_generation_manifest_sha256:pointer.generation_manifest_sha256,
189
+ candidate_version:candidate.version,candidate_manifest_sha256:candidate.manifest_sha256,
190
+ predecessor_policy_sha256:policySha256,preflight_subject_sha256:preflightSubjectSha256
191
+ }});
192
+ const subject={release_id:pointer.release_id,version:pointer.version,
193
+ generation_manifest_sha256:pointer.generation_manifest_sha256,
194
+ coding_policy_sha256:policySha256.coding,autonomous_policy_sha256:policySha256.autonomous,
195
+ candidate_version:candidate.version,candidate_manifest_sha256:candidate.manifest_sha256,
196
+ preflight_subject_sha256:preflightSubjectSha256,owner_approval_subject_sha256:approval.subject_sha256,
197
+ grant_absent_planes:PLANES};
198
+ return Object.freeze({
199
+ state:'VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR',
200
+ ...subject,owner_approval_verified:true,owner_key_id:approval.owner_key_id,
201
+ approval_expires_at_utc:approval.expires_at_utc,
202
+ subject_sha256:sha(Buffer.from(canonicalJson(subject),'utf8'))
203
+ });
204
+ }
@@ -51,11 +51,30 @@ export async function verifyGenerationBundle(root,expectedManifestSha256){
51
51
  return {files,generation_manifest_sha256:manifestSha256};
52
52
  }
53
53
 
54
- function rebindCodingPolicy(raw,{developmentRoot,stateRoot,parityScratchRoot}){
54
+ export function rebindCodingPolicy(raw,{developmentRoot,stateRoot,parityScratchRoot}){
55
55
  const policy=clone(raw);
56
56
  policy.lease_file=path.join(stateRoot,'runtime','coding-grant.json');
57
57
  policy.evidence_dir=path.join(stateRoot,'evidence','coding');
58
58
  if(!policy.roots?.core) throw new Error('coding policy requires core root for R27');
59
+ // A prior generation may contain legacy whole-drive roots. Never let those
60
+ // capabilities propagate into a new signed release generation.
61
+ const removed=new Set();
62
+ for(const [id,root] of Object.entries(policy.roots)){
63
+ if(typeof root?.path!=='string') throw new Error('coding root path invalid: '+id);
64
+ const full=path.win32.normalize(root.path),drive=path.win32.parse(full).root;
65
+ if(!/^[a-z]:\\$/i.test(drive)||full.toLowerCase()!==drive.toLowerCase()) continue;
66
+ if(!((id==='local_c'&&drive.toLowerCase()==='c:\\')||
67
+ (id==='local_d'&&drive.toLowerCase()==='d:\\')))
68
+ throw new Error('unrecognized whole-drive Coding root: '+id);
69
+ removed.add(id);
70
+ delete policy.roots[id];
71
+ }
72
+ for(const [id,command] of Object.entries(policy.commands??{})){
73
+ if(!Array.isArray(command.root_ids)) continue;
74
+ const next=command.root_ids.filter(rootId=>!removed.has(rootId));
75
+ if(next.length===0) throw new Error('Coding command would lose every scoped root: '+id);
76
+ command.root_ids=next;
77
+ }
59
78
  policy.roots.core.path=developmentRoot;
60
79
  policy.roots.parity_scratch={path:parityScratchRoot,read:true,write:true,exec:false,deny_paths:[]};
61
80
  return policy;
@@ -73,7 +73,7 @@ async function withTimeout(operation,timeoutMs,label){
73
73
  ]);
74
74
  }finally{if(timer)clearTimeout(timer);}
75
75
  }
76
- function payloadHash({deviceId,epoch,capabilityId,payload}){
76
+ export function payloadHash({deviceId,epoch,capabilityId,payload}){
77
77
  return sha(Buffer.from(canonicalJson({device_id:deviceId,epoch,capability_id:capabilityId,payload}),'utf8'));
78
78
  }
79
79
 
@@ -325,9 +325,40 @@ export function createLiveTimelineWriter({stream=process.stdout,displayMode='uni
325
325
  if(!stream||typeof stream.write!=='function') throw new Error('timeline writer stream invalid');
326
326
  if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('timeline writer display mode invalid');
327
327
  if(typeof rewrite!=='boolean') throw new Error('timeline writer rewrite invalid');
328
- let active=null;
328
+ let active=null,congested=false,needsNewline=false,suppressedSinceDrain=0,totalSuppressed=0;
329
+ // Display output is lossy under a stalled consumer; the on-disk signed audit
330
+ // and runtime logs remain authoritative. Do not enqueue unbounded text in
331
+ // Node's Writable internal buffer after write() returns false.
332
+ function emit(chunk){
333
+ const ok=stream.write(chunk);
334
+ if(ok===false){
335
+ if(typeof stream.once!=='function') throw new Error('timeline writer requires drain-capable stream under backpressure');
336
+ congested=true;
337
+ stream.once('drain',onDrain);
338
+ }
339
+ }
340
+ function onDrain(){
341
+ congested=false;
342
+ if(active||needsNewline){
343
+ active=null;needsNewline=false;
344
+ emit('\n');
345
+ if(congested) return;
346
+ }
347
+ if(suppressedSinceDrain){
348
+ const skipped=suppressedSinceDrain;
349
+ suppressedSinceDrain=0;
350
+ emit('[WARN] LIVELOG '+skipped+' presentation lines suppressed by stdout backpressure; inspect signed audit and runtime logs.\n');
351
+ }
352
+ }
353
+ function noteSuppressed(){
354
+ suppressedSinceDrain++;
355
+ totalSuppressed++;
356
+ }
329
357
  function flush(){
330
- if(active){stream.write('\n');active=null;}
358
+ if(!active) return;
359
+ active=null;
360
+ if(congested){needsNewline=true;return;}
361
+ emit('\n');
331
362
  }
332
363
  // Inspect terminal width for every emission: resize must not erase a wrapped prior line.
333
364
  // Unknown Unicode cell width falls back to append-only output, never truncation.
@@ -337,20 +368,24 @@ export function createLiveTimelineWriter({stream=process.stdout,displayMode='uni
337
368
  }
338
369
  function write(line,{event_type=null,request_id=null}={}){
339
370
  if(typeof line!=='string'||!line) return;
371
+ if(congested){noteSuppressed();return;}
340
372
  const tty=stream.isTTY===true&&rewrite;
341
373
  if(!tty||!fitsOneRow(line)){
342
374
  if(active) flush();
343
- stream.write(line+'\n');
375
+ if(congested){noteSuppressed();return;}
376
+ emit(line+'\n');
344
377
  return;
345
378
  }
346
379
  if(active&&event_type==='tool.result'&&request_id&&request_id===active.request_id){
347
- stream.write('\r\x1b[2K'+line+'\n');active=null;return;
380
+ active=null;emit('\r\x1b[2K'+line+'\n');return;
348
381
  }
349
382
  if(active) flush();
350
- if(event_type==='tool.call'&&request_id){stream.write(line);active={request_id};return;}
351
- stream.write(line+'\n');
383
+ if(congested){noteSuppressed();return;}
384
+ if(event_type==='tool.call'&&request_id){emit(line);active={request_id};return;}
385
+ emit(line+'\n');
352
386
  }
353
- return Object.freeze({write,flush,pending(){return active?{...active}:null;}});
387
+ return Object.freeze({write,flush,pending(){return active?{...active}:null;},
388
+ retention(){return Object.freeze({congested,suppressed_since_drain:suppressedSinceDrain,total_suppressed:totalSuppressed});}});
354
389
  }
355
390
 
356
391
  export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,foldHeartbeats=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false,slowCallMs=1000}={}){
@@ -549,6 +584,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,fo
549
584
  (p50!==null?' | p50 '+formatDuration(p50)+' | p95 '+formatDuration(p95)+' | p99 '+formatDuration(p99)+' | sample '+durationSamples.length+'/'+durationObservedCount:'')+
550
585
  (slowest?' | slowest '+slowest.tool+' '+formatDuration(slowest.duration_ms):'')+
551
586
  ' | peak in-flight '+peakInFlight+
587
+ (pendingOverflow?' | '+pendingOverflow+' presentation spans untracked (overflow); inspect signed audit':'')+
552
588
  (foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
553
589
  (top?' | top: '+top:''),displayMode);
554
590
  }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.53';
1
+ export const DEADBYTE_VERSION = '0.15.55';
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';
@@ -14,12 +14,13 @@ function fakePlane(){
14
14
 
15
15
  test('R47 remote Commander bridge routes remote_device directly to remote-device-r44 plane',async()=>{
16
16
  const server=fakeServer(),plane=fakePlane();
17
+ plane.probe=async value=>{plane.calls.push({op:'probe',value});return {device_id:value.deviceId,epoch:value.epoch,connection_id:'c',reachable:true};};
17
18
  registerCommanderRemoteBridge(server,{remoteDevicePlane:plane});
18
19
  assert.ok(server.tools.has('remote_device'));assert.ok(server.tools.has('remote_mcp'));
19
20
  const tool=server.tools.get('remote_device').handler;const id='a'.repeat(64);
20
21
  await tool({op:'pair',public_key_spki_base64:'ZmFrZQ==',display_name:'lab'});
21
22
  await tool({op:'status',device_id:id});await tool({op:'report_online',device_id:id,epoch:1,connection_id:'c'});
22
- await tool({op:'probe',device_id:id,epoch:1});await tool({op:'dispatch',device_id:id,epoch:1,capability_id:'machine.status',payload:{},request_id:'b'.repeat(64)});
23
+ await tool({op:'probe',device_id:id,epoch:1,connection_id:'c',nonce:'f'.repeat(64)});await tool({op:'dispatch',device_id:id,epoch:1,capability_id:'machine.status',payload:{},request_id:'b'.repeat(64)});
23
24
  await tool({op:'revoke',device_id:id,epoch:1});await tool({op:'rotate_credential',device_id:id,epoch:1,encrypted_bundle_base64:'dG9rZW4=',expected_previous_sha256:null});await tool({op:'list'});
24
25
  assert.deepEqual(plane.calls.map(x=>x.op),['pair','status','report_online','probe','dispatch','revoke','rotate_credential','list']);
25
26
  });
@@ -37,3 +38,21 @@ test('R47 remote surfaces fail closed as unavailable when no remote-device-r44 p
37
38
  const result=await server.tools.get('remote_device').handler({op:'list'});
38
39
  assert.equal(result.status,'unavailable');assert.match(result.error,/remote-device-r44 plane is not configured/);
39
40
  });
41
+
42
+ test('R146 remote readiness bridge binds the caller nonce and exact live connection',async()=>{
43
+ const server=fakeServer(),id='e'.repeat(64),nonce='f'.repeat(64),calls=[];
44
+ const plane={
45
+ async probe(value){calls.push(value);return {device_id:id,epoch:3,connection_id:'conn-3',reachable:true};}
46
+ };
47
+ registerCommanderRemoteBridge(server,{remoteDevicePlane:plane});
48
+ const registered=server.tools.get('remote_device');
49
+ const parsed=registered.meta.inputSchema.safeParse({op:'probe',device_id:id,epoch:3,connection_id:'conn-3',nonce});
50
+ assert.equal(parsed.success,true,'strict remote_device schema must accept the readiness challenge');
51
+ const result=await registered.handler(parsed.data);
52
+ assert.deepEqual(calls,[{deviceId:id,epoch:3}]);
53
+ assert.equal(result.device_id,id);assert.equal(result.epoch,3);
54
+ assert.equal(result.connection_id,'conn-3');assert.equal(result.nonce,nonce);assert.equal(result.ready,true);
55
+
56
+ plane.probe=async()=>({device_id:id,epoch:3,connection_id:'superseded',reachable:true});
57
+ await assert.rejects(registered.handler(parsed.data),/connection identity mismatch/i);
58
+ });
@@ -2,6 +2,7 @@ import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { createHash } from 'node:crypto';
5
+ import { validateRemoteProbeResponseR47 } from '../src/commander-remote-relay-r47.mjs';
5
6
 
6
7
  const sha=value=>createHash('sha256').update(value).digest('hex');
7
8
 
@@ -23,3 +24,13 @@ test('R47 relay is native MCP client and contains no Desktop Commander dependenc
23
24
  assert.doesNotMatch(relay,/@wonderwhy-er\/desktop-commander/);
24
25
  assert.equal(sha(relay).length,64);
25
26
  });
27
+
28
+ test('R146 remote relay rejects fabricated or stale readiness acknowledgments',()=>{
29
+ const deviceId='a'.repeat(64),nonce='b'.repeat(64),connectionId='conn-live';
30
+ const good={device_id:deviceId,epoch:4,connection_id:connectionId,nonce,ready:true};
31
+ assert.deepEqual(validateRemoteProbeResponseR47(good,{deviceId,epoch:4,connectionId,nonce}),good);
32
+ assert.throws(()=>validateRemoteProbeResponseR47({...good,nonce:'c'.repeat(64)},{deviceId,epoch:4,connectionId,nonce}),/nonce mismatch/i);
33
+ assert.throws(()=>validateRemoteProbeResponseR47({...good,connection_id:'conn-stale'},{deviceId,epoch:4,connectionId,nonce}),/connection identity mismatch/i);
34
+ assert.throws(()=>validateRemoteProbeResponseR47({...good,device_id:'d'.repeat(64)},{deviceId,epoch:4,connectionId,nonce}),/device identity mismatch/i);
35
+ assert.throws(()=>validateRemoteProbeResponseR47({...good,ready:false},{deviceId,epoch:4,connectionId,nonce}),/not ready/i);
36
+ });
@@ -76,18 +76,17 @@ test('R121 correctly signed restore after user revoke is still an invalid author
76
76
  await assert.rejects(candidate.auditHistory({plane:'process'}), /authority epoch transition invalid/);
77
77
  assert.equal((await readFile(path.join(dir, 'events', eventFile))).equals(eventBytes), true);
78
78
 
79
- // An older source can append a fresh grant over this forged head because
80
- // its immediate-predecessor check lacks transition semantics. The new
81
- // hot path checks the newest pair, while explicit audit must detect the
82
- // invalid transition that is now deeper in history.
79
+ // Current protected writers must reject the already-forged transition
80
+ // rather than extending it and making the corrupted head appear trusted.
83
81
  const finalNonce = randomBytes(32).toString('hex');
84
82
  const finalGrant = Buffer.from(JSON.stringify({instance_nonce:finalNonce, fixture:'R121 final'}));
85
- const finalHead = await writer.append({plane:'process', action:'grant',
86
- grantBytes:finalGrant, grantInstanceNonce:finalNonce});
87
- assert.equal((await candidate.readHead({plane:'process'})).event_sha256, finalHead.event_sha256);
83
+ await assert.rejects(writer.append({plane:'process', action:'grant',
84
+ grantBytes:finalGrant, grantInstanceNonce:finalNonce}), /authority epoch transition invalid/);
85
+ await assert.rejects(candidate.readHead({plane:'process'}), /authority epoch transition invalid/);
88
86
  assert.equal((await candidate.verifyGrant({plane:'process', grantBytes:finalGrant,
89
- grantInstanceNonce:finalNonce})).trusted, true);
87
+ grantInstanceNonce:finalNonce})).trusted, false);
90
88
  await assert.rejects(candidate.auditHistory({plane:'process'}), /authority epoch transition invalid/);
89
+ assert.equal((await readFile(path.join(dir, 'events', eventFile))).equals(eventBytes), true);
91
90
  } finally {
92
91
  await rm(root, {recursive:true, force:true, maxRetries:8, retryDelay:100});
93
92
  }