deadbyte-mcp 0.15.64 → 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.
- package/MANIFEST.SHA256 +64 -22
- package/PROVENANCE.json +32 -7
- package/README.txt +31 -18
- package/RELEASE.json +40 -7
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +48 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R168-ACTIVATION-RECONCILIATION-BLOCKER-20261026.md +39 -0
- package/docs/R168-CRASH-JOURNAL-PROTECTED-PREFLIGHT-20261026.md +46 -0
- package/docs/R168-FIVE-PLANE-FENCE-20261026.md +41 -0
- package/docs/R168-NATIVE-EXCLUSION-TERMINAL-CONTRACT-20261026.md +53 -0
- package/docs/R168-NPX-REMOTE-SKEW-HOTFIX-20261026.md +41 -0
- package/docs/R168-READONLY-AUDIT-AND-RESUME-20261026.md +56 -0
- package/docs/R168-SIGNED-RECONCILIATION-PRECOMMIT-20261026.md +45 -0
- package/package.json +6 -3
- package/scripts/deploy-r48.mjs +37 -4
- package/scripts/gate-windows-r167.ps1 +75 -0
- package/scripts/gate-windows-r168.ps1 +75 -0
- package/scripts/npm-release-r48.mjs +33 -3
- package/scripts/npm-release-readiness-r167.mjs +70 -0
- package/scripts/npm-release-readiness-r168.mjs +70 -0
- package/scripts/r168-audit-readonly.mjs +32 -0
- package/scripts/r168-npm-registry-readonly.mjs +19 -0
- package/scripts/r168-protected-preflight.mjs +65 -0
- package/scripts/r168-release-scan.mjs +28 -0
- package/scripts/r168-source-full-test-runner.mjs +46 -0
- package/scripts/r168-subject-readonly.mjs +49 -0
- package/src/activation-path-r167.mjs +55 -0
- package/src/activation-reconciliation-audit-r168.mjs +200 -0
- package/src/activation-reconciliation-consent-r168.mjs +235 -0
- package/src/activation-reconciliation-fence-r168.mjs +87 -0
- package/src/activation-reconciliation-journal-r168.mjs +100 -0
- package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
- package/src/activation-reconciliation-terminal-r168.mjs +111 -0
- package/src/deadbyte-cli.mjs +20 -0
- package/src/npm-publication-resume-r168.mjs +25 -0
- package/src/remote-one-shot-coherence-r168.mjs +36 -0
- package/src/version.mjs +1 -1
- package/test/process-policy-refresh.test.mjs +8 -5
- package/test/r130-release-security-contract.test.mjs +7 -7
- package/test/r132-release-identity.test.mjs +7 -7
- package/test/r153-prepublish-identity.test.mjs +2 -2
- package/test/r160-release-contract.test.mjs +4 -12
- package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
- package/test/r165-live-cli-skew.test.mjs +7 -7
- package/test/r167-activation-path.test.mjs +124 -0
- package/test/r167-release-contract.test.mjs +51 -0
- package/test/r167-release-readiness.test.mjs +27 -0
- package/test/r168-combined-targeted-runner.mjs +14 -0
- package/test/r168-crash-journal.test.mjs +149 -0
- package/test/r168-five-plane-fence-runner.mjs +11 -0
- package/test/r168-five-plane-fence.test.mjs +128 -0
- package/test/r168-focused-suite.test.mjs +4 -0
- package/test/r168-native-five-plane-runner.mjs +11 -0
- package/test/r168-native-five-plane-scratch.test.mjs +49 -0
- package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
- package/test/r168-npm-resume-identity.test.mjs +49 -0
- package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
- package/test/r168-protected-entrypoint.test.mjs +53 -0
- package/test/r168-reconciliation-audit.test.mjs +42 -0
- package/test/r168-release-contract.test.mjs +56 -0
- package/test/r168-remote-one-shot-coherence.test.mjs +65 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r63-one-shot-coherence.test.mjs +10 -5
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -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
|
+
}
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -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
|
@@ -65,14 +65,17 @@ async function fixture() {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
test('
|
|
68
|
+
test('R167 process policy binds coherently to the explicit development root', async () => {
|
|
69
69
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
70
|
+
const expectedRoot = path.resolve(process.env.DEADBYTE_EXPECTED_DEVELOPMENT_ROOT || repoRoot);
|
|
70
71
|
const policyPath = path.join(repoRoot, 'controller', 'deadbyte-process-policy.json');
|
|
71
72
|
const policy = JSON.parse(await readFile(policyPath, 'utf8'));
|
|
72
|
-
assert.equal(policy.
|
|
73
|
-
assert.
|
|
74
|
-
assert.equal(policy.
|
|
75
|
-
assert.equal(policy.
|
|
73
|
+
assert.equal(path.resolve(policy.roots.core.path), expectedRoot);
|
|
74
|
+
assert.notEqual(expectedRoot, path.parse(expectedRoot).root, 'development root must not be a broad filesystem root');
|
|
75
|
+
assert.equal(policy.grant_file, path.join(expectedRoot, 'controller', 'runtime', 'process-grant.json'));
|
|
76
|
+
assert.equal(policy.state_dir, path.join(expectedRoot, 'controller', 'runtime', 'process-state'));
|
|
77
|
+
assert.equal(policy.native_host.path, path.join(expectedRoot, 'bin', 'deadbyte-process-host.exe'));
|
|
78
|
+
assert.equal(policy.native_host.sha256, sha256(await readFile(policy.native_host.path)));
|
|
76
79
|
assert.ok(policy.profiles.ping_hold);
|
|
77
80
|
assert.ok(policy.profiles.powershell);
|
|
78
81
|
assert.ok(policy.profiles.powershell_repl);
|
|
@@ -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('
|
|
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.
|
|
17
|
-
assert.equal(release.train,'
|
|
18
|
-
assert.equal(matrix.train,'
|
|
19
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
20
|
-
assert.equal(release.source_predecessor.train,'
|
|
21
|
-
assert.match(release.source_predecessor.source_path,/0\.15\.
|
|
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('
|
|
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,'
|
|
19
|
-
assert.equal(matrix.train,'
|
|
20
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
21
|
-
assert.equal(release.source_predecessor.train,'
|
|
22
|
-
assert.match(release.source_predecessor.source_path,/0\.15\.
|
|
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,'
|
|
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);
|
|
@@ -22,11 +22,11 @@ const historical=()=>{
|
|
|
22
22
|
runtime_predecessor:runtime,parent:{...runtime}}};
|
|
23
23
|
};
|
|
24
24
|
const copy=()=>historical();
|
|
25
|
-
test('R153 historical verifier refuses current
|
|
25
|
+
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.
|
|
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
|
});
|
|
@@ -3,25 +3,17 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import {readFile} from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import {fileURLToPath} from 'node:url';
|
|
6
|
-
import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
7
|
-
|
|
8
6
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
7
|
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
8
|
|
|
11
|
-
test('R160
|
|
12
|
-
const [
|
|
13
|
-
read('package.json'),read('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
|
|
14
|
-
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
15
|
-
for(const version of [pkg.version,lock.version,lock.packages[''].version,bridge.version,release.version,
|
|
16
|
-
provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION]) assert.equal(version,'0.15.64');
|
|
17
|
-
assert.equal(release.train,'R166');
|
|
18
|
-
assert.equal(matrix.train,'R166');
|
|
9
|
+
test('R160 historical contract remains distinct from the current package identity',async()=>{
|
|
10
|
+
const [release,provenance]=await Promise.all([read('RELEASE.json'),read('PROVENANCE.json')]);
|
|
19
11
|
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
20
12
|
assert.equal(release.runtime_predecessor.release_id,'v0.15.61-055d1ff0aa99e60b');
|
|
21
13
|
assert.equal(release.runtime_predecessor.manifest_sha256,'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf');
|
|
22
14
|
assert.equal(release.runtime_predecessor.generation_manifest_sha256,'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4');
|
|
23
|
-
assert.equal(release.
|
|
24
|
-
assert.
|
|
15
|
+
assert.equal(release.r160.source_predecessor_train,'R159');
|
|
16
|
+
assert.equal(release.r160.runtime_predecessor_version,'0.15.61');
|
|
25
17
|
assert.equal(release.r160.public_tool_delta,0);
|
|
26
18
|
assert.equal(release.r160.authority_scope_delta,0);
|
|
27
19
|
assert.equal(release.r160.production_activation,'NOT_RUN');
|
|
@@ -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.
|
|
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('
|
|
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.
|
|
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('
|
|
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,/
|
|
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.
|
|
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,/
|
|
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,/
|
|
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,/
|
|
88
|
+
assert.match(r.output,/R168 version skew/i,r.output);
|
|
89
89
|
assert.doesNotMatch(r.trace,/disarmhost/);
|
|
90
90
|
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,mkdir,readFile,readdir,rm,writeFile} from 'node:fs/promises';
|
|
5
|
+
import {spawnSync} from 'node:child_process';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import {fileURLToPath} from 'node:url';
|
|
9
|
+
import {
|
|
10
|
+
parseActivationPathFlags,
|
|
11
|
+
assertTrustedActivationEntrypoint,
|
|
12
|
+
appendActivationPathArgs
|
|
13
|
+
} from '../src/activation-path-r167.mjs';
|
|
14
|
+
|
|
15
|
+
const protectedRoot='C:\\Program Files\\DEADBYTE-R167';
|
|
16
|
+
const developmentRoot='D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.65-r167-activation-path';
|
|
17
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
18
|
+
const H=value=>createHash('sha256').update(value).digest('hex');
|
|
19
|
+
|
|
20
|
+
test('R167 parses one exact protected epoch root and one separate development root',()=>{
|
|
21
|
+
const parsed=parseActivationPathFlags([
|
|
22
|
+
'--preflight','--authority-epoch-root',protectedRoot,'--development-root',developmentRoot
|
|
23
|
+
]);
|
|
24
|
+
assert.equal(parsed.authorityEpochRoot,protectedRoot);
|
|
25
|
+
assert.equal(parsed.developmentRoot,developmentRoot);
|
|
26
|
+
assert.deepEqual(parsed.remainingArgs,['--preflight']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('R167 rejects missing duplicate relative UNC drive-relative and noncanonical roots',()=>{
|
|
30
|
+
const invalid=[
|
|
31
|
+
['--authority-epoch-root'],
|
|
32
|
+
['--development-root'],
|
|
33
|
+
['--authority-epoch-root','relative'],
|
|
34
|
+
['--authority-epoch-root','\\\\server\\share'],
|
|
35
|
+
['--authority-epoch-root','C:relative'],
|
|
36
|
+
['--authority-epoch-root','C:\\Program Files\\..\\Writable'],
|
|
37
|
+
['--authority-epoch-root',protectedRoot,'--authority-epoch-root',protectedRoot],
|
|
38
|
+
['--development-root',developmentRoot,'--development-root',developmentRoot]
|
|
39
|
+
];
|
|
40
|
+
for(const argv of invalid)assert.throws(()=>parseActivationPathFlags(argv));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('R167 EPOCH_NATIVE activation requires the executing package root to be the trusted protected root',()=>{
|
|
44
|
+
assert.throws(()=>assertTrustedActivationEntrypoint({
|
|
45
|
+
continuity:'EPOCH_NATIVE',packageRoot:developmentRoot,
|
|
46
|
+
authorityEpochRoot:protectedRoot,developmentRoot,trusted:true
|
|
47
|
+
}),/protected entrypoint/i);
|
|
48
|
+
assert.throws(()=>assertTrustedActivationEntrypoint({
|
|
49
|
+
continuity:'EPOCH_NATIVE',packageRoot:protectedRoot,
|
|
50
|
+
authorityEpochRoot:protectedRoot,developmentRoot,trusted:false
|
|
51
|
+
}),/ACL-trusted/i);
|
|
52
|
+
assert.deepEqual(assertTrustedActivationEntrypoint({
|
|
53
|
+
continuity:'EPOCH_NATIVE',packageRoot:protectedRoot,
|
|
54
|
+
authorityEpochRoot:protectedRoot,developmentRoot,trusted:true
|
|
55
|
+
}),{authorityEpochRoot:protectedRoot,developmentRoot});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('R167 forwards exact protected stage epoch and development roots to updater without fallback',()=>{
|
|
59
|
+
const args=appendActivationPathArgs(['--expected-version','0.15.65'],{
|
|
60
|
+
stageRoot:protectedRoot,authorityEpochRoot:protectedRoot,developmentRoot
|
|
61
|
+
});
|
|
62
|
+
assert.deepEqual(args,[
|
|
63
|
+
'--expected-version','0.15.65','--stage',protectedRoot,
|
|
64
|
+
'--development-root',developmentRoot,'--authority-epoch-root',protectedRoot
|
|
65
|
+
]);
|
|
66
|
+
assert.throws(()=>appendActivationPathArgs([],{
|
|
67
|
+
stageRoot:developmentRoot,authorityEpochRoot:protectedRoot,developmentRoot
|
|
68
|
+
}),/stage.*protected/i);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('R167 writable EPOCH_NATIVE entrypoint rejects before creating an activation claim',
|
|
72
|
+
{skip:process.platform!=='win32'},async()=>{
|
|
73
|
+
const isolated=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r167-preclaim-'));
|
|
74
|
+
try{
|
|
75
|
+
const state=path.join(isolated,'state'),runtime=path.join(state,'runtime'),bin=path.join(state,'bin');
|
|
76
|
+
const generation=path.join(state,'generations','fixture'),policies=path.join(state,'policies');
|
|
77
|
+
await Promise.all([mkdir(runtime,{recursive:true}),mkdir(bin,{recursive:true}),mkdir(generation,{recursive:true}),mkdir(policies,{recursive:true})]);
|
|
78
|
+
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
79
|
+
const fields={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
|
|
80
|
+
const policyPaths={},config=[];
|
|
81
|
+
const configFields={process:'ProcessPolicyFile',coding:'CodingPolicyFile',autonomous:'AutonomousPolicyFile',desktop:'DesktopPolicyFile',host:'HostPolicyFile'};
|
|
82
|
+
for(const [plane,field] of Object.entries(fields)){
|
|
83
|
+
const grant=path.join(runtime,`${plane}-grant.json`),policy=path.join(policies,`${plane}.json`);
|
|
84
|
+
const body={schema:plane==='host'?'deadbyte.host-policy.v2':`deadbyte.${plane}-policy.v1`,enabled:true,[field]:grant};
|
|
85
|
+
await writeFile(policy,JSON.stringify(body));
|
|
86
|
+
policyPaths[plane]=policy;
|
|
87
|
+
config.push(`${configFields[plane]} = '${policy.replaceAll("'","''")}'`);
|
|
88
|
+
if(plane==='coding')await writeFile(grant,JSON.stringify({schema:'fixture',nonce:H('coding-grant')}));
|
|
89
|
+
}
|
|
90
|
+
const pointer={schema:'deadbyte.active-release.v1',...release.parent,
|
|
91
|
+
release_root:path.join(state,'releases',release.parent.release_id),generation_root:generation,
|
|
92
|
+
launcher_sha256:H('launcher'),config_path:path.join(generation,'controller-config.psd1')};
|
|
93
|
+
await writeFile(pointer.config_path,`@{ ${config.join('; ')} }\n`);
|
|
94
|
+
const pointerBytes=Buffer.from(JSON.stringify(pointer));
|
|
95
|
+
await writeFile(path.join(state,'active-release.json'),pointerBytes);
|
|
96
|
+
const disarmed=plane=>({configured:true,armed:false,reason:plane==='autonomous'?'coding-disarmed':'disarmed'});
|
|
97
|
+
const observation={schema:'deadbyte.controller-observation.v1',state:'ONLINE',local_ready:true,
|
|
98
|
+
bridge:{alive:true,reason:'verified',pid:101},core:{alive:true,reason:'verified',pid:102},
|
|
99
|
+
cloud:{alive:true,reason:'verified',pid:103},supervisor:{running:true},
|
|
100
|
+
authority:{process:disarmed('process'),coding:{configured:true,armed:true,reason:'standing-grant',epoch_seq:7,epoch_sha256:H('coding-epoch')},
|
|
101
|
+
autonomous:disarmed('autonomous'),desktop:disarmed('desktop'),host:disarmed('host')}};
|
|
102
|
+
const observationPath=path.join(state,'observation.json');
|
|
103
|
+
await writeFile(observationPath,JSON.stringify(observation));
|
|
104
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)\nif($Command -ne 'status' -or -not $Json){throw 'mutation attempted'}\nGet-Content -Raw -LiteralPath '${observationPath.replaceAll("'","''")}'\n`);
|
|
105
|
+
const before=H(pointerBytes);
|
|
106
|
+
const preflightRun=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight',
|
|
107
|
+
'--authority-epoch-root',protectedRoot,'--development-root',developmentRoot],{
|
|
108
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:30000,windowsHide:true});
|
|
109
|
+
assert.equal(preflightRun.status,0,preflightRun.stderr);
|
|
110
|
+
const preflight=JSON.parse(preflightRun.stdout.trim());
|
|
111
|
+
assert.equal(preflight.authority_continuity,'EPOCH_NATIVE');
|
|
112
|
+
assert.equal(preflight.activation_entrypoint_state,'REJECTED');
|
|
113
|
+
assert.equal(preflight.protected_activation_entrypoint,false);
|
|
114
|
+
assert.match(preflight.activation_entrypoint_reason,/protected|trusted|UNTRUSTED_WRITE_ACE/i);
|
|
115
|
+
const run=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),
|
|
116
|
+
'--authority-epoch-root',protectedRoot,'--development-root',developmentRoot],{
|
|
117
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:30000,windowsHide:true});
|
|
118
|
+
assert.notEqual(run.status,0,run.stdout);
|
|
119
|
+
assert.match(run.stderr,/trusted|protected|UNTRUSTED_WRITE_ACE/i);
|
|
120
|
+
assert.equal(H(await readFile(path.join(state,'active-release.json'))),before);
|
|
121
|
+
const updates=path.join(state,'evidence','updates');
|
|
122
|
+
await assert.rejects(readdir(updates),error=>error?.code==='ENOENT');
|
|
123
|
+
}finally{await rm(isolated,{recursive:true,force:true});}
|
|
124
|
+
});
|