deadbyte-mcp 0.15.66 → 0.15.68

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 (38) hide show
  1. package/MANIFEST.SHA256 +37 -25
  2. package/PROVENANCE.json +33 -9
  3. package/README.txt +20 -20
  4. package/RELEASE.json +40 -8
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +15 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-process-policy.json +4 -4
  9. package/docs/R169-NPX-ACTIVE-CONTROLLER-DELEGATION-20261026.md +57 -0
  10. package/node_modules/which/CHANGELOG.md +166 -0
  11. package/package.json +4 -2
  12. package/scripts/r172-protected-preflight.mjs +42 -0
  13. package/scripts/r173-protected-preflight.mjs +42 -0
  14. package/src/activation-authority-proof-r75.mjs +28 -4
  15. package/src/activation-continuity-abort-r172.mjs +108 -0
  16. package/src/activation-lock-r65.mjs +5 -1
  17. package/src/activation-preflight-r65.mjs +4 -2
  18. package/src/active-controller-delegation-r169.mjs +57 -0
  19. package/src/deadbyte-cli.mjs +18 -0
  20. package/src/remote-one-shot-coherence-r168.mjs +7 -0
  21. package/src/version.mjs +1 -1
  22. package/test/r130-release-security-contract.test.mjs +7 -7
  23. package/test/r132-release-identity.test.mjs +8 -8
  24. package/test/r153-prepublish-identity.test.mjs +1 -1
  25. package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
  26. package/test/r165-live-cli-skew.test.mjs +2 -2
  27. package/test/r167-release-contract.test.mjs +11 -10
  28. package/test/r168-protected-entrypoint.test.mjs +12 -12
  29. package/test/r168-release-contract.test.mjs +5 -15
  30. package/test/r168-remote-one-shot-coherence.test.mjs +11 -10
  31. package/test/r169-delegate-active.test.mjs +36 -0
  32. package/test/r169-delegate-cli-integration.test.mjs +93 -0
  33. package/test/r169-targeted-runner.mjs +16 -0
  34. package/test/r172-activation-recovery.test.mjs +95 -0
  35. package/test/r172-release-contract.test.mjs +35 -0
  36. package/test/r173-release-contract.test.mjs +32 -0
  37. package/test/r59-tool-surface-compat.test.mjs +1 -1
  38. package/test/r75-authority-evidence.test.mjs +32 -0
