deadbyte-mcp 0.15.65 → 0.15.66

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 (57) hide show
  1. package/MANIFEST.SHA256 +56 -19
  2. package/PROVENANCE.json +19 -7
  3. package/README.txt +31 -18
  4. package/RELEASE.json +25 -7
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +32 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-process-policy.json +4 -4
  9. package/docs/R168-ACTIVATION-RECONCILIATION-BLOCKER-20261026.md +39 -0
  10. package/docs/R168-CRASH-JOURNAL-PROTECTED-PREFLIGHT-20261026.md +46 -0
  11. package/docs/R168-FIVE-PLANE-FENCE-20261026.md +41 -0
  12. package/docs/R168-NATIVE-EXCLUSION-TERMINAL-CONTRACT-20261026.md +53 -0
  13. package/docs/R168-NPX-REMOTE-SKEW-HOTFIX-20261026.md +41 -0
  14. package/docs/R168-READONLY-AUDIT-AND-RESUME-20261026.md +56 -0
  15. package/docs/R168-SIGNED-RECONCILIATION-PRECOMMIT-20261026.md +45 -0
  16. package/package.json +4 -2
  17. package/scripts/gate-windows-r168.ps1 +75 -0
  18. package/scripts/npm-release-r48.mjs +23 -3
  19. package/scripts/npm-release-readiness-r168.mjs +70 -0
  20. package/scripts/r168-audit-readonly.mjs +32 -0
  21. package/scripts/r168-npm-registry-readonly.mjs +19 -0
  22. package/scripts/r168-protected-preflight.mjs +65 -0
  23. package/scripts/r168-release-scan.mjs +28 -0
  24. package/scripts/r168-source-full-test-runner.mjs +46 -0
  25. package/scripts/r168-subject-readonly.mjs +49 -0
  26. package/src/activation-reconciliation-audit-r168.mjs +200 -0
  27. package/src/activation-reconciliation-consent-r168.mjs +235 -0
  28. package/src/activation-reconciliation-fence-r168.mjs +87 -0
  29. package/src/activation-reconciliation-journal-r168.mjs +100 -0
  30. package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
  31. package/src/activation-reconciliation-terminal-r168.mjs +111 -0
  32. package/src/deadbyte-cli.mjs +20 -0
  33. package/src/npm-publication-resume-r168.mjs +25 -0
  34. package/src/remote-one-shot-coherence-r168.mjs +36 -0
  35. package/src/version.mjs +1 -1
  36. package/test/r130-release-security-contract.test.mjs +7 -7
  37. package/test/r132-release-identity.test.mjs +7 -7
  38. package/test/r153-prepublish-identity.test.mjs +1 -1
  39. package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
  40. package/test/r165-live-cli-skew.test.mjs +7 -7
  41. package/test/r167-release-contract.test.mjs +10 -10
  42. package/test/r168-combined-targeted-runner.mjs +14 -0
  43. package/test/r168-crash-journal.test.mjs +149 -0
  44. package/test/r168-five-plane-fence-runner.mjs +11 -0
  45. package/test/r168-five-plane-fence.test.mjs +128 -0
  46. package/test/r168-focused-suite.test.mjs +4 -0
  47. package/test/r168-native-five-plane-runner.mjs +11 -0
  48. package/test/r168-native-five-plane-scratch.test.mjs +49 -0
  49. package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
  50. package/test/r168-npm-resume-identity.test.mjs +49 -0
  51. package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
  52. package/test/r168-protected-entrypoint.test.mjs +53 -0
  53. package/test/r168-reconciliation-audit.test.mjs +42 -0
  54. package/test/r168-release-contract.test.mjs +56 -0
  55. package/test/r168-remote-one-shot-coherence.test.mjs +65 -0
  56. package/test/r59-tool-surface-compat.test.mjs +1 -1
  57. package/test/r63-one-shot-coherence.test.mjs +10 -5
