deadbyte-mcp 0.15.66 → 0.15.67
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 +33 -23
- package/PROVENANCE.json +23 -9
- package/README.txt +20 -20
- package/RELEASE.json +26 -8
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +15 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R169-NPX-ACTIVE-CONTROLLER-DELEGATION-20261026.md +57 -0
- package/node_modules/which/CHANGELOG.md +166 -0
- package/package.json +3 -2
- package/scripts/r172-protected-preflight.mjs +42 -0
- package/src/activation-continuity-abort-r172.mjs +108 -0
- package/src/activation-lock-r65.mjs +5 -1
- package/src/activation-preflight-r65.mjs +2 -1
- package/src/active-controller-delegation-r169.mjs +57 -0
- package/src/deadbyte-cli.mjs +18 -0
- package/src/remote-one-shot-coherence-r168.mjs +7 -0
- package/src/version.mjs +1 -1
- package/test/r130-release-security-contract.test.mjs +7 -7
- package/test/r132-release-identity.test.mjs +8 -8
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
- package/test/r165-live-cli-skew.test.mjs +2 -2
- package/test/r167-release-contract.test.mjs +11 -10
- package/test/r168-protected-entrypoint.test.mjs +12 -12
- package/test/r168-release-contract.test.mjs +5 -15
- package/test/r168-remote-one-shot-coherence.test.mjs +11 -10
- package/test/r169-delegate-active.test.mjs +36 -0
- package/test/r169-delegate-cli-integration.test.mjs +93 -0
- package/test/r169-targeted-runner.mjs +16 -0
- package/test/r172-activation-recovery.test.mjs +95 -0
- package/test/r172-release-contract.test.mjs +35 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
|
@@ -6,7 +6,7 @@ import {fileURLToPath} from 'node:url';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
|
|
8
8
|
const root=fileURLToPath(new URL('..',import.meta.url));
|
|
9
|
-
const script=path.join(root,'scripts','
|
|
9
|
+
const script=path.join(root,'scripts','r172-protected-preflight.mjs');
|
|
10
10
|
const protectedRoot='C:\\Program Files';
|
|
11
11
|
const relative=path.win32.relative(protectedRoot,root);
|
|
12
12
|
const isProtected=process.platform==='win32'&&relative!==''&&relative!=='..'&&!relative.startsWith('..'+path.win32.sep)&&!path.win32.isAbsolute(relative);
|
|
@@ -14,38 +14,38 @@ function launch(file,...args){return spawnSync(process.execPath,[file,...args],{
|
|
|
14
14
|
encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:65536
|
|
15
15
|
});}
|
|
16
16
|
|
|
17
|
-
test('
|
|
17
|
+
test('R172 installed preflight respects R89 protected-vs-writable execution context',()=>{
|
|
18
18
|
const result=launch(script);
|
|
19
19
|
if(isProtected){
|
|
20
20
|
assert.equal(result.status,0,result.stderr);
|
|
21
|
-
assert.match(result.stdout,/
|
|
21
|
+
assert.match(result.stdout,/R172_PROTECTED_PREFLIGHT_READONLY_PASS/);
|
|
22
22
|
assert.match(result.stdout,/"terminalization_authorized":false/);
|
|
23
23
|
assert.match(result.stdout,/"activation_authorized":false/);
|
|
24
24
|
}else{
|
|
25
25
|
assert.notEqual(result.status,0);
|
|
26
|
-
assert.match(result.stderr,/
|
|
27
|
-
assert.match(result.stderr,/
|
|
26
|
+
assert.match(result.stderr,/R172_PROTECTED_PREFLIGHT=BLOCKED/);
|
|
27
|
+
assert.match(result.stderr,/R172_.*_(UNPROTECTED|VERSION_DRIFT)/);
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
test('
|
|
31
|
+
test('R172 writable development copy remains blocked when protected fixture exists',()=>{
|
|
32
32
|
const source=path.resolve(process.env.DEADBYTE_EXPECTED_DEVELOPMENT_ROOT||root);
|
|
33
33
|
if(path.win32.relative(protectedRoot,source).toLowerCase()===path.win32.relative(protectedRoot,root).toLowerCase()&&isProtected)
|
|
34
34
|
return test.skip('no separate development source configured');
|
|
35
|
-
const result=launch(path.join(source,'scripts','
|
|
35
|
+
const result=launch(path.join(source,'scripts','r172-protected-preflight.mjs'));
|
|
36
36
|
assert.notEqual(result.status,0);
|
|
37
|
-
assert.match(result.stderr,/
|
|
38
|
-
assert.match(result.stderr,/
|
|
37
|
+
assert.match(result.stderr,/R172_PROTECTED_PREFLIGHT=BLOCKED/);
|
|
38
|
+
assert.match(result.stderr,/R172_.*_UNPROTECTED/);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test('
|
|
41
|
+
test('R172 entrypoint does not accept flags to skip manifest, R89 or root checks',()=>{
|
|
42
42
|
const result=launch(script,'--skip-trust');
|
|
43
43
|
assert.notEqual(result.status,0);
|
|
44
|
-
assert.match(result.stderr,/
|
|
44
|
+
assert.match(result.stderr,/R172_PROTECTED_PREFLIGHT=BLOCKED/);
|
|
45
45
|
assert.match(result.stderr,/UNKNOWN_OPTION/);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test('
|
|
48
|
+
test('R172 protected preflight source is read-only and calls exact manifest and OS ACL verification',async()=>{
|
|
49
49
|
const src=await readFile(script,'utf8');
|
|
50
50
|
assert.match(src,/verifyManifestTreeExact\(/);
|
|
51
51
|
assert.match(src,/inspectTrustedInstallation\(/);
|
|
@@ -11,25 +11,15 @@ const parent='v0.15.61-055d1ff0aa99e60b';
|
|
|
11
11
|
const predecessorManifest='3316654646cc870584a7c969c2032af6c795229ff8c65e5a8b794f97c2458c41';
|
|
12
12
|
const predecessorTarball='fc3ae5b1505e316bc9dfedf8d063cb00641754c8f5f1306a31f8cc4bb84c9484';
|
|
13
13
|
|
|
14
|
-
test('
|
|
15
|
-
const
|
|
16
|
-
read('package.json'),read('package-lock.json'),read('RELEASE.json'),
|
|
17
|
-
read('PROVENANCE.json'),read('SBOM.cdx.json'),read('TEST-MATRIX.json'),read('bridge/package.json')]);
|
|
18
|
-
for(const version of [DEADBYTE_VERSION,pkg.version,lock.version,lock.packages[''].version,
|
|
19
|
-
release.version,provenance.version,bom.metadata.component.version,matrix.version,bridge.version])
|
|
20
|
-
assert.equal(version,'0.15.66');
|
|
21
|
-
assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:r168:0.15.66');
|
|
22
|
-
assert.equal(release.train,'R168');assert.equal(matrix.train,'R168');
|
|
14
|
+
test('R172 preserves the immutable R168 publication lineage separately from current identity',async()=>{
|
|
15
|
+
const release=await read('RELEASE.json');
|
|
23
16
|
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
24
17
|
assert.equal(release.runtime_predecessor.release_id,parent);
|
|
25
18
|
assert.equal(release.parent.release_id,parent);
|
|
26
|
-
assert.equal(release.source_predecessor.version,'0.15.65');
|
|
27
|
-
assert.equal(release.source_predecessor.train,'R167');
|
|
28
|
-
assert.equal(release.source_predecessor.manifest_sha256,predecessorManifest);
|
|
29
|
-
assert.equal(release.source_predecessor.tarball_sha256,predecessorTarball);
|
|
30
|
-
assert.equal(provenance.source_predecessor_manifest_file_sha256,predecessorManifest);
|
|
31
|
-
assert.equal(provenance.source_predecessor_tarball_sha256,predecessorTarball);
|
|
32
19
|
assert.equal(release.r168.scope,'REMOTE_VERSION_SKEW_FAIL_CLOSED_AND_READONLY_RECONCILIATION');
|
|
20
|
+
assert.equal(release.r168.source_predecessor_version,'0.15.65');
|
|
21
|
+
assert.equal(release.r168.source_predecessor_manifest_sha256,predecessorManifest);
|
|
22
|
+
assert.equal(release.r168.source_predecessor_tarball_sha256,predecessorTarball);
|
|
33
23
|
assert.equal(release.r168.owner_approval,'NOT_PRESENT');
|
|
34
24
|
assert.equal(release.r168.old_r65_activation_claim,'PRESERVED_UNKNOWN');
|
|
35
25
|
assert.equal(release.r168.production_activation,'NOT_RUN');
|
|
@@ -13,16 +13,17 @@ test('R168 status commands on skew observe the active release without activation
|
|
|
13
13
|
assert.equal(r.active_version,'0.15.61');
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
|
-
test('
|
|
17
|
-
const
|
|
18
|
-
'disarmprocess','disarmcoding','
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
assert.equal(
|
|
23
|
-
assert.equal(
|
|
24
|
-
assert.
|
|
25
|
-
assert.
|
|
16
|
+
test('R169 authority forwarding only, lifecycle remains skew-blocked',()=>{
|
|
17
|
+
const delegated=['armprocess','armcoding','armautonomous','armdesktop','armhost',
|
|
18
|
+
'disarmprocess','disarmcoding','disarmautonomous','disarmdesktop','disarmhost'];
|
|
19
|
+
const blocked=['restart','stop','start','resume','clean','toggle','fulltest','test','menu','url'];
|
|
20
|
+
for(const command of [...delegated,...blocked]){
|
|
21
|
+
const result=selectR168OneShotRemotePath({command,active,cliVersion:'0.15.65'});
|
|
22
|
+
assert.equal(result.mode,delegated.includes(command)?'delegate_active':'blocked_version_skew',command);
|
|
23
|
+
assert.equal(result.may_activate,false);
|
|
24
|
+
assert.equal(result.may_mutate_authority,delegated.includes(command));
|
|
25
|
+
assert.ok(result.message.includes('0.15.61'));
|
|
26
|
+
assert.ok(result.message.includes('0.15.65'));
|
|
26
27
|
}
|
|
27
28
|
});
|
|
28
29
|
test('R168 coherent one-shot still defers to existing signed coherence verification',()=>{
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {selectR168OneShotRemotePath} from '../src/remote-one-shot-coherence-r168.mjs';
|
|
4
|
+
|
|
5
|
+
const active={version:'0.15.61',release_id:'v0.15.61-055d1ff0aa99e60b'};
|
|
6
|
+
const cliVersion='0.15.66';
|
|
7
|
+
|
|
8
|
+
test('R169 explicit arm and disarm commands delegate to already installed active runtime without activating npm cache',()=>{
|
|
9
|
+
const commands=['armprocess','disarmprocess','armcoding','disarmcoding','armautonomous',
|
|
10
|
+
'disarmautonomous','armdesktop','disarmdesktop','armhost','disarmhost'];
|
|
11
|
+
for(const command of commands){
|
|
12
|
+
const result=selectR168OneShotRemotePath({command,active,cliVersion});
|
|
13
|
+
assert.equal(result.mode,'delegate_active',command);
|
|
14
|
+
assert.equal(result.may_activate,false,command);
|
|
15
|
+
assert.equal(result.active_version,'0.15.61',command);
|
|
16
|
+
assert.equal(result.may_mutate_authority,true,command);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
test('R169 leaves status as read-only and dangerous lifecycle commands fail closed on skew',()=>{
|
|
20
|
+
assert.equal(selectR168OneShotRemotePath({command:'status',active,cliVersion}).mode,'observe_active');
|
|
21
|
+
for(const command of ['restart','stop','start','resume','clean','toggle','park','menu','fulltest','test','hosttest','codingtest']){
|
|
22
|
+
const decision=selectR168OneShotRemotePath({command,active,cliVersion});
|
|
23
|
+
assert.equal(decision.mode,'blocked_version_skew',command);
|
|
24
|
+
assert.equal(decision.may_activate,false,command);
|
|
25
|
+
assert.equal(decision.may_mutate_authority,false,command);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
test('R169 never delegates privilege changes if caller is older than active runtime',()=>{
|
|
29
|
+
const latest={version:'0.15.67',release_id:'v0.15.67-0000000000000000'};
|
|
30
|
+
const r=selectR168OneShotRemotePath({command:'armprocess',active:latest,cliVersion});
|
|
31
|
+
assert.equal(r.mode,'blocked_version_skew');
|
|
32
|
+
assert.equal(r.may_mutate_authority,false);
|
|
33
|
+
});
|
|
34
|
+
test('R169 missing active runtime cannot turn arm into an implicit installer',()=>{
|
|
35
|
+
assert.throws(()=>selectR168OneShotRemotePath({command:'armprocess',active:null,cliVersion}),/active runtime binding/);
|
|
36
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,readFile,writeFile,rm} from 'node:fs/promises';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
import {createHash} from 'node:crypto';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
|
|
10
|
+
const cli=fileURLToPath(new URL('../src/deadbyte-cli.mjs',import.meta.url));
|
|
11
|
+
const hash=x=>createHash('sha256').update(x).digest('hex');
|
|
12
|
+
async function scratch(){
|
|
13
|
+
const state=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r169-cli-'));
|
|
14
|
+
const bin=path.join(state,'bin'),rel=path.join(state,'releases','fixture'),
|
|
15
|
+
gen=path.join(state,'generations','fixture');
|
|
16
|
+
for(const dir of [bin,rel,gen])await mkdir(dir,{recursive:true});
|
|
17
|
+
const launcher=path.join(bin,'DEADBYTE-MCP.ps1');
|
|
18
|
+
const log=path.join(state,'invocations.txt');
|
|
19
|
+
const source=`param([Parameter(ValueFromRemainingArguments=$true)][string[]]$ControllerArgs)
|
|
20
|
+
[IO.File]::AppendAllText('${log.replaceAll('\\','\\\\')}',([string]::Join(' ', $ControllerArgs)+'|'))
|
|
21
|
+
if($ControllerArgs[0] -eq 'armprocess') { Write-Output 'R169_CONTROLLER_COMMAND=armprocess';exit 0 }
|
|
22
|
+
if($ControllerArgs[0] -eq 'processstatus') { Write-Output 'Process plane: DISARMED';exit 0 }
|
|
23
|
+
Write-Error ('R169_FIXTURE_UNEXPECTED_COMMAND='+$ControllerArgs[0]);exit 5
|
|
24
|
+
`;
|
|
25
|
+
await writeFile(launcher,source,'utf8');
|
|
26
|
+
await writeFile(log,'','utf8');
|
|
27
|
+
const manifest=Buffer.from('R169 release placeholder manifest');
|
|
28
|
+
const generation=Buffer.from('R169 generation placeholder manifest');
|
|
29
|
+
await writeFile(path.join(rel,'MANIFEST.SHA256'),manifest);
|
|
30
|
+
await writeFile(path.join(gen,'GENERATION.SHA256'),generation);
|
|
31
|
+
await writeFile(path.join(gen,'controller-config.psd1'),'# scratch only\n');
|
|
32
|
+
const manifestSha=hash(manifest);
|
|
33
|
+
const pointer={schema:'deadbyte.active-release.v1',version:'0.15.61',
|
|
34
|
+
release_id:'v0.15.61-'+manifestSha.slice(0,16),
|
|
35
|
+
manifest_sha256:manifestSha,release_root:rel,generation_root:gen,
|
|
36
|
+
generation_manifest_sha256:hash(generation),
|
|
37
|
+
launcher_sha256:hash(await readFile(launcher)),config_path:path.join(gen,'controller-config.psd1')};
|
|
38
|
+
await writeFile(path.join(state,'active-release.json'),JSON.stringify(pointer)+'\n');
|
|
39
|
+
const run=(command='armprocess')=>spawnSync(process.execPath,[cli,'remote',command],{
|
|
40
|
+
cwd:path.dirname(cli),encoding:'utf8',shell:false,windowsHide:true,timeout:20000,
|
|
41
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:state},maxBuffer:128*1024});
|
|
42
|
+
return {state,launcher,log,run};
|
|
43
|
+
}
|
|
44
|
+
test('R169 npm-cache CLI delegates explicit armprocess to pinned installed runtime without auto-activation',async()=>{
|
|
45
|
+
if(process.platform!=='win32')return;
|
|
46
|
+
const {state,log,run}=await scratch();
|
|
47
|
+
try{
|
|
48
|
+
const result=run();
|
|
49
|
+
assert.equal(result.status,0,result.stderr);
|
|
50
|
+
assert.match(result.stdout,/R169_CONTROLLER_COMMAND=armprocess/);
|
|
51
|
+
assert.equal(await readFile(log,'utf8'),'armprocess|');
|
|
52
|
+
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
53
|
+
assert.equal(pointer.version,'0.15.61');
|
|
54
|
+
}finally{await rm(state,{force:true,recursive:true});}
|
|
55
|
+
});
|
|
56
|
+
test('R169 rejects changed launcher bytes before invoking privileged controller',async()=>{
|
|
57
|
+
if(process.platform!=='win32')return;
|
|
58
|
+
const {state,launcher,log,run}=await scratch();
|
|
59
|
+
try{
|
|
60
|
+
await writeFile(launcher,"throw 'modified launcher'\n");
|
|
61
|
+
const result=run();
|
|
62
|
+
assert.notEqual(result.status,0);
|
|
63
|
+
assert.match(result.stderr,/launcher|digest|hash|integrity|pin/i);
|
|
64
|
+
assert.equal(await readFile(log,'utf8'),'');
|
|
65
|
+
}finally{await rm(state,{force:true,recursive:true});}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('R169 rejects changed release manifest before any installed authority dispatch',async()=>{
|
|
69
|
+
if(process.platform!=='win32')return;
|
|
70
|
+
const {state,log,run}=await scratch();
|
|
71
|
+
try{
|
|
72
|
+
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
73
|
+
await writeFile(path.join(pointer.release_root,'MANIFEST.SHA256'),'tampered manifest');
|
|
74
|
+
const outcome=run();
|
|
75
|
+
assert.notEqual(outcome.status,0);
|
|
76
|
+
assert.match(outcome.stderr,/release manifest digest mismatch/);
|
|
77
|
+
assert.equal(await readFile(log,'utf8'),'');
|
|
78
|
+
}finally{await rm(state,{force:true,recursive:true});}
|
|
79
|
+
});
|
|
80
|
+
test('R169 rejects escaped release root and does not dispatch to an untrusted controller',async()=>{
|
|
81
|
+
if(process.platform!=='win32')return;
|
|
82
|
+
const {state,log,run}=await scratch();
|
|
83
|
+
try{
|
|
84
|
+
const pointerPath=path.join(state,'active-release.json');
|
|
85
|
+
const pointer=JSON.parse(await readFile(pointerPath,'utf8'));
|
|
86
|
+
pointer.release_root=path.join(os.tmpdir(),'escape-release');
|
|
87
|
+
await writeFile(pointerPath,JSON.stringify(pointer)+'\n');
|
|
88
|
+
const outcome=run();
|
|
89
|
+
assert.notEqual(outcome.status,0);
|
|
90
|
+
assert.match(outcome.stderr,/escapes state root/);
|
|
91
|
+
assert.equal(await readFile(log,'utf8'),'');
|
|
92
|
+
}finally{await rm(state,{force:true,recursive:true});}
|
|
93
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// R169 focused regression: existing R168 safety + new npm active-runtime delegation.
|
|
2
|
+
import {spawnSync} from 'node:child_process';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
const dir=path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const cases=['r168-five-plane-fence.test.mjs','r168-focused-suite.test.mjs',
|
|
7
|
+
'r168-crash-journal.test.mjs','r168-protected-entrypoint.test.mjs',
|
|
8
|
+
'r168-remote-one-shot-coherence.test.mjs',
|
|
9
|
+
'r169-delegate-active.test.mjs','r169-delegate-cli-integration.test.mjs'];
|
|
10
|
+
const r=spawnSync(process.execPath,['--test','--test-concurrency=1',...cases.map(n=>path.join(dir,n))],{
|
|
11
|
+
cwd:dir,encoding:'utf8',shell:false,windowsHide:true,timeout:120000,maxBuffer:4*1024*1024
|
|
12
|
+
});
|
|
13
|
+
process.stdout.write(r.stdout??'');
|
|
14
|
+
process.stderr.write(r.stderr??'');
|
|
15
|
+
if(r.error)throw r.error;
|
|
16
|
+
process.exitCode=r.status??1;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp, mkdir, readFile, rm, writeFile} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {classifyActivationPreflight} from '../src/activation-preflight-r65.mjs';
|
|
8
|
+
import {createActivationClaimManager} from '../src/activation-lock-r65.mjs';
|
|
9
|
+
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
10
|
+
import {prepareAuthorityTestTrust} from './r65-authority-test-trust.mjs';
|
|
11
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
12
|
+
|
|
13
|
+
const hex = c => c.repeat(64);
|
|
14
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
15
|
+
const planes = ['process', 'coding', 'autonomous', 'desktop', 'host'];
|
|
16
|
+
|
|
17
|
+
test('R172 accepts epoch-revoked only when independent proof says verified disarmed', () => {
|
|
18
|
+
const parent = {release_id: 'v0-parent', version: '0.15.61', manifest_sha256: hex('1'), generation_manifest_sha256: hex('2')};
|
|
19
|
+
const authority = Object.fromEntries(planes.map(plane => [plane, {
|
|
20
|
+
configured: true, armed: false, reason: plane === 'coding' ? 'epoch-revoked' : 'disarmed',
|
|
21
|
+
epoch_seq: null, epoch_sha256: null
|
|
22
|
+
}]));
|
|
23
|
+
const policy_sha256 = Object.fromEntries(planes.map((plane, index) => [plane, hex(String(index + 3))]));
|
|
24
|
+
const observation = withAuthorityProof({state: 'ONLINE', local_ready: true,
|
|
25
|
+
bridge: {alive: true, reason: 'verified', pid: 10}, core: {alive: true, reason: 'verified', pid: 11},
|
|
26
|
+
cloud: {alive: true, reason: 'verified', pid: 12}, supervisor: {running: true}, authority, policy_sha256,
|
|
27
|
+
active_release_id: parent.release_id, active_manifest_sha256: parent.manifest_sha256,
|
|
28
|
+
active_generation_manifest_sha256: parent.generation_manifest_sha256});
|
|
29
|
+
const result = classifyActivationPreflight({pointer: parent, expectedParent: parent,
|
|
30
|
+
candidate: {version: '0.15.67', manifest_sha256: hex('9')},
|
|
31
|
+
expectedCandidate: {version: '0.15.67', manifest_sha256: hex('9')}, observation});
|
|
32
|
+
assert.equal(result.state, 'ONLINE_READY');
|
|
33
|
+
const forged = structuredClone(observation);
|
|
34
|
+
forged.authority_proof.coding.state = 'UNVERIFIED';
|
|
35
|
+
assert.equal(classifyActivationPreflight({pointer: parent, expectedParent: parent,
|
|
36
|
+
candidate: {version: '0.15.67', manifest_sha256: hex('9')},
|
|
37
|
+
expectedCandidate: {version: '0.15.67', manifest_sha256: hex('9')}, observation: forged}).state, 'AUTHORITY_UNVERIFIED');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('R172 reclaims a pre-dispatch continuity abort only after signed revoke has a trusted terminal grant', async () => {
|
|
41
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-r172-continuity-abort-'));
|
|
42
|
+
try {
|
|
43
|
+
const runtime = await prepareAuthorityTestTrust(root), updates = path.join(root, 'evidence', 'updates');
|
|
44
|
+
const generation = path.join(root, 'generations', 'v0-parent-g1');
|
|
45
|
+
await mkdir(updates, {recursive: true}); await mkdir(generation, {recursive: true});
|
|
46
|
+
const grantFile = path.join(runtime, 'coding-grant.json');
|
|
47
|
+
const policy = {schema: 'deadbyte.coding-policy.v1', enabled: true, lease_file: grantFile,
|
|
48
|
+
roots: {project: {path: path.join(root, 'project')}}};
|
|
49
|
+
const policyBytes = Buffer.from(JSON.stringify(policy));
|
|
50
|
+
await writeFile(path.join(generation, 'coding-policy.json'), policyBytes);
|
|
51
|
+
const oldGrant = Buffer.from(JSON.stringify({schema: 'deadbyte.coding-grant.v1', policy_sha256: sha(policyBytes),
|
|
52
|
+
instance_nonce: hex('a'), root_ids: ['project'], granted_at_utc: new Date().toISOString()}));
|
|
53
|
+
await writeFile(grantFile, oldGrant);
|
|
54
|
+
const store = createAuthorityEpochStore({runtimeDir: runtime, policyBytes});
|
|
55
|
+
const armed = await store.append({plane: 'coding', action: 'grant', grantBytes: oldGrant,
|
|
56
|
+
grantInstanceNonce: hex('a'), updateId: null});
|
|
57
|
+
const manager = createActivationClaimManager({stateRoot: root}), subject = hex('b');
|
|
58
|
+
const claim = await manager.claim(subject), continuity = 'r65-' + 'c'.repeat(32);
|
|
59
|
+
const revoked = await store.append({plane: 'coding', action: 'transaction_revoke', grantBytes: oldGrant,
|
|
60
|
+
grantInstanceNonce: hex('a'), updateId: continuity, expectedHeadSha256: armed.event_sha256});
|
|
61
|
+
const newGrant = Buffer.from(JSON.stringify({schema: 'deadbyte.coding-grant.v1', policy_sha256: sha(policyBytes),
|
|
62
|
+
instance_nonce: hex('d'), root_ids: ['project'], granted_at_utc: new Date().toISOString()}));
|
|
63
|
+
await writeFile(grantFile, newGrant);
|
|
64
|
+
await store.append({plane: 'coding', action: 'grant', grantBytes: newGrant,
|
|
65
|
+
grantInstanceNonce: hex('d'), updateId: null, expectedHeadSha256: revoked.event_sha256});
|
|
66
|
+
const pointer = {schema: 'deadbyte.active-release.v1', release_id: 'v0-parent', version: '0.15.61',
|
|
67
|
+
manifest_sha256: hex('1'), generation_manifest_sha256: hex('2'), generation_root: generation};
|
|
68
|
+
await writeFile(path.join(root, 'active-release.json'), JSON.stringify(pointer));
|
|
69
|
+
await writeFile(path.join(updates, 'r65-activation-outcome-r172.json'), JSON.stringify({
|
|
70
|
+
schema: 'deadbyte.r65-activation-outcome.v1', claim_operation_id: claim.operation_id,
|
|
71
|
+
preflight_subject_sha256: subject, candidate_version: '0.15.66', candidate_manifest_sha256: hex('9'),
|
|
72
|
+
predecessor_release_id: pointer.release_id, outcome: {state: 'ACTIVATION_OUTCOME_UNKNOWN', preflight: 'AUTHORITY_UNVERIFIED'},
|
|
73
|
+
update_id: null, state_path: null, continuity_update_id: continuity,
|
|
74
|
+
observed_at_utc: new Date().toISOString()
|
|
75
|
+
}));
|
|
76
|
+
const next = await manager.claim(hex('e'));
|
|
77
|
+
assert.notEqual(next.operation_id, claim.operation_id);
|
|
78
|
+
await next.release();
|
|
79
|
+
} finally { await rm(root, {recursive: true, force: true}); }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('R172 keeps the claim when continuity revoke is not followed by a trusted terminal state', async () => {
|
|
83
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-r172-continuity-blocked-'));
|
|
84
|
+
try {
|
|
85
|
+
const updates = path.join(root, 'evidence', 'updates'); await mkdir(updates, {recursive: true});
|
|
86
|
+
const manager = createActivationClaimManager({stateRoot: root}), claim = await manager.claim(hex('a'));
|
|
87
|
+
await writeFile(path.join(updates, 'r65-activation-outcome-r172.json'), JSON.stringify({
|
|
88
|
+
schema: 'deadbyte.r65-activation-outcome.v1', claim_operation_id: claim.operation_id,
|
|
89
|
+
preflight_subject_sha256: hex('a'), candidate_version: '0.15.66', candidate_manifest_sha256: hex('9'),
|
|
90
|
+
predecessor_release_id: 'v0-parent', outcome: {state: 'ACTIVATION_OUTCOME_UNKNOWN', preflight: 'AUTHORITY_UNVERIFIED'},
|
|
91
|
+
update_id: null, state_path: null, continuity_update_id: 'r65-' + 'c'.repeat(32), observed_at_utc: new Date().toISOString()
|
|
92
|
+
}));
|
|
93
|
+
await assert.rejects(() => manager.claim(hex('e')), /unresolved|in progress/i);
|
|
94
|
+
} finally { await rm(root, {recursive: true, force: true}); }
|
|
95
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
7
|
+
|
|
8
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
|
+
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
|
+
const readOptional=async rel=>{try{return await read(rel);}catch(error){if(error?.code==='ENOENT')return null;throw error;}};
|
|
11
|
+
|
|
12
|
+
test('R172 binds the published source predecessor separately from the active runtime predecessor',async()=>{
|
|
13
|
+
const [pkg,lock,bridge,release,provenance,matrix,bom]=await Promise.all([
|
|
14
|
+
read('package.json'),readOptional('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
|
|
15
|
+
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
16
|
+
const versions=[pkg.version,bridge.version,release.version,provenance.version,matrix.version,
|
|
17
|
+
bom.metadata.component.version,DEADBYTE_VERSION];
|
|
18
|
+
if(lock)versions.push(lock.version,lock.packages[''].version);
|
|
19
|
+
for(const value of versions)assert.equal(value,'0.15.67');
|
|
20
|
+
assert.equal(release.train,'R172'); assert.equal(matrix.train,'R172');
|
|
21
|
+
assert.equal(release.source_predecessor.version,'0.15.66');
|
|
22
|
+
assert.equal(release.source_predecessor.manifest_sha256,'962e227dace247d59aa860211388fbde461c5f9c8a7e8a878e34a14a27d355f8');
|
|
23
|
+
assert.equal(release.source_predecessor.tarball_sha256,'31b194a4fbff7dbdb9f4f275b4ea49c5115f7b0bb2241c0c72d72ef690311a5d');
|
|
24
|
+
assert.equal(release.runtime_predecessor.release_id,'v0.15.61-055d1ff0aa99e60b');
|
|
25
|
+
assert.equal(release.r172.public_tool_delta,0); assert.equal(release.r172.authority_scope_delta,0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('R172 recovery remains fail closed until final protected and publication gates pass',async()=>{
|
|
29
|
+
const [release,matrix]=await Promise.all([read('RELEASE.json'),read('TEST-MATRIX.json')]);
|
|
30
|
+
assert.equal(release.r172.current_predispatch_claim,'PENDING_VERIFIED_RECONCILIATION');
|
|
31
|
+
assert.equal(release.r172.protected_full_regression,'PENDING_FINAL_BYTES');
|
|
32
|
+
assert.equal(matrix.r172.protected_native_positive,'PASS_27_TESTS_0_FAILURES');
|
|
33
|
+
assert.equal(release.r172.npm_publish,'NOT_RUN');
|
|
34
|
+
assert.equal(release.r172.production_activation,'NOT_RUN');
|
|
35
|
+
});
|
|
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
|
|
|
83
83
|
assert.deepEqual(diff.added,[]);
|
|
84
84
|
assert.deepEqual(diff.removed,[]);
|
|
85
85
|
const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
|
|
86
|
-
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66'].includes(packageVersion)){
|
|
86
|
+
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67'].includes(packageVersion)){
|
|
87
87
|
assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
|
|
88
88
|
assert.equal(diff.unchanged.length,41);
|
|
89
89
|
const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
|