@@ -0,0 +1,166 @@
1
+ # Changes
2
+
3
+
4
+ ## 2.0.2
5
+
6
+ * Rename bin to `node-which`
7
+
8
+ ## 2.0.1
9
+
10
+ * generate changelog and publish on version bump
11
+ * enforce 100% test coverage
12
+ * Promise interface
13
+
14
+ ## 2.0.0
15
+
16
+ * Parallel tests, modern JavaScript, and drop support for node < 8
17
+
18
+ ## 1.3.1
19
+
20
+ * update deps
21
+ * update travis
22
+
23
+ ## v1.3.0
24
+
25
+ * Add nothrow option to which.sync
26
+ * update tap
27
+
28
+ ## v1.2.14
29
+
30
+ * appveyor: drop node 5 and 0.x
31
+ * travis-ci: add node 6, drop 0.x
32
+
33
+ ## v1.2.13
34
+
35
+ * test: Pass missing option to pass on windows
36
+ * update tap
37
+ * update isexe to 2.0.0
38
+ * neveragain.tech pledge request
39
+
40
+ ## v1.2.12
41
+
42
+ * Removed unused require
43
+
44
+ ## v1.2.11
45
+
46
+ * Prevent changelog script from being included in package
47
+
48
+ ## v1.2.10
49
+
50
+ * Use env.PATH only, not env.Path
51
+
52
+ ## v1.2.9
53
+
54
+ * fix for paths starting with ../
55
+ * Remove unused `is-absolute` module
56
+
57
+ ## v1.2.8
58
+
59
+ * bullet items in changelog that contain (but don't start with) #
60
+
61
+ ## v1.2.7
62
+
63
+ * strip 'update changelog' changelog entries out of changelog
64
+
65
+ ## v1.2.6
66
+
67
+ * make the changelog bulleted
68
+
69
+ ## v1.2.5
70
+
71
+ * make a changelog, and keep it up to date
72
+ * don't include tests in package
73
+ * Properly handle relative-path executables
74
+ * appveyor
75
+ * Attach error code to Not Found error
76
+ * Make tests pass on Windows
77
+
78
+ ## v1.2.4
79
+
80
+ * Fix typo
81
+
82
+ ## v1.2.3
83
+
84
+ * update isexe, fix regression in pathExt handling
85
+
86
+ ## v1.2.2
87
+
88
+ * update deps, use isexe module, test windows
89
+
90
+ ## v1.2.1
91
+
92
+ * Sometimes windows PATH entries are quoted
93
+ * Fixed a bug in the check for group and user mode bits. This bug was introduced during refactoring for supporting strict mode.
94
+ * doc cli
95
+
96
+ ## v1.2.0
97
+
98
+ * Add support for opt.all and -as cli flags
99
+ * test the bin
100
+ * update travis
101
+ * Allow checking for multiple programs in bin/which
102
+ * tap 2
103
+
104
+ ## v1.1.2
105
+
106
+ * travis
107
+ * Refactored and fixed undefined error on Windows
108
+ * Support strict mode
109
+
110
+ ## v1.1.1
111
+
112
+ * test +g exes against secondary groups, if available
113
+ * Use windows exe semantics on cygwin & msys
114
+ * cwd should be first in path on win32, not last
115
+ * Handle lower-case 'env.Path' on Windows
116
+ * Update docs
117
+ * use single-quotes
118
+
119
+ ## v1.1.0
120
+
121
+ * Add tests, depend on is-absolute
122
+
123
+ ## v1.0.9
124
+
125
+ * which.js: root is allowed to execute files owned by anyone
126
+
127
+ ## v1.0.8
128
+
129
+ * don't use graceful-fs
130
+
131
+ ## v1.0.7
132
+
133
+ * add license to package.json
134
+
135
+ ## v1.0.6
136
+
137
+ * isc license
138
+
139
+ ## 1.0.5
140
+
141
+ * Awful typo
142
+
143
+ ## 1.0.4
144
+
145
+ * Test for path absoluteness properly
146
+ * win: Allow '' as a pathext if cmd has a . in it
147
+
148
+ ## 1.0.3
149
+
150
+ * Remove references to execPath
151
+ * Make `which.sync()` work on Windows by honoring the PATHEXT variable.
152
+ * Make `isExe()` always return true on Windows.
153
+ * MIT
154
+
155
+ ## 1.0.2
156
+
157
+ * Only files can be exes
158
+
159
+ ## 1.0.1
160
+
161
+ * Respect the PATHEXT env for win32 support
162
+ * should 0755 the bin
163
+ * binary
164
+ * guts
165
+ * package
166
+ * 1st
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deadbyte-mcp",
3
- "version": "0.15.66",
3
+ "version": "0.15.68",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
@@ -97,7 +97,9 @@
97
97
  "r167:focused:test": "node --test --test-concurrency=1 test/r167-activation-path.test.mjs test/r167-release-contract.test.mjs test/r167-release-readiness.test.mjs test/r160-release-contract.test.mjs test/r158-epoch-bootstrap.test.mjs test/r65-activation-orchestration.test.mjs",
98
98
  "gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r168.ps1 -Stage all",
99
99
  "r168:focused:test": "node test/r168-combined-targeted-runner.mjs",
100
- "r168:release:verify": "node scripts/npm-release-r48.mjs --verify-only"
100
+ "r168:release:verify": "node scripts/npm-release-r48.mjs --verify-only",
101
+ "r172:focused:test": "node --test --test-concurrency=1 test/r172-activation-recovery.test.mjs test/r172-release-contract.test.mjs test/r169-delegate-active.test.mjs test/r169-delegate-cli-integration.test.mjs test/r65-activation-lock-recovery.test.mjs test/r65-activation-orchestration.test.mjs",
102
+ "r173:focused:test": "node --test --test-concurrency=1 test/r75-authority-evidence.test.mjs test/r172-activation-recovery.test.mjs test/r173-release-contract.test.mjs test/r65-activation-preflight.test.mjs test/r65-activation-orchestration.test.mjs"
101
103
  },
102
104
  "bundleDependencies": [
103
105
  "@modelcontextprotocol/server",
@@ -0,0 +1,42 @@
1
+ // R172 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ import {createHash} from 'node:crypto';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
7
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
8
+
9
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
10
+ const version='0.15.67',expectedTrain='R172';
11
+ const mandatory=['scripts/r172-protected-preflight.mjs','src/activation-continuity-abort-r172.mjs',
12
+ 'src/activation-lock-r65.mjs','src/activation-preflight-r65.mjs',
13
+ 'src/activation-reconciliation-journal-r168.mjs','src/activation-reconciliation-terminal-r168.mjs',
14
+ 'src/activation-reconciliation-fence-r168.mjs','src/activation-reconciliation-audit-r168.mjs'];
15
+ function denied(reason){throw new Error('R172_'+reason);}
16
+ function actualProtectedRoot(){
17
+ const trusted='C:\\Program Files',rel=path.win32.relative(trusted,root);
18
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))denied('ROOT_UNPROTECTED');
19
+ return root;
20
+ }
21
+ async function main(){
22
+ if(process.platform!=='win32'||process.arch!=='x64')denied('WINDOWS_X64_REQUIRED');
23
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
24
+ const verifiedRoot=actualProtectedRoot();
25
+ const pkg=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
26
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
27
+ if(pkg.name!=='deadbyte-mcp'||pkg.version!==version||release.name!=='deadbyte-mcp'||
28
+ release.version!==version||release.train!==expectedTrain)denied('VERSION_DRIFT');
29
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
30
+ const entries=parseManifest(manifest.toString('utf8'));
31
+ for(const name of mandatory)if(!entries.has(name))denied('SOURCE_NOT_IN_MANIFEST');
32
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
33
+ const important=[...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
34
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
35
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),path.win32.join(verifiedRoot,'RELEASE.json'),
36
+ path.win32.join(verifiedRoot,'package.json')];
37
+ await inspectTrustedInstallation(important);
38
+ const hash=createHash('sha256').update(manifest).digest('hex');
39
+ process.stdout.write(JSON.stringify({status:'R172_PROTECTED_PREFLIGHT_READONLY_PASS',version,train:expectedTrain,
40
+ manifest_sha256:hash,files:entries.size,terminalization_authorized:false,activation_authorized:false})+'\n');
41
+ }
42
+ try{await main();}catch(error){process.stderr.write('R172_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');process.exitCode=1;}
@@ -0,0 +1,42 @@
1
+ // R173 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ import {createHash} from 'node:crypto';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
7
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
8
+
9
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
10
+ const version='0.15.68',expectedTrain='R173';
11
+ const mandatory=['scripts/r173-protected-preflight.mjs','src/activation-continuity-abort-r172.mjs',
12
+ 'src/activation-lock-r65.mjs','src/activation-preflight-r65.mjs',
13
+ 'src/activation-reconciliation-journal-r168.mjs','src/activation-reconciliation-terminal-r168.mjs',
14
+ 'src/activation-reconciliation-fence-r168.mjs','src/activation-reconciliation-audit-r168.mjs'];
15
+ function denied(reason){throw new Error('R173_'+reason);}
16
+ function actualProtectedRoot(){
17
+ const trusted='C:\\Program Files',rel=path.win32.relative(trusted,root);
18
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))denied('ROOT_UNPROTECTED');
19
+ return root;
20
+ }
21
+ async function main(){
22
+ if(process.platform!=='win32'||process.arch!=='x64')denied('WINDOWS_X64_REQUIRED');
23
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
24
+ const verifiedRoot=actualProtectedRoot();
25
+ const pkg=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
26
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
27
+ if(pkg.name!=='deadbyte-mcp'||pkg.version!==version||release.name!=='deadbyte-mcp'||
28
+ release.version!==version||release.train!==expectedTrain)denied('VERSION_DRIFT');
29
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
30
+ const entries=parseManifest(manifest.toString('utf8'));
31
+ for(const name of mandatory)if(!entries.has(name))denied('SOURCE_NOT_IN_MANIFEST');
32
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
33
+ const important=[...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
34
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
35
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),path.win32.join(verifiedRoot,'RELEASE.json'),
36
+ path.win32.join(verifiedRoot,'package.json')];
37
+ await inspectTrustedInstallation(important);
38
+ const hash=createHash('sha256').update(manifest).digest('hex');
39
+ process.stdout.write(JSON.stringify({status:'R173_PROTECTED_PREFLIGHT_READONLY_PASS',version,train:expectedTrain,
40
+ manifest_sha256:hash,files:entries.size,terminalization_authorized:false,activation_authorized:false})+'\n');
41
+ }
42
+ try{await main();}catch(error){process.stderr.write('R173_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');process.exitCode=1;}
@@ -1,6 +1,7 @@
1
1
  import {createHash} from 'node:crypto';
2
2
  import {lstat,open,realpath} from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
4
5
 
5
6
  const FIELDS=Object.freeze({process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'});
6
7
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
@@ -22,7 +23,9 @@ async function readRegularPinned(file,label){
22
23
  }finally{await handle.close();}
23
24
  }
24
25
 
25
- export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority}){
26
+ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority,
27
+ epochStoreFactory=createAuthorityEpochStore}){
28
+ if(typeof epochStoreFactory!=='function')throw new Error('authority epoch store factory invalid');
26
29
  const runtimeRoot=await realpath(path.join(stateRoot,'runtime'));
27
30
  const result={};
28
31
  for(const [plane,field] of Object.entries(FIELDS)){
@@ -49,11 +52,31 @@ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha2
49
52
  const grantAbsent=grantInfo===undefined;
50
53
  if(grantInfo&&(!grantInfo.isFile()||grantInfo.isSymbolicLink()))throw new Error(`${plane} grant file type invalid`);
51
54
  const validEntry=entry&&entry.configured===true&&typeof entry.armed==='boolean';
52
- const validDisarmed=validEntry&&!entry.armed&&grantAbsent&&
53
- (entry.reason==='disarmed'||(plane==='autonomous'&&entry.reason==='coding-disarmed'));
55
+ const grantBytes=grantInfo?await readRegularPinned(checkedPath,`${plane} grant`):null;
56
+ const grantSha=grantBytes?sha(grantBytes):null;
57
+ let transactionRevoked=false;
58
+ if(validEntry&&!entry.armed&&entry.reason==='epoch-revoked'&&grantBytes){
59
+ let grant;
60
+ try{grant=JSON.parse(grantBytes.toString('utf8').replace(/^\uFEFF/,''));}catch{}
61
+ const nonce=grant?.instance_nonce;
62
+ if(typeof nonce==='string'&&/^[0-9a-f]{64}$/.test(nonce)){
63
+ const store=epochStoreFactory({runtimeDir:parent,policyBytes});
64
+ const [audit,head,verification]=await Promise.all([
65
+ store.auditHistory({plane}),store.readHead({plane}),
66
+ store.verifyGrant({plane,grantBytes,grantInstanceNonce:nonce,policyBytes})
67
+ ]);
68
+ transactionRevoked=audit?.status==='PASS'&&Number.isSafeInteger(audit.verified_events)&&audit.verified_events>0&&
69
+ head?.action==='transaction_revoke'&&Number.isSafeInteger(head.seq)&&head.seq>0&&
70
+ /^[0-9a-f]{64}$/.test(String(head.event_sha256??''))&&
71
+ head.policy_sha256===expectedHash&&head.grant_sha256===grantSha&&head.grant_instance_nonce===nonce&&
72
+ /^r65-[0-9a-f]{32}$/.test(String(head.update_id??''))&&verification?.trusted===false&&
73
+ verification?.reason==='epoch-revoked'&&verification?.event_sha256===head.event_sha256;
74
+ }
75
+ }
76
+ const validDisarmed=validEntry&&!entry.armed&&(
77
+ (grantAbsent&&(entry.reason==='disarmed'||(plane==='autonomous'&&entry.reason==='coding-disarmed')))||transactionRevoked);
54
78
  const validArmed=validEntry&&entry.armed&&!grantAbsent&&entry.reason==='standing-grant'&&
55
79
  Number.isSafeInteger(entry.epoch_seq)&&entry.epoch_seq>0&&/^[0-9a-f]{64}$/.test(entry.epoch_sha256??'');
56
- const grantSha=grantInfo?sha(await readRegularPinned(checkedPath,`${plane} grant`)):null;
57
80
  let grantFinal;
58
81
  try{grantFinal=await lstat(checkedPath);}
59
82
  catch(error){if(error?.code!=='ENOENT')throw error;}
@@ -61,6 +84,7 @@ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha2
61
84
  throw new Error(`${plane} grant identity changed during observation`);
62
85
  result[plane]=Object.freeze({state:validDisarmed?'VERIFIED_DISARMED':validArmed?'VERIFIED_ARMED':'UNVERIFIED',
63
86
  policy_sha256:expectedHash,grant_absent:grantAbsent,
87
+ ...(transactionRevoked?{transaction_revoked:true}:{}),
64
88
  ...(grantSha?{grant_sha256:grantSha}:{})});
65
89
  }
66
90
  return Object.freeze(result);
@@ -0,0 +1,108 @@
1
+ import {createHash} from 'node:crypto';
2
+ import {lstat, readFile, readdir} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
5
+
6
+ const PLANES = Object.freeze(['process', 'coding', 'autonomous', 'desktop', 'host']);
7
+ const GRANT_FIELD = Object.freeze({
8
+ process: 'grant_file', coding: 'lease_file', autonomous: 'lease_file',
9
+ desktop: 'grant_file', host: 'arm_file'
10
+ });
11
+ const HEX64 = /^[0-9a-f]{64}$/u;
12
+ const CONTINUITY = /^r65-[0-9a-f]{32}$/u;
13
+ const sha = bytes => createHash('sha256').update(bytes).digest('hex');
14
+
15
+ function parse(bytes, label) {
16
+ let text = bytes.toString('utf8');
17
+ if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);
18
+ try { return JSON.parse(text); }
19
+ catch { throw new Error(`R172 ${label} JSON invalid`); }
20
+ }
21
+
22
+ function inside(root, target) {
23
+ const relative = path.relative(path.resolve(root), path.resolve(target));
24
+ return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
25
+ }
26
+
27
+ async function optionalBytes(file) {
28
+ try { return await readFile(file); }
29
+ catch (error) { if (error?.code === 'ENOENT') return null; throw error; }
30
+ }
31
+
32
+ export async function verifyR172PredispatchContinuityAbort({outcome, lock, stateRoot} = {}) {
33
+ if (!outcome || !lock || typeof stateRoot !== 'string' || !path.isAbsolute(stateRoot)) return false;
34
+ if (outcome.schema !== 'deadbyte.r65-activation-outcome.v1' ||
35
+ outcome.claim_operation_id !== lock.operation_id ||
36
+ outcome.preflight_subject_sha256 !== lock.subject_sha256 ||
37
+ outcome.outcome?.state !== 'ACTIVATION_OUTCOME_UNKNOWN' ||
38
+ outcome.outcome?.preflight !== 'AUTHORITY_UNVERIFIED' ||
39
+ outcome.update_id !== null || outcome.state_path !== null ||
40
+ !CONTINUITY.test(String(outcome.continuity_update_id ?? '')) ||
41
+ !HEX64.test(String(outcome.candidate_manifest_sha256 ?? '')) ||
42
+ typeof outcome.candidate_version !== 'string' || typeof outcome.predecessor_release_id !== 'string') return false;
43
+
44
+ const created = Date.parse(lock.created_at_utc), observed = Date.parse(outcome.observed_at_utc);
45
+ if (!Number.isFinite(created) || !Number.isFinite(observed) || observed < created || observed - created > 120_000) return false;
46
+ const state = path.resolve(stateRoot), pointerBytes = await readFile(path.join(state, 'active-release.json'));
47
+ const pointer = parse(pointerBytes, 'active pointer');
48
+ if (pointer.release_id !== outcome.predecessor_release_id ||
49
+ !HEX64.test(String(pointer.manifest_sha256 ?? '')) ||
50
+ !HEX64.test(String(pointer.generation_manifest_sha256 ?? ''))) return false;
51
+ const generation = path.resolve(String(pointer.generation_root ?? ''));
52
+ if (!inside(path.join(state, 'generations'), generation)) return false;
53
+
54
+ const updates = path.join(state, 'evidence', 'updates');
55
+ const candidatePrefix = `r27-${outcome.candidate_manifest_sha256.slice(0, 16)}-`;
56
+ const updateEntries = await readdir(updates, {withFileTypes: true});
57
+ if (updateEntries.some(entry => entry.name.startsWith(candidatePrefix))) return false;
58
+
59
+ const runtime = path.join(state, 'runtime');
60
+ const store = createAuthorityEpochStore({runtimeDir: runtime});
61
+ const revoked = new Map();
62
+ for (const plane of PLANES) {
63
+ const audit = await store.auditHistory({plane});
64
+ if (!['PASS', 'EMPTY'].includes(audit.status)) return false;
65
+ const eventsDir = path.join(runtime, 'authority-epochs', plane, 'events');
66
+ let entries;
67
+ try { entries = await readdir(eventsDir, {withFileTypes: true}); }
68
+ catch (error) { if (error?.code === 'ENOENT' && audit.status === 'EMPTY') entries = []; else throw error; }
69
+ if (entries.length !== audit.verified_events || entries.length > 10_000) return false;
70
+ for (const entry of entries) {
71
+ if (!entry.isFile() || entry.isSymbolicLink()) return false;
72
+ const match = /^(\d{16})-([0-9a-f]{64})\.json$/u.exec(entry.name);
73
+ if (!match) return false;
74
+ const bytes = await readFile(path.join(eventsDir, entry.name));
75
+ if (sha(bytes) !== match[2]) return false;
76
+ const event = parse(bytes, `${plane} epoch event`);
77
+ if (event.update_id !== outcome.continuity_update_id) continue;
78
+ const when = Date.parse(event.recorded_at_utc);
79
+ if (event.plane !== plane || event.action !== 'transaction_revoke' ||
80
+ !Number.isFinite(when) || when < created || when > observed || revoked.has(plane)) return false;
81
+ revoked.set(plane, event);
82
+ }
83
+ }
84
+ if (revoked.size < 1) return false;
85
+
86
+ for (const [plane, event] of revoked) {
87
+ const policyFile = path.join(generation, `${plane}-policy.json`);
88
+ const policyBytes = await readFile(policyFile), policy = parse(policyBytes, `${plane} policy`);
89
+ const grantFile = path.resolve(String(policy?.[GRANT_FIELD[plane]] ?? ''));
90
+ if (!inside(runtime, grantFile)) return false;
91
+ const planeStore = createAuthorityEpochStore({runtimeDir: runtime, policyBytes});
92
+ const head = await planeStore.readHead({plane});
93
+ if (!head || head.seq <= event.seq || head.previous_event_sha256 === null) return false;
94
+ if (head.action === 'user_revoke') {
95
+ if (await optionalBytes(grantFile) !== null) return false;
96
+ continue;
97
+ }
98
+ if (!['grant', 'transaction_restore'].includes(head.action)) return false;
99
+ const grantBytes = await optionalBytes(grantFile);
100
+ if (!grantBytes) return false;
101
+ const grant = parse(grantBytes, `${plane} grant`);
102
+ const verified = await planeStore.verifyGrant({
103
+ plane, grantBytes, grantInstanceNonce: grant.instance_nonce, policyBytes
104
+ });
105
+ if (verified.trusted !== true || verified.event_sha256 !== head.event_sha256) return false;
106
+ }
107
+ return true;
108
+ }
@@ -2,6 +2,7 @@ import {createPublicKey,randomBytes} from 'node:crypto';
2
2
  import {mkdir,open,readFile,readdir,unlink} from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import {verifySlotUpdateEnvelope} from './runtime-update.mjs';