@@ -0,0 +1,87 @@
1
+ // R168 five-plane R85 exclusion, with a deliberately non-injectable production
2
+ // entrypoint. Simulation is ONLY a pure test of sequencing: no production proof.
3
+ import {randomBytes} from 'node:crypto';
4
+ import {lstat} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {canonicalJson} from './canonical-json.mjs';
7
+ import {acquireHeldEpochLock} from './epoch-lock-r85.mjs';
8
+ import {queryForegroundIdentity,validForegroundIdentity} from './foreground-process-identity.mjs';
9
+
10
+ const PLANES=Object.freeze(['process','coding','autonomous','desktop','host']);
11
+ const validate=(stateRoot,onHeld)=>{
12
+ if(typeof onHeld!=='function'||typeof stateRoot!=='string'||!path.isAbsolute(stateRoot))
13
+ throw new Error('R168 held epoch fence arguments invalid');
14
+ };
15
+
16
+ async function runHeldFence({stateRoot,onHeld,acquire,owner}){
17
+ validate(stateRoot,onHeld);
18
+ if(typeof acquire!=='function'||!validForegroundIdentity(owner)||owner.pid!==process.pid)
19
+ throw new Error('R168 native owner process identity unavailable');
20
+ const root=path.resolve(stateRoot),runtime=path.join(root,'runtime','authority-epochs');
21
+ const folders=PLANES.map(plane=>({plane,dir:path.join(runtime,plane)}));
22
+ // Preflight the entire directory set before acquiring any lock. Never make
23
+ // missing directories or silently repair a stale owner in reconciliation.
24
+ for(const entry of folders){
25
+ const stat=await lstat(entry.dir);
26
+ if(!stat.isDirectory()||stat.isSymbolicLink())
27
+ throw new Error('R168 epoch plane directory untrusted or unavailable: '+entry.plane);
28
+ }
29
+ const leases=[];
30
+ let started=false,primary=null,result;
31
+ try{
32
+ for(const entry of folders){
33
+ const bytes=Buffer.from(canonicalJson({
34
+ schema:'deadbyte.authority-epoch-lock.r65.v1',
35
+ pid:process.pid,token:randomBytes(16).toString('hex'),
36
+ created_at_utc:new Date().toISOString(),owner_identity:owner
37
+ }),'utf8');
38
+ const lease=await acquire(path.join(entry.dir,'.lock'),bytes);
39
+ if(!lease||typeof lease.release!=='function')
40
+ throw new Error('R168 epoch native lock busy: '+entry.plane);
41
+ leases.push({plane:entry.plane,lease});
42
+ }
43
+ started=true;
44
+ result=await onHeld(Object.freeze({
45
+ planeOrder:PLANES,
46
+ assertHeld:()=>{
47
+ if(!started||leases.length!==PLANES.length)
48
+ throw new Error('R168 five-plane native exclusion not held');
49
+ return true;
50
+ }
51
+ }));
52
+ }catch(error){primary=error;}
53
+ const releaseErrors=[];
54
+ for(const {plane,lease} of leases.reverse()){
55
+ try{await lease.release();}
56
+ catch(error){releaseErrors.push(new Error('R168 failed to release '+plane+' native epoch lock',{cause:error}));}
57
+ }
58
+ if(releaseErrors.length&&primary)
59
+ throw new AggregateError([primary,...releaseErrors],'R168 native epoch fence and cleanup ambiguous');
60
+ if(releaseErrors.length)throw new AggregateError(releaseErrors,'R168 native epoch fence release ambiguous');
61
+ if(primary)throw primary;
62
+ return result;
63
+ }
64
+
65
+ // Only this entrypoint can be used by any future trusted reconciliation commit.
66
+ // The R85 loader itself enforces R89 and native binary/source identity.
67
+ export async function withR168FivePlaneEpochFence(options={}){
68
+ if(!options||typeof options!=='object'||Array.isArray(options)||
69
+ Object.keys(options).some(key=>!['stateRoot','onHeld'].includes(key)))
70
+ throw new Error('R168 protected fence forbids injected or unknown options');
71
+ validate(options.stateRoot,options.onHeld);
72
+ if(process.platform!=='win32')throw new Error('R168 production epoch exclusion requires Windows');
73
+ const owner=queryForegroundIdentity(process.pid);
74
+ return runHeldFence({...options,owner,acquire:acquireHeldEpochLock});
75
+ }
76
+
77
+ // Non-authoritative simulation only. Deliberately NOT called by production
78
+ // entrypoint; test-worker environment is not a security boundary.
79
+ export async function simulateR168FivePlaneEpochFence({
80
+ stateRoot,onHeld,testAcquire,testOwnerIdentity
81
+ }={}){
82
+ if(process.env.NODE_TEST_CONTEXT!=='child-v8')
83
+ throw new Error('R168 simulation requires a Node test worker');
84
+ if(typeof testAcquire!=='function')
85
+ throw new Error('R168 simulation requires a fake acquire');
86
+ return runHeldFence({stateRoot,onHeld,acquire:testAcquire,owner:testOwnerIdentity});
87
+ }
@@ -0,0 +1,100 @@
1
+ // R168 fail-closed, non-mutating reconciliation intent journal.
2
+ // This is a standalone receipt contract. Production CAS MUST NOT use it
3
+ // without a trusted R89 entrypoint, held five-plane R85 locks, actual owner
4
+ // approval and native FileIdInfo observations. No delete/revoke here.
5
+ import {createHash} from 'node:crypto';
6
+ import {lstat,open,readFile,realpath} from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import {canonicalJson} from './canonical-json.mjs';
9
+ import {verifyR168SignedAuthorization} from './activation-reconciliation-consent-r168.mjs';
10
+ import {verifyR168TerminalResult} from './activation-reconciliation-terminal-r168.mjs';
11
+
12
+ const SHA=/^[a-f0-9]{64}$/;
13
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
14
+ const stable=body=>Buffer.from(canonicalJson(body)+'\n','utf8');
15
+ function check(condition,reason){if(!condition)throw new Error('R168 journal '+reason);}
16
+ async function normalDirectory(value){
17
+ check(typeof value==='string'&&path.isAbsolute(value),'evidence directory must be absolute');
18
+ const dir=path.resolve(value),stat=await lstat(dir);
19
+ check(stat.isDirectory()&&!stat.isSymbolicLink(),'evidence directory not ordinary');
20
+ check(path.resolve(await realpath(dir)).toLowerCase()===dir.toLowerCase(),
21
+ 'evidence directory reparse denied');
22
+ return dir;
23
+ }
24
+ function content(subject,approval,prepared){
25
+ const body={schema:'deadbyte.r168-terminal-intent.v1',
26
+ phase:'INTENT_PREPARED_ONLY',
27
+ subject_sha256:subject.subject_sha256,
28
+ claim_operation_id:subject.claim_operation_id,
29
+ claim_file_id:subject.claim_file_id,
30
+ claim_file_sha256:subject.claim_file_sha256,
31
+ predecessor_pointer_sha256:subject.predecessor_pointer_sha256,
32
+ owner_approval_nonce:approval.body.nonce,
33
+ owner_approval_sha256:sha(Buffer.from(canonicalJson(approval))),
34
+ prepared_receipt_sha256:prepared.receipt_sha256,
35
+ terminalization_committed:false,activation_authorized:false};
36
+ return {body,body_sha256:sha(Buffer.from(canonicalJson(body)))};
37
+ }
38
+ export async function prepareR168TerminalIntent({
39
+ evidenceDir,subject,approval,prepared,ownerPublicKey,machinePublicKey,now
40
+ }={}){
41
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,
42
+ ownerPublicKey,machinePublicKey,now});
43
+ const dir=await normalDirectory(evidenceDir);
44
+ check(SHA.test(approval.body.nonce),'owner approval nonce invalid');
45
+ const file=path.join(dir,'r168-terminal-intent-'+approval.body.nonce+'.json');
46
+ const encoded=stable(content(subject,approval,prepared));
47
+ check(encoded.length<32768,'intent size exceeded');
48
+ let handle;
49
+ try{handle=await open(file,'wx',0o600);}
50
+ catch(error){if(error?.code==='EEXIST')throw new Error('R168 journal one-use intent already exists; no-clobber');throw error;}
51
+ try{
52
+ // A write or fsync error leaves any incomplete intent in place to block
53
+ // replay. We never rename/rewrite it to appear as a successful receipt.
54
+ await handle.writeFile(encoded);
55
+ await handle.sync();
56
+ }finally{await handle.close();}
57
+ const checkBytes=await readFile(file);
58
+ check(checkBytes.equals(encoded),'intent changed during readback');
59
+ return Object.freeze({file,sha256:sha(encoded),bytes:encoded.length,
60
+ state:'INTENT_PREPARED_ONLY',activation_authorized:false});
61
+ }
62
+ export async function classifyR168Recovery({
63
+ recordFile,subject,approval,prepared,ownerPublicKey,machinePublicKey,
64
+ observedClaim,terminal=null,after=null,now
65
+ }={}){
66
+ // Historical validity of a recorded intent remains verifiable after the
67
+ // one-time owner's approval window expires. This DOES NOT grant fresh consent.
68
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,ownerPublicKey,machinePublicKey,
69
+ now:prepared?.body?.created_at_utc});
70
+ check(typeof recordFile==='string'&&path.isAbsolute(recordFile),'record path invalid');
71
+ const stat=await lstat(recordFile);
72
+ check(stat.isFile()&&!stat.isSymbolicLink()&&stat.size>0&&stat.size<=32768,
73
+ 'journal file is not regular or bounded');
74
+ const original=await readFile(recordFile);
75
+ check(original.length===stat.size,'journal file changed during audit');
76
+ let record;
77
+ try{record=JSON.parse(original.toString('utf8'));}catch{throw new Error('R168 journal damaged JSON');}
78
+ const expected=content(subject,approval,prepared);
79
+ check(record?.body_sha256===expected.body_sha256&&
80
+ canonicalJson(record.body)===canonicalJson(expected.body)&&
81
+ sha(Buffer.from(canonicalJson(record.body)))===record.body_sha256,
82
+ 'journal digest/identity tampered');
83
+ check(observedClaim&&typeof observedClaim==='object','claim observation absent');
84
+ if(observedClaim.state==='PRESENT'){
85
+ if(terminal)throw new Error('R168 journal terminal/claim state ambiguous');
86
+ const same=observedClaim.fileId===subject.claim_file_id&&
87
+ observedClaim.sha256===subject.claim_file_sha256;
88
+ return Object.freeze({state:same?'PRECOMMIT_CLAIM_PRESENT_BLOCKED':'CLAIM_CHANGED_BLOCKED',
89
+ activation_authorized:false,terminalization_committed:false});
90
+ }
91
+ check(observedClaim.state==='ABSENT','claim observation unverified');
92
+ if(!terminal)return Object.freeze({state:'CAS_OUTCOME_UNKNOWN_BLOCKED',
93
+ activation_authorized:false,terminalization_committed:false});
94
+ check(after,'terminal post-CAS evidence missing');
95
+ verifyR168TerminalResult({subject,approval,prepared,terminal,
96
+ ownerPublicKey,machinePublicKey,after,now});
97
+ return Object.freeze({state:'TERMINAL_VERIFIED_ACTIVATION_SEPARATE',
98
+ activation_authorized:false,terminalization_committed:true,
99
+ terminal_receipt_sha256:terminal.receipt_sha256});
100
+ }
@@ -0,0 +1,42 @@
1
+ // R168 exclusive storage for *authorization-only* receipts.
2
+ // Does not sign an owner approval, terminalize claims or invoke native deletion.
3
+ import {createHash} from 'node:crypto';
4
+ import {lstat,open,readFile,realpath} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {verifyR168SignedAuthorization} from './activation-reconciliation-consent-r168.mjs';
7
+
8
+ const SHA=/^[a-f0-9]{64}$/;
9
+ const digest=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+
11
+ export async function persistR168OneUseAuthorization({
12
+ evidenceDir,subject,approval,receipt,ownerPublicKey,machinePublicKey,now
13
+ }={}){
14
+ verifyR168SignedAuthorization({subject,approval,receipt,ownerPublicKey,machinePublicKey,now});
15
+ if(typeof evidenceDir!=='string'||!path.isAbsolute(evidenceDir))
16
+ throw new Error('R168 receipt evidence directory must be absolute');
17
+ const dir=path.resolve(evidenceDir);
18
+ const stats=await lstat(dir);
19
+ if(!stats.isDirectory()||stats.isSymbolicLink()||path.resolve(await realpath(dir))!==dir)
20
+ throw new Error('R168 receipt directory not a real bounded directory');
21
+ const nonce=approval.body.nonce;
22
+ if(!SHA.test(String(nonce)))throw new Error('R168 receipt one-use nonce invalid');
23
+ const file=path.join(dir,'r168-reconciliation-authorization-'+nonce+'.json');
24
+ const data=Buffer.from(JSON.stringify({
25
+ schema:'deadbyte.r168-reconciliation-authorization-record.v1',
26
+ status:'PREPARED_ONLY',
27
+ owner_approval:approval,
28
+ receipt
29
+ })+'\n','utf8');
30
+ if(data.length>32768)throw new Error('R168 authorization receipt oversized');
31
+ let handle;
32
+ try{handle=await open(file,'wx',0o600);}
33
+ catch(error){if(error?.code==='EEXIST')throw new Error('R168 one-use receipt already exists; no-clobber');throw error;}
34
+ try{
35
+ await handle.writeFile(data);
36
+ await handle.sync();
37
+ }finally{await handle.close();}
38
+ const observed=await readFile(file);
39
+ if(!observed.equals(data))throw new Error('R168 one-use authorization receipt changed during verification');
40
+ return Object.freeze({path:file,sha256:digest(data),bytes:data.length,
41
+ terminalization_committed:false,activation_authorized:false});
42
+ }
@@ -0,0 +1,111 @@
1
+ // R168 cryptographic terminal-result contract. Pure: no CAS, filesystem,
2
+ // owner-key discovery or authority mutation occurs here. A protected caller
3
+ // must obtain every 'after' field from native handle-bound observations under
4
+ // its five-plane epoch fence before invoking this contract.
5
+ import {createHash,createPublicKey,sign,verify} from 'node:crypto';
6
+ import {canonicalJson} from './canonical-json.mjs';
7
+ import {verifyR168SignedAuthorization} from './activation-reconciliation-consent-r168.mjs';
8
+
9
+ const DOMAIN='DEADBYTE-R168-TERMINAL-RESULT-V1';
10
+ const SHA=/^[0-9a-f]{64}$/;
11
+ const PLANES=['process','coding','autonomous','desktop','host'];
12
+ const hash=body=>createHash('sha256').update(Buffer.from(canonicalJson(body),'utf8')).digest('hex');
13
+ const message=body=>Buffer.concat([Buffer.from(DOMAIN+'\0','utf8'),
14
+ Buffer.from(canonicalJson(body),'utf8')]);
15
+ function requireR168(ok,reason){if(!ok)throw new Error('R168 terminal '+reason);}
16
+
17
+ function verifiedAfter(subject,after){
18
+ requireR168(after&&typeof after==='object'&&!Array.isArray(after)&&
19
+ Object.keys(after).length===8&&
20
+ ['native_cas_result','claim_absent','pointer_sha256','old_outcome_sha256',
21
+ 'coding_policy_sha256','coding_grant_sha256','coding_epoch_sha256','epoch_history']
22
+ .every(k=>Object.hasOwn(after,k)),'post-CAS evidence field shape invalid');
23
+ requireR168(after.native_cas_result==='REMOVED'&&after.claim_absent===true,
24
+ 'claim native CAS removal and absence not verified');
25
+ requireR168(after.pointer_sha256===subject.predecessor_pointer_sha256,
26
+ 'predecessor pointer changed');
27
+ requireR168(after.old_outcome_sha256===subject.old_outcome_sha256,
28
+ 'old outcome evidence changed');
29
+ requireR168(after.coding_policy_sha256===subject.coding_policy_sha256&&
30
+ after.coding_grant_sha256===subject.coding_grant_sha256&&
31
+ after.coding_epoch_sha256===subject.coding_epoch_sha256,
32
+ 'Coding grant/epoch changed after CAS');
33
+ requireR168(after.epoch_history&&typeof after.epoch_history==='object'&&
34
+ Object.keys(after.epoch_history).length===PLANES.length&&
35
+ canonicalJson(after.epoch_history)===canonicalJson(subject.epoch_history),
36
+ 'signed five-plane epoch history changed after CAS');
37
+ }
38
+
39
+ function expectedBody({subject,prepared,after,now}){
40
+ verifiedAfter(subject,after);
41
+ requireR168(SHA.test(String(prepared?.receipt_sha256)), 'prepared receipt digest invalid');
42
+ return Object.freeze({
43
+ schema:'deadbyte.r168-terminal-result.v1',
44
+ action:'terminalize_unknown_without_replay',
45
+ subject_sha256:subject.subject_sha256,
46
+ prepared_receipt_sha256:prepared.receipt_sha256,
47
+ claim_file_sha256:subject.claim_file_sha256,claim_file_id:subject.claim_file_id,
48
+ old_outcome_sha256:after.old_outcome_sha256,
49
+ predecessor_pointer_sha256:after.pointer_sha256,
50
+ coding_policy_sha256:after.coding_policy_sha256,
51
+ coding_grant_sha256:after.coding_grant_sha256,
52
+ coding_epoch_sha256:after.coding_epoch_sha256,
53
+ epoch_history:structuredClone(after.epoch_history),
54
+ native_cas_result:'REMOVED',claim_absent:true,
55
+ observed_at_utc:now,
56
+ terminalization_committed:true,activation_authorized:false
57
+ });
58
+ }
59
+
60
+ export function createR168TerminalResult({
61
+ subject,approval,prepared,ownerPublicKey,machinePrivateKey,after,now
62
+ }={}){
63
+ requireR168(machinePrivateKey?.asymmetricKeyType==='ed25519'&&machinePrivateKey?.type==='private',
64
+ 'trusted machine signing key invalid');
65
+ const machinePublicKey=createPublicKey(machinePrivateKey);
66
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,
67
+ ownerPublicKey,machinePublicKey,now});
68
+ const body=expectedBody({subject,prepared,after,now});
69
+ const signature=sign(null,message(body),machinePrivateKey);
70
+ const unsigned={body,body_sha256:hash(body),
71
+ attestation:{algorithm:'ed25519',domain:DOMAIN,signature_base64:signature.toString('base64')}};
72
+ return Object.freeze({...unsigned,receipt_sha256:hash(unsigned)});
73
+ }
74
+
75
+ export function verifyR168TerminalResult({
76
+ subject,approval,prepared,terminal,ownerPublicKey,machinePublicKey,after,now
77
+ }={}){
78
+ // Archived evidence remains verifiable after one-time approval expiry, but
79
+ // only if the signed preparation AND terminal observation occurred inside
80
+ // that original approval interval. Never interpret replay as live consent.
81
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,
82
+ ownerPublicKey,machinePublicKey,now:prepared?.body?.created_at_utc});
83
+ const terminalTime=terminal?.body?.observed_at_utc;
84
+ const observed=Date.parse(terminalTime),preparedAt=Date.parse(prepared?.body?.created_at_utc);
85
+ const expires=Date.parse(approval?.body?.expires_at_utc);
86
+ const checkTime=Date.parse(now??new Date().toISOString());
87
+ requireR168(typeof terminalTime==='string'&&
88
+ terminalTime.length===24&&terminalTime.endsWith('Z')&&
89
+ Number.isFinite(observed)&&new Date(observed).toISOString()===terminalTime&&
90
+ Number.isFinite(checkTime)&&observed>=preparedAt&&observed<expires&&observed<=checkTime,
91
+ 'terminal observation time outside approved window or in the future');
92
+ const expected=expectedBody({subject,prepared,after,now:terminalTime});
93
+ requireR168(terminal?.body&&canonicalJson(terminal.body)===canonicalJson(expected),
94
+ 'terminal result body differs from verified post-CAS evidence');
95
+ requireR168(terminal.body_sha256===hash(terminal.body),
96
+ 'terminal result body digest invalid');
97
+ const {receipt_sha256,...unsigned}=terminal;
98
+ requireR168(receipt_sha256===hash(unsigned),'terminal result receipt digest mismatch');
99
+ requireR168(terminal.attestation?.algorithm==='ed25519'&&terminal.attestation?.domain===DOMAIN,
100
+ 'terminal result attestation identity invalid');
101
+ const encoded=terminal.attestation.signature_base64;
102
+ const signature=Buffer.from(String(encoded??''),'base64');
103
+ requireR168(signature.length===64&&signature.toString('base64')===encoded,
104
+ 'terminal result signature encoding invalid');
105
+ const key=machinePublicKey?.type==='public'&&machinePublicKey?.asymmetricKeyType==='ed25519'?
106
+ machinePublicKey:createPublicKey(machinePublicKey);
107
+ requireR168(key.asymmetricKeyType==='ed25519'&&
108
+ verify(null,message(terminal.body),key,signature),
109
+ 'terminal result machine signature invalid');
110
+ return true;
111
+ }
@@ -19,6 +19,7 @@ import { printDoctor } from './doctor-r45.mjs';
19
19
  import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, defaultForegroundTimelineOptions, formatRemoteLogResetNotice, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