5
+ import {verifyR172PredispatchContinuityAbort} from './activation-continuity-abort-r172.mjs';
5
6
 
6
7
  const HEX64=/^[0-9a-f]{64}$/;
7
8
  const OP=/^[0-9a-f]{32}$/;
@@ -130,8 +131,11 @@ async function findTerminalOutcome(updatesDir,lock,stateRoot){
130
131
  catch{continue;}
131
132
  if(value?.claim_operation_id!==lock.operation_id)continue;
132
133
  if(value?.preflight_subject_sha256!==lock.subject_sha256){foreign=true;continue;}
134
+ let continuityAbort=false;
135
+ try{continuityAbort=await verifyR172PredispatchContinuityAbort({outcome:value,lock,stateRoot});}
136
+ catch{continuityAbort=false;}
133
137
  if(isSafePreMutationReject(value,lock)||await isSafeTerminalActivation(value,lock,stateRoot)||
134
- await isSafeSignedPreconditionReject(value,lock,stateRoot))matched=true;
138
+ await isSafeSignedPreconditionReject(value,lock,stateRoot)||continuityAbort)matched=true;
135
139
  else unknown=true;
136
140
  }
137
141
  return {matched,foreign,unknown};
@@ -55,14 +55,16 @@ export function classifyActivationPreflight({pointer,expectedParent,candidate,ex
55
55
  for(const plane of PLANES){
56
56
  const entry=observation?.authority?.[plane],proof=observation?.authority_proof?.[plane];
57
57
  if(entry&&typeof entry==='object'){
58
- const knownReason=entry.armed?entry.reason==='standing-grant':entry.reason==='disarmed'||entry.reason==='coding-disarmed';
58
+ const knownReason=entry.armed?entry.reason==='standing-grant':
59
+ entry.reason==='disarmed'||entry.reason==='coding-disarmed'||entry.reason==='epoch-revoked';
59
60
  if(!knownReason)return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority reason unverified`,...bound});
60
61
  }
61
62
  if(!proof)
62
63
  return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority proof missing`,...bound});
63
64
  if(proof&&(proof.policy_sha256!==observation?.policy_sha256?.[plane]||
64
65
  proof.state!==(authority[plane]?'VERIFIED_ARMED':'VERIFIED_DISARMED')||
65
- (!authority[plane]&&proof.grant_absent!==true)))
66
+ (!authority[plane]&&proof.grant_absent!==true&&
67
+ !(entry?.reason==='epoch-revoked'&&proof.transaction_revoked===true))))
66
68
  return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority proof unverified`,...bound});
67
69
  }
68
70
  if(PLANES.some(plane=>!HEX64.test(String(observation?.policy_sha256?.[plane]))))
@@ -0,0 +1,57 @@
1
+ // R169 pre-dispatch identity fence. Only routes an explicit user-issued
2
+ // authority command to the already installed controller, never upgrades or
3
+ // fabricates an authority grant.
4
+ import {createHash} from 'node:crypto';
5
+ import {lstat,readFile,realpath} from 'node:fs/promises';
6
+ import path from 'node:path';
7
+
8
+ const SHA=/^[0-9a-f]{64}$/;
9
+ const hash=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+ function deny(condition,reason){if(!condition)throw new Error('R169 installed controller '+reason);}
11
+ const canonical=pathName=>path.win32.normalize(path.win32.resolve(pathName)).toLowerCase();
12
+ function descendant(parent,child){
13
+ const r=path.win32.relative(parent,child);
14
+ return r!==''&&r!=='..'&&!r.startsWith('..'+path.win32.sep)&&!path.win32.isAbsolute(r);
15
+ }
16
+ async function fileBytes(file){
17
+ const before=await lstat(file);
18
+ deny(before.isFile()&&!before.isSymbolicLink()&&before.size>0&&before.size<=8*1024*1024,
19
+ 'file not regular or bounded');
20
+ deny(canonical(await realpath(file))===canonical(file),'file reparse not allowed');
21
+ const bytes=await readFile(file);
22
+ deny(bytes.length===before.size,'file length changed');
23
+ return bytes;
24
+ }
25
+ export async function assertR169PinnedActiveDelegation({stateRoot,launcherPath,active}={}){
26
+ deny(process.platform==='win32','Windows only');
27
+ deny(typeof stateRoot==='string'&&path.win32.isAbsolute(stateRoot),'state root invalid');
28
+ deny(active&&typeof active==='object'&&
29
+ SHA.test(String(active.manifest_sha256))&&
30
+ SHA.test(String(active.generation_manifest_sha256))&&
31
+ SHA.test(String(active.launcher_sha256))&&
32
+ typeof active.release_id==='string'&&
33
+ typeof active.release_root==='string'&&
34
+ typeof active.generation_root==='string'&&
35
+ typeof active.config_path==='string','active release binding incomplete');
36
+ const root=path.win32.resolve(stateRoot);
37
+ const expectedLauncher=path.win32.join(root,'bin','DEADBYTE-MCP.ps1');
38
+ deny(canonical(launcherPath)===canonical(expectedLauncher),'launcher path not active stable launcher');
39
+ deny(descendant(path.win32.join(root,'releases'),active.release_root)&&
40
+ descendant(path.win32.join(root,'generations'),active.generation_root),
41
+ 'release/generation escapes state root');
42
+ deny(canonical(active.config_path)===canonical(path.win32.join(active.generation_root,'controller-config.psd1')),
43
+ 'generation controller config not bound');
44
+ const launcher=await fileBytes(launcherPath);
45
+ deny(hash(launcher)===active.launcher_sha256,'launcher digest mismatch');
46
+ const releaseManifest=await fileBytes(path.win32.join(active.release_root,'MANIFEST.SHA256'));
47
+ deny(hash(releaseManifest)===active.manifest_sha256,'release manifest digest mismatch');
48
+ const generationManifest=await fileBytes(path.win32.join(active.generation_root,'GENERATION.SHA256'));
49
+ deny(hash(generationManifest)===active.generation_manifest_sha256,
50
+ 'generation manifest digest mismatch');
51
+ // Installed PowerShell launcher enforces its own identity/generation/
52
+ // controller provenance again before invoking the actual controller.
53
+ return Object.freeze({release_id:active.release_id,version:active.version,
54
+ launcher_sha256:active.launcher_sha256,manifest_sha256:active.manifest_sha256,
55
+ generation_manifest_sha256:active.generation_manifest_sha256,
56
+ activation_attempted:false});
57
+ }
@@ -20,6 +20,7 @@ import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelin
20
20
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
21
21
  import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
22
22
  import {selectR168OneShotRemotePath} from './remote-one-shot-coherence-r168.mjs';
23
+ import {assertR169PinnedActiveDelegation} from './active-controller-delegation-r169.mjs';
23
24
  import { windowsPowerShellEnvironment } from './runtime-update.mjs';
24
25
  import { resolveStateRoot } from './state-root.mjs';
25
26
  import {foregroundIdentityMatches,queryForegroundIdentity,validForegroundIdentity,proveRestrictedPidReuse} from './foreground-process-identity.mjs';
@@ -960,6 +961,23 @@ async function runRemote(args){
960
961
  const active=await readActiveRuntimeBinding(ctx.root);
961
962
  const decision=selectR168OneShotRemotePath({command,active,cliVersion:DEADBYTE_VERSION});
962
963
  if(decision.mode==='blocked_version_skew') throw new Error(decision.message);
964
+ if(decision.mode==='delegate_active'){
965
+ // R169 explicitly delegates ONLY plane arm/disarm verbs to the installed
966
+ // controller. The launcher verifies its own identity and policy; this path
967
+ // never launches deploy-r48.mjs or attempts runtime coherence activation.
968
+ await assertR169PinnedActiveDelegation({stateRoot:ctx.root,launcherPath:ctx.launcher,active});
969
+ const unchanged=await readActiveRuntimeBinding(ctx.root);
970
+ if(unchanged?.release_id!==active.release_id||
971
+ unchanged?.generation_manifest_sha256!==active.generation_manifest_sha256)
972
+ throw new Error('R169 active runtime changed before delegated command; no dispatch');
973
+ const result=invokeController(ctx,command,{allowFailure:true,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
974
+ const final=await readActiveRuntimeBinding(ctx.root);
975
+ if(final?.release_id!==active.release_id||
976
+ final?.generation_manifest_sha256!==active.generation_manifest_sha256)
977
+ throw new Error('R169 active runtime changed during delegated command; outcome unknown');
978
+ process.exitCode=result.status;
979
+ return;
980
+ }
963
981
  if(decision.mode==='observe_active'||decision.mode==='observe_unbound'){
964
982
  if(decision.mode==='observe_active')await reportVersionCoherence(ctx.root,{displayMode:'ascii'});
965
983
  const observed=invokeController(ctx,command,{allowFailure:true,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
@@ -9,6 +9,8 @@ const COMMANDS=new Set([
9
9
  'armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
10
10
  ]);
11
11
  const OBSERVE=new Set(['status','processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus']);
12
+ const DELEGATE=new Set(['armprocess','disarmprocess','armcoding','disarmcoding','armautonomous',
13
+ 'disarmautonomous','armdesktop','disarmdesktop','armhost','disarmhost']);
12
14
  const safe=(mode,active,cli,message=null)=>Object.freeze({
13
15
  mode,active_version:active,cli_version:cli,message,
14
16
  may_activate:false,may_mutate_authority:false
@@ -31,6 +33,11 @@ export function selectR168OneShotRemotePath({command,active,cliVersion}={}){
31
33
  'R168 version skew: installed runtime '+active.version+', npm CLI '+cliVersion+
32
34
  '. Automatic activation from npm cache is forbidden. Existing runtime remains unchanged.';
33
35
  if(OBSERVE.has(command))return safe('observe_active',active.version,cliVersion,explanation);
36
+ if(relation>0&&DELEGATE.has(command))return Object.freeze({
37
+ ...safe('delegate_active',active.version,cliVersion,
38
+ explanation+' Forwarding explicit plane authority request to installed active controller.'),
39
+ may_mutate_authority:true
40
+ });
34
41
  return safe('blocked_version_skew',active.version,cliVersion,
35
42
  explanation+' Run the corresponding command through the installed active-release launcher only if specifically authorized.');
36
43
  }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.66';
1
+ export const DEADBYTE_VERSION = '0.15.68';
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 R168 identity',async()=>{
10
+ test('current successor retains the R130 security baseline with truthful R173 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.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);
16
+ assert.equal(actual,'0.15.68');
17
+ assert.equal(release.train,'R173');
18
+ assert.equal(matrix.train,'R173');
19
+ assert.equal(release.source_predecessor.version,'0.15.67');
20
+ assert.equal(release.source_predecessor.train,'R172');
21
+ assert.match(release.source_predecessor.source_path,/0\.15\.67-r172-final-remote-recovery/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');