20
20
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
21
21
  import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
22
+ import {selectR168OneShotRemotePath} from './remote-one-shot-coherence-r168.mjs';
22
23
  import { windowsPowerShellEnvironment } from './runtime-update.mjs';
23
24
  import { resolveStateRoot } from './state-root.mjs';
24
25
  import {foregroundIdentityMatches,queryForegroundIdentity,validForegroundIdentity,proveRestrictedPidReuse} from './foreground-process-identity.mjs';
@@ -787,6 +788,16 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
787
788
  const display=prepareRemoteConsole();
788
789
  const displayMode=display.mode;
789
790
  const ctx=await remoteContext();
791
+ // Never attempt automatic activation or cleanup from an npm-cache build on
792
+ // an EPOCH_NATIVE production runtime. This guard precedes session recovery,
793
+ // marker claims, revocations and any controller startup.
794
+ const active=await readActiveRuntimeBinding(ctx.root);
795
+ if(active){
796
+ const decision=selectR168OneShotRemotePath({command:'start',active,cliVersion:DEADBYTE_VERSION});
797
+ if(decision.mode==='blocked_version_skew') throw new Error(decision.message);
798
+ }
799
+ // First installation without an active pointer retains the existing
800
+ // unbound bootstrap path. A bound older release must never auto-upgrade.
790
801
  if(displayMode==='ascii') writeRemote(process.stdout,`[DISPLAY] ASCII fallback: ${display.reason}\n`,displayMode);
791
802
  if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor,{displayMode});
792
803
  const startupLock=await acquireForegroundSessionLock({root:ctx.root,powershell:ctx.powershell});
@@ -946,6 +957,15 @@ async function runRemote(args){
946
957
  if(positional.length>1) throw new Error('remote accepts one controller command');
947
958
  const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
948
959
  const ctx=await remoteContext();
960
+ const active=await readActiveRuntimeBinding(ctx.root);
961
+ const decision=selectR168OneShotRemotePath({command,active,cliVersion:DEADBYTE_VERSION});
962
+ if(decision.mode==='blocked_version_skew') throw new Error(decision.message);
963
+ if(decision.mode==='observe_active'||decision.mode==='observe_unbound'){
964
+ if(decision.mode==='observe_active')await reportVersionCoherence(ctx.root,{displayMode:'ascii'});
965
+ const observed=invokeController(ctx,command,{allowFailure:true,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
966
+ process.exitCode=observed.status;
967
+ return;
968
+ }
949
969
  await synchronizeOneShotRuntime(ctx,{displayMode:'ascii'});
950
970
  const result=invokeController(ctx,command,{allowFailure:true});
951
971
  process.exitCode=result.status;
@@ -0,0 +1,25 @@
1
+ // R168: derive resume metadata only from a hash-pinned immutable npm artifact.
2
+ import {createHash} from 'node:crypto';
3
+
4
+ const HEX64=/^[0-9a-f]{64}$/;
5
+ const digest=(alg,bytes,encoding='hex')=>createHash(alg).update(bytes).digest(encoding);
6
+
7
+ export function resumePackRecord({filename,localBytes,claim,version,manifestSha256}={}){
8
+ if(typeof version!=='string'||!/^\d+\.\d+\.\d+$/.test(version)||
9
+ filename!=='deadbyte-mcp-'+version+'.tgz')
10
+ throw new Error('npm resume version or filename invalid');
11
+ if(!Buffer.isBuffer(localBytes)||localBytes.length<1)
12
+ throw new Error('npm resume preserved tarball bytes unavailable');
13
+ if(!HEX64.test(String(manifestSha256))||
14
+ !claim||typeof claim!=='object'||Array.isArray(claim)||
15
+ claim.schema!=='deadbyte.npm-publication-claim.r65.v1'||claim.version!==version||
16
+ claim.manifest_sha256!==manifestSha256||
17
+ !HEX64.test(String(claim.tarball_sha256)))
18
+ throw new Error('npm resume immutable claim manifest or version mismatch');
19
+ const sha256=digest('sha256',localBytes);
20
+ if(sha256!==claim.tarball_sha256)
21
+ throw new Error('npm resume preserved tarball differs from immutable claim digest');
22
+ return Object.freeze({filename,sha256,
23
+ integrity:'sha512-'+digest('sha512',localBytes,'base64'),
24
+ shasum:digest('sha1',localBytes)});
25
+ }
@@ -0,0 +1,36 @@
1
+ // R168 one-shot command dispatch. Pure policy. Does not spawn or modify state.
2
+ import {compareRuntimeVersions} from './runtime-coherence.mjs';
3
+
4
+ const COMMANDS=new Set([
5
+ 'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
6
+ 'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
7
+ 'armdesktop','disarmdesktop','desktopstatus',
8
+ 'armcoding','disarmcoding','codingstatus','codingtest',
9
+ 'armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
10
+ ]);
11
+ const OBSERVE=new Set(['status','processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus']);
12
+ const safe=(mode,active,cli,message=null)=>Object.freeze({
13
+ mode,active_version:active,cli_version:cli,message,
14
+ may_activate:false,may_mutate_authority:false
15
+ });
16
+
17
+ export function selectR168OneShotRemotePath({command,active,cliVersion}={}){
18
+ if(typeof command!=='string'||!COMMANDS.has(command))
19
+ throw new Error('R168 unsupported remote controller command');
20
+ if(active===null||active===undefined){
21
+ if(OBSERVE.has(command))return safe('observe_unbound',null,cliVersion,
22
+ 'R168 active runtime binding absent: read-only status only, never activate.');
23
+ throw new Error('R168 active runtime binding unavailable for authority mutation');
24
+ }
25
+ if(typeof active!=='object'||typeof active.version!=='string'||
26
+ !/^\d+\.\d+\.\d+$/.test(active.version))
27
+ throw new Error('R168 active runtime binding unavailable or invalid');
28
+ const relation=compareRuntimeVersions(cliVersion,active.version);
29
+ if(relation===0) return safe('verify_coherent',active.version,cliVersion);
30
+ const explanation=
31
+ 'R168 version skew: installed runtime '+active.version+', npm CLI '+cliVersion+
32
+ '. Automatic activation from npm cache is forbidden. Existing runtime remains unchanged.';
33
+ if(OBSERVE.has(command))return safe('observe_active',active.version,cliVersion,explanation);
34
+ return safe('blocked_version_skew',active.version,cliVersion,
35
+ explanation+' Run the corresponding command through the installed active-release launcher only if specifically authorized.');
36
+ }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.65';
1
+ export const DEADBYTE_VERSION = '0.15.66';
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';
@@ -7,18 +7,18 @@ import {DEADBYTE_VERSION} from '../src/version.mjs';
7
7
 
8
8
  const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
9
9
  const read=async file=>JSON.parse(await readFile(path.join(root,file),'utf8'));
10
- test('current successor retains the R130 security baseline with truthful R167 identity',async()=>{
10
+ test('current successor retains the R130 security baseline with truthful R168 identity',async()=>{
11
11
  const [pkg,release,provenance,bom,lock,matrix]=await Promise.all([
12
12
  read('package.json'),read('RELEASE.json'),read('PROVENANCE.json'),
13
13
  read('SBOM.cdx.json'),read('package-lock.json'),read('TEST-MATRIX.json')]);
14
14
  assert.equal(pkg.name,'deadbyte-mcp');
15
15
  for(const actual of [pkg.version,release.version,provenance.version,matrix.version,bom.metadata.component.version,lock.version,lock.packages[''].version,DEADBYTE_VERSION])
16
- assert.equal(actual,'0.15.65');
17
- assert.equal(release.train,'R167');
18
- assert.equal(matrix.train,'R167');
19
- assert.equal(release.source_predecessor.version,'0.15.64');
20
- assert.equal(release.source_predecessor.train,'R166');
21
- assert.match(release.source_predecessor.source_path,/0\.15\.64-r166-final/i);
16
+ assert.equal(actual,'0.15.66');
17
+ assert.equal(release.train,'R168');
18
+ assert.equal(matrix.train,'R168');
19
+ assert.equal(release.source_predecessor.version,'0.15.65');
20
+ assert.equal(release.source_predecessor.train,'R167');
21
+ assert.match(release.source_predecessor.source_path,/0\.15\.65-r167-activation-path/i);
22
22
  assert.equal(release.source_predecessor.manifest_status,'VERIFIED_FINAL_BYTES');
23
23
  assert.equal(release.runtime_predecessor.version,'0.15.61');
24
24
  assert.equal(provenance.r160.source_predecessor_manifest_status,'STALE_REJECTED');
@@ -8,22 +8,22 @@ import {DEADBYTE_VERSION} from '../src/version.mjs';
8
8
  const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
9
9
  const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
10
10
 
11
- test('R167 package identity preserves R132 evidence and published R166 source predecessor',async()=>{
11
+ test('R168 package identity preserves R132 evidence and published R167 source predecessor',async()=>{
12
12
  const [pkg,lock,release,prov,bom,matrix]=await Promise.all([
13
13
  read('package.json'),read('package-lock.json'),read('RELEASE.json'),
14
14
  read('PROVENANCE.json'),read('SBOM.cdx.json'),read('TEST-MATRIX.json')]);
15
15
  for(const value of [pkg.version,lock.version,lock.packages[''].version,release.version,prov.version,bom.metadata.component.version,matrix.version,DEADBYTE_VERSION]){
16
16
  assert.equal(value,pkg.version);
17
17
  }
18
- assert.equal(release.train,'R167');
19
- assert.equal(matrix.train,'R167');
20
- assert.equal(release.source_predecessor.version,'0.15.64');
21
- assert.equal(release.source_predecessor.train,'R166');
22
- assert.match(release.source_predecessor.source_path,/0\.15\.64-r166-final/i);
18
+ assert.equal(release.train,'R168');
19
+ assert.equal(matrix.train,'R168');
20
+ assert.equal(release.source_predecessor.version,'0.15.65');
21
+ assert.equal(release.source_predecessor.train,'R167');
22
+ assert.match(release.source_predecessor.source_path,/0\.15\.65-r167-activation-path/i);
23
23
  assert.equal(prov.r160.source_predecessor_manifest_status,'STALE_REJECTED');
24
24
  assert.equal(release.r165.published_source_manifest_sha256,'4adf7d94682fbb71c4a91eb3147ecc48076bbc5d79cb67fcb875cde6a1bb4805');
25
25
  assert.equal(release.r165.published_source_tarball_sha256,'812e97609b7c51353ed25f2202129737e43bb4fbe0778c3adec43ec065352641');
26
- assert.equal(release.source_predecessor.tarball_sha256,'3d22412c3cebabd66bfa92ca4dbaaf3ef49acba7f92436a16485d7e4cb18c653');
26
+ assert.equal(release.source_predecessor.tarball_sha256,'fc3ae5b1505e316bc9dfedf8d063cb00641754c8f5f1306a31f8cc4bb84c9484');
27
27
  assert.equal(prov.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
28
28
  assert.equal(release.runtime_predecessor.version,'0.15.61');
29
29
  assert.equal(prov.runtime_predecessor.release_id,release.runtime_predecessor.release_id);
@@ -26,7 +26,7 @@ test('R153 historical verifier refuses current successor package identity',async
26
26
  const state={pkg:JSON.parse(await readFile(path.join(root,'package.json'),'utf8')),
27
27
  release:JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8')),
28
28
  bridge:JSON.parse(await readFile(path.join(root,'bridge/package.json'),'utf8'))};
29
- assert.equal(state.pkg.version,'0.15.65');
29
+ assert.equal(state.pkg.version,'0.15.66');
30
30
  assert.throws(()=>verifyR153Lineage(state),/package version/);
31
31
  await assert.rejects(verifyR153IdentityTree(root),/package version/);
32
32
  });
@@ -8,7 +8,7 @@ import {fileURLToPath} from 'node:url';
8
8
  import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
9
9
 
10
10
  const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
11
- const version='0.15.65';
11
+ const version='0.15.66';
12
12
  const parent=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.61-055d1ff0aa99e60b');
13
13
  const parentDigest='055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf';
14
14
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
@@ -21,7 +21,7 @@ async function generation(state,label){
21
21
  await writeFile(path.join(root,'GENERATION.SHA256'),manifest,{flag:'wx'});
22
22
  return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
23
23
  }
24
- test('R167 isolated immutable slot activates, rolls back, and restores 0.15.65 without authority or production mutation',async()=>{
24
+ test('R168 isolated immutable slot activates, rolls back, and restores 0.15.66 without authority or production mutation',async()=>{
25
25
  const stateBase=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r163-isolated-'));
26
26
  const state=path.join(stateBase,'state');
27
27
  try{
@@ -17,7 +17,7 @@ const hashMarker=c=>c.repeat(64);
17
17
  async function fixture(t,kind){
18
18
  assert.equal(typeof envRoot,'string'); assert.ok(path.isAbsolute(envRoot));
19
19
  assert.match(sha(await readFile(path.join(envRoot,'MANIFEST.SHA256'))),/^[0-9a-f]{64}$/);
20
- assert.equal(JSON.parse(await readFile(path.join(envRoot,'package.json'),'utf8')).version,'0.15.65');
20
+ assert.equal(JSON.parse(await readFile(path.join(envRoot,'package.json'),'utf8')).version,'0.15.66');
21
21
  const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r164-skew-e2e-'));
22
22
  t.after(async()=>{await rm(dir,{recursive:true,force:true,maxRetries:10,retryDelay:150});});
23
23
  const state=path.join(dir,'state'),bin=path.join(state,'bin');
@@ -65,26 +65,26 @@ async function run(fx){
65
65
  assert.doesNotMatch(trace,/\b(?:start|restart|armprocess|armcoding|armautonomous|deploy)\b/i,'unexpected runtime operation');
66
66
  return {output:(result.stderr||'')+'\n'+(result.stdout||''),trace};
67
67
  }
68
- gate('R167 executable CLI passes completed R159 history, reaches 0.15.61 -> 0.15.65 synchronization safely',async t=>{
68
+ gate('R168 executable CLI blocks npm-cache activation on version skew before touching R159 history',async t=>{
69
69
  const fx=await fixture(t,'completed');
70
70
  const r=await run(fx);
71
- assert.match(r.output,/runtime coherence 'disarmhost' failed exit=93/i,r.output);
71
+ assert.match(r.output,/R168 version skew.*0\.15\.61.*0\.15\.66/i,r.output);
72
72
  assert.doesNotMatch(r.output,/R159 ambiguous multiple completed/i);
73
- assert.match(r.trace,/disarmhost/);
73
+ assert.equal(r.trace,'');
74
74
  assert.doesNotMatch(r.trace,/disarmdesktop/);
75
75
  });
76
76
  gate('R165 executable CLI rejects pending current reconciliation before upgrade',async t=>{
77
77
  const fx=await fixture(t,'pending'),r=await run(fx);
78
- assert.match(r.output,/pending marker reconciliation requires an offline runtime/i,r.output);
78
+ assert.match(r.output,/R168 version skew/i,r.output);
79
79
  assert.doesNotMatch(r.trace,/disarmhost/);
80
80
  });
81
81
  gate('R165 executable CLI rejects tampered terminal before upgrade',async t=>{
82
82
  const fx=await fixture(t,'tampered'),r=await run(fx);
83
- assert.match(r.output,/terminal intent\/subject mismatch|terminal.*mismatch/i,r.output);
83
+ assert.match(r.output,/R168 version skew/i,r.output);
84
84
  assert.doesNotMatch(r.trace,/disarmhost/);
85
85
  });
86
86
  gate('R165 executable CLI keeps ambiguous history fail-closed when predecessor is not ONLINE',async t=>{
87
87
  const fx=await fixture(t,'offline'),r=await run(fx);
88
- assert.match(r.output,/R159 ambiguous multiple completed reconciliation receipts/i,r.output);
88
+ assert.match(r.output,/R168 version skew/i,r.output);
89
89
  assert.doesNotMatch(r.trace,/disarmhost/);
90
90
  });
@@ -8,11 +8,11 @@ import {DEADBYTE_VERSION} from '../src/version.mjs';
8
8
  const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
9
9
  const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
10
10
  const readOptional=async rel=>{try{return await read(rel);}catch(error){if(error?.code==='ENOENT')return null;throw error;}};
11
- const VERSION='0.15.65';
11
+ const VERSION='0.15.66';
12
12
  const R166_MANIFEST='ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849';
13
13
  const R166_TARBALL='3d22412c3cebabd66bfa92ca4dbaaf3ef49acba7f92436a16485d7e4cb18c653';
14
14
 
15
- test('R167 package identity binds published R166 source separately from active R153 runtime',async()=>{
15
+ test('R168 package identity binds published R167 source separately from active R153 runtime',async()=>{
16
16
  const [pkg,lock,bridge,release,provenance,matrix,bom]=await Promise.all([
17
17
  read('package.json'),readOptional('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
18
18
  read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
@@ -20,20 +20,20 @@ test('R167 package identity binds published R166 source separately from active R
20
20
  provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION];
21
21
  if(lock)versions.push(lock.version,lock.packages[''].version);
22
22
  for(const version of versions)assert.equal(version,VERSION);
23
- assert.equal(release.train,'R167');
24
- assert.equal(matrix.train,'R167');
25
- assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:r167:0.15.65');
23
+ assert.equal(release.train,'R168');
24
+ assert.equal(matrix.train,'R168');
25
+ assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:r168:0.15.66');
26
26
  assert.deepEqual(release.runtime_predecessor,{
27
27
  version:'0.15.61',release_id:'v0.15.61-055d1ff0aa99e60b',
28
28
  manifest_sha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
29
29
  generation_manifest_sha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'
30
30
  });
31
- assert.equal(release.source_predecessor.version,'0.15.64');
32
- assert.equal(release.source_predecessor.train,'R166');
33
- assert.equal(release.source_predecessor.tarball_sha256,R166_TARBALL);
31
+ assert.equal(release.source_predecessor.version,'0.15.65');
32
+ assert.equal(release.source_predecessor.train,'R167');
33
+ assert.equal(release.source_predecessor.tarball_sha256,'fc3ae5b1505e316bc9dfedf8d063cb00641754c8f5f1306a31f8cc4bb84c9484');
34
34
  assert.equal(release.r167.source_predecessor_manifest_sha256,R166_MANIFEST);
35
- assert.equal(provenance.source_predecessor_manifest_file_sha256,R166_MANIFEST);
36
- assert.equal(provenance.source_predecessor_tarball_sha256,R166_TARBALL);
35
+ assert.equal(provenance.source_predecessor_manifest_file_sha256,'3316654646cc870584a7c969c2032af6c795229ff8c65e5a8b794f97c2458c41');
36
+ assert.equal(provenance.source_predecessor_tarball_sha256,'fc3ae5b1505e316bc9dfedf8d063cb00641754c8f5f1306a31f8cc4bb84c9484');
37
37
  assert.equal(release.r167.public_tool_delta,0);
38
38
  assert.equal(release.r167.authority_scope_delta,0);
39
39
  assert.equal(release.r167.production_activation,'NOT_RUN');