deadbyte-mcp 0.15.47 → 0.15.49
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 +111 -48
- package/PROVENANCE.json +1 -1
- package/README.txt +18 -18
- package/RELEASE.json +3 -3
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +2 -2
- package/bench/r121-epoch-readhead.mjs +54 -0
- package/bench/r124-epoch-predecessor.mjs +53 -0
- package/bin/deadbyte-epoch-lock-r85.node +0 -0
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/HARNESS2-ADAPTATION-20261020.md +24 -0
- package/docs/R128-RELEASE-CANDIDATE.md +26 -0
- package/docs/R75-RECOVERY-HANDOFF.md +16 -0
- package/docs/R85-EPOCH-LOCK-BINARY.json +13 -0
- package/native/deadbyte-epoch-lock-r85.cpp +241 -0
- package/package.json +1 -1
- package/scripts/build-epoch-lock-r85.ps1 +34 -0
- package/scripts/deploy-r48.mjs +3 -1
- package/scripts/npm-release-r48.mjs +18 -4
- package/scripts/r75-npm-bin-integrity-gate.mjs +33 -0
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/verify-authority-epoch-history.mjs +38 -0
- package/scripts/verify-r70-full-regression.ps1 +12 -2
- package/src/activation-authority-proof-r75.mjs +67 -0
- package/src/activation-continuity-engine-r65.mjs +17 -10
- package/src/activation-preflight-r65.mjs +13 -0
- package/src/agent-eval-r42.mjs +30 -0
- package/src/authority-continuity-r65.mjs +442 -29
- package/src/autonomous-local-supervisor.mjs +77 -47
- package/src/deadbyte-cli.mjs +73 -46
- package/src/epoch-lock-r85.mjs +48 -0
- package/src/foreground-marker-cas-win32.mjs +284 -0
- package/src/foreground-process-identity.mjs +3 -3
- package/src/privileged-install-trust-r89.mjs +196 -0
- package/src/remote-live-log.mjs +15 -8
- package/src/remote-log-decode-r60.mjs +65 -0
- package/src/remote-session-guardian.mjs +13 -12
- package/src/runtime-update.mjs +5 -22
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +24 -0
- package/test/autonomous-local-supervisor.test.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/harness2-eval-context-isolation.test.mjs +90 -0
- package/test/helpers/r75-authority-proof.mjs +9 -0
- package/test/r100-posix-owned-lock-race.test.mjs +72 -0
- package/test/r101-acl-probe-environment.test.mjs +51 -0
- package/test/r106-trusted-powershell-spawn.test.mjs +53 -0
- package/test/r107-updater-powershell-path.test.mjs +77 -0
- package/test/r108-remote-powershell-path.test.mjs +59 -0
- package/test/r110-supervisor-powershell-path.test.mjs +28 -0
- package/test/r111-supervisor-marker-cas.test.mjs +116 -0
- package/test/r112-supervisor-claim-cas.test.mjs +63 -0
- package/test/r113-supervisor-cleanup-terminality.test.mjs +100 -0
- package/test/r114-supervisor-absence-proof.test.mjs +45 -0
- package/test/r115-posix-continuity-entrypoint.test.mjs +17 -0
- package/test/r116-authority-crash-matrix.test.mjs +83 -0
- package/test/r117-concurrent-orphan-recovery.test.mjs +54 -0
- package/test/r118-cross-process-orphan-recovery.test.mjs +99 -0
- package/test/r119-recovery-crash-after-commit.test.mjs +91 -0
- package/test/r120-authority-history-audit.test.mjs +103 -0
- package/test/r121-signed-transition-invariant.test.mjs +132 -0
- package/test/r122-epoch-bounded-read.test.mjs +58 -0
- package/test/r123-recovery-bounded-read.test.mjs +64 -0
- package/test/r124-live-log-integrity.test.mjs +66 -0
- package/test/r124-live-log-tail-boundaries.test.mjs +57 -0
- package/test/r124-signed-predecessor-shape.test.mjs +66 -0
- package/test/r125-restore-binding-types.test.mjs +63 -0
- package/test/r126-restore-policy-envelope.test.mjs +89 -0
- package/test/r127-restore-append-policy-boundary.test.mjs +25 -0
- package/test/r33-closeout-regression.test.mjs +2 -1
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +2 -2
- package/test/r55-offline-predecessor-guardian-family.test.mjs +2 -2
- package/test/r56-rollback-launcher-restore.test.mjs +2 -2
- package/test/r57-core-startup-race.test.mjs +2 -2
- package/test/r58-concurrent-recovery.test.mjs +10 -7
- package/test/r58-stale-owner.test.mjs +5 -6
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +2 -1
- package/test/r65-activation-preflight.test.mjs +7 -6
- package/test/r65-authority-epoch-recovery.test.mjs +3 -1
- package/test/r65-authority-epoch-store.test.mjs +9 -6
- package/test/r65-authority-restored-safe.test.mjs +2 -1
- package/test/r65-deploy-preflight.test.mjs +13 -14
- package/test/r65-epoch-native-activation-integration.test.mjs +5 -4
- package/test/r65-legacy-authority-migration.test.mjs +2 -1
- package/test/r67-npm-cross-volume.test.mjs +9 -2
- package/test/r71-full-verifier-artifact.test.mjs +21 -15
- package/test/r71-isolated-slot-rollback-restore.test.mjs +3 -1
- package/test/r71-release-identity.test.mjs +3 -1
- package/test/r74-kernel-lock.test.mjs +12 -6
- package/test/r74-prepublish-contract.test.mjs +3 -1
- package/test/r75-activation-authority-proof.test.mjs +46 -0
- package/test/r75-authority-evidence.test.mjs +77 -0
- package/test/r76-pinned-marker-cas.test.mjs +78 -0
- package/test/r77-epoch-grant-cas.test.mjs +51 -0
- package/test/r78-parent-junction-cas.test.mjs +62 -0
- package/test/r79-orphan-revocation-reconcile.test.mjs +88 -0
- package/test/r79-windows-fsync-regression.test.mjs +45 -0
- package/test/r80-durable-epoch-commit.test.mjs +76 -0
- package/test/r82-epoch-lock-owned-cas.test.mjs +93 -0
- package/test/r83-epoch-lock-fileid-cas.test.mjs +92 -0
- package/test/r84-epoch-lock-created-fileid.test.mjs +112 -0
- package/test/r85-epoch-lock-release-lifetime.test.mjs +25 -0
- package/test/r85-epoch-repeat-append.test.mjs +30 -0
- package/test/r86-epoch-cleanup-and-loader.test.mjs +126 -0
- package/test/r87-crashed-orphan-revoke-reconcile.test.mjs +110 -0
- package/test/r88-native-disposition-failure.test.mjs +35 -0
- package/test/r89-privileged-install-boundary.test.mjs +123 -0
- package/test/r92-acl-ace-applicability.test.mjs +45 -0
- package/test/r94-win32-propagation-enum.test.mjs +51 -0
- package/test/r99-privileged-import-closure.test.mjs +45 -0
- package/test/remote-session-cli.test.mjs +69 -9
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash, generateKeyPairSync, randomBytes, sign} from 'node:crypto';
|
|
4
|
+
import {mkdtemp, mkdir, rm, writeFile} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
8
|
+
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
|
+
|
|
10
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
const domain = Buffer.from('DEADBYTE-AUTHORITY-DURABLE-EPOCH-R65-V1\0');
|
|
12
|
+
const eventName = (seq, hash) => `${String(seq).padStart(16, '0')}-${hash}.json`;
|
|
13
|
+
|
|
14
|
+
test('R125 hot read rejects signed restore predecessor with array-valued SHA binding', async () => {
|
|
15
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-r125-binding-'));
|
|
16
|
+
try {
|
|
17
|
+
const runtimeDir = path.join(root, 'runtime');
|
|
18
|
+
const trust = path.join(root, 'trust');
|
|
19
|
+
const dir = path.join(runtimeDir, 'authority-epochs', 'process');
|
|
20
|
+
await mkdir(path.join(dir, 'events'), {recursive:true});
|
|
21
|
+
await mkdir(trust);
|
|
22
|
+
const keys = generateKeyPairSync('ed25519');
|
|
23
|
+
await writeFile(path.join(trust, 'ed25519-public.pem'), keys.publicKey.export({type:'spki', format:'pem'}));
|
|
24
|
+
const keyId = sha(keys.publicKey.export({type:'spki', format:'der'}));
|
|
25
|
+
const signEvent = body => {
|
|
26
|
+
const signature = sign(null, Buffer.concat([domain, Buffer.from(canonicalJson(body))]), keys.privateKey);
|
|
27
|
+
const event = {...body, attestation:{algorithm:'ed25519', domain:'DEADBYTE-AUTHORITY-DURABLE-EPOCH-R65-V1', key_id:keyId, signature_base64:signature.toString('base64')}};
|
|
28
|
+
const bytes = Buffer.from(canonicalJson(event));
|
|
29
|
+
return {bytes, hash:sha(bytes), event};
|
|
30
|
+
};
|
|
31
|
+
const nonce1 = randomBytes(32).toString('hex');
|
|
32
|
+
const nonce2 = randomBytes(32).toString('hex');
|
|
33
|
+
const nonce3 = randomBytes(32).toString('hex');
|
|
34
|
+
const oldGrant = sha(Buffer.from('old'));
|
|
35
|
+
const restoredGrant = sha(Buffer.from('restored'));
|
|
36
|
+
const currentGrantBytes = Buffer.from('current');
|
|
37
|
+
const common = {schema:'deadbyte.authority-epoch-event.r65.v1', plane:'process', recorded_at_utc:new Date().toISOString()};
|
|
38
|
+
const one = signEvent({...common, seq:1, action:'grant', update_id:null,
|
|
39
|
+
grant_sha256:oldGrant, grant_instance_nonce:nonce1, restore_binding:null, previous_event_sha256:null});
|
|
40
|
+
const two = signEvent({...common, seq:2, action:'transaction_revoke', update_id:'r125-update',
|
|
41
|
+
grant_sha256:oldGrant, grant_instance_nonce:nonce1, restore_binding:null, previous_event_sha256:one.hash});
|
|
42
|
+
const binding = {prior_grant_sha256:[oldGrant], prior_grant_instance_nonce:nonce1,
|
|
43
|
+
predecessor_policy_envelope_sha256:'1'.repeat(64), successor_policy_envelope_sha256:'2'.repeat(64),
|
|
44
|
+
candidate_manifest_sha256:'3'.repeat(64), continuity_subject_sha256:'4'.repeat(64)};
|
|
45
|
+
const three = signEvent({...common, seq:3, action:'transaction_restore', update_id:'r125-update',
|
|
46
|
+
grant_sha256:restoredGrant, grant_instance_nonce:nonce2, restore_binding:binding, previous_event_sha256:two.hash});
|
|
47
|
+
const fourBody = {...common, seq:4, action:'grant', update_id:null,
|
|
48
|
+
grant_sha256:sha(currentGrantBytes), grant_instance_nonce:nonce3, restore_binding:null,
|
|
49
|
+
previous_event_sha256:three.hash};
|
|
50
|
+
const four = signEvent(fourBody);
|
|
51
|
+
for (const [seq, signed] of [[1,one],[2,two],[3,three],[4,four]])
|
|
52
|
+
await writeFile(path.join(dir, 'events', eventName(seq, signed.hash)), signed.bytes);
|
|
53
|
+
await writeFile(path.join(dir, 'head.json'), Buffer.from(canonicalJson({
|
|
54
|
+
schema:'deadbyte.authority-epoch-head.r65.v1', plane:'process', seq:4, action:'grant', update_id:null,
|
|
55
|
+
grant_sha256:fourBody.grant_sha256, grant_instance_nonce:nonce3, restore_binding:null,
|
|
56
|
+
previous_event_sha256:three.hash, event_sha256:four.hash, event_file:eventName(4,four.hash)})));
|
|
57
|
+
const store = createAuthorityEpochStore({runtimeDir});
|
|
58
|
+
await assert.rejects(store.readHead({plane:'process'}), /authority epoch transaction restore binding invalid/);
|
|
59
|
+
assert.equal((await store.verifyGrant({plane:'process', grantBytes:currentGrantBytes, grantInstanceNonce:nonce3})).trusted, false);
|
|
60
|
+
} finally {
|
|
61
|
+
await rm(root, {recursive:true, force:true, maxRetries:8, retryDelay:100});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash, generateKeyPairSync, randomBytes, sign} from 'node:crypto';
|
|
4
|
+
import {mkdtemp, mkdir, rm, writeFile} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
8
|
+
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
|
+
|
|
10
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
const domain = Buffer.from('DEADBYTE-AUTHORITY-DURABLE-EPOCH-R65-V1\0');
|
|
12
|
+
const eventName = (seq, hash) => `${String(seq).padStart(16, '0')}-${hash}.json`;
|
|
13
|
+
|
|
14
|
+
async function signedRestoreFixture(bindingOverride) {
|
|
15
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-r126-envelope-'));
|
|
16
|
+
const runtimeDir = path.join(root, 'runtime');
|
|
17
|
+
const trustDir = path.join(root, 'trust');
|
|
18
|
+
const eventDir = path.join(runtimeDir, 'authority-epochs', 'process', 'events');
|
|
19
|
+
await mkdir(eventDir, {recursive:true});
|
|
20
|
+
await mkdir(trustDir);
|
|
21
|
+
const keys = generateKeyPairSync('ed25519');
|
|
22
|
+
await writeFile(path.join(trustDir, 'ed25519-public.pem'), keys.publicKey.export({type:'spki', format:'pem'}));
|
|
23
|
+
const keyId = sha(keys.publicKey.export({type:'spki', format:'der'}));
|
|
24
|
+
const signEvent = body => {
|
|
25
|
+
const signature = sign(null, Buffer.concat([domain, Buffer.from(canonicalJson(body))]), keys.privateKey);
|
|
26
|
+
const event = {...body, attestation:{algorithm:'ed25519', domain:'DEADBYTE-AUTHORITY-DURABLE-EPOCH-R65-V1', key_id:keyId, signature_base64:signature.toString('base64')}};
|
|
27
|
+
const bytes = Buffer.from(canonicalJson(event));
|
|
28
|
+
return {bytes, hash:sha(bytes)};
|
|
29
|
+
};
|
|
30
|
+
const oldNonce = randomBytes(32).toString('hex');
|
|
31
|
+
const newNonce = randomBytes(32).toString('hex');
|
|
32
|
+
const oldGrantSha = sha(Buffer.from('old-grant'));
|
|
33
|
+
const newGrantBytes = Buffer.from('new-grant');
|
|
34
|
+
const priorEnvelope = '1'.repeat(64);
|
|
35
|
+
const nextEnvelope = '2'.repeat(64);
|
|
36
|
+
const common = {schema:'deadbyte.authority-epoch-event.r65.v1', plane:'process', recorded_at_utc:new Date().toISOString()};
|
|
37
|
+
const one = signEvent({...common, seq:1, action:'grant', update_id:null, grant_sha256:oldGrantSha,
|
|
38
|
+
grant_instance_nonce:oldNonce, restore_binding:null, previous_event_sha256:null,
|
|
39
|
+
policy_sha256:'a'.repeat(64), policy_envelope_sha256:priorEnvelope});
|
|
40
|
+
const two = signEvent({...common, seq:2, action:'transaction_revoke', update_id:'r126-update',
|
|
41
|
+
grant_sha256:oldGrantSha, grant_instance_nonce:oldNonce, restore_binding:null,
|
|
42
|
+
previous_event_sha256:one.hash, policy_sha256:'a'.repeat(64), policy_envelope_sha256:priorEnvelope});
|
|
43
|
+
const restoreBinding = {prior_grant_sha256:oldGrantSha, prior_grant_instance_nonce:oldNonce,
|
|
44
|
+
predecessor_policy_envelope_sha256:priorEnvelope, successor_policy_envelope_sha256:nextEnvelope,
|
|
45
|
+
candidate_manifest_sha256:'3'.repeat(64), continuity_subject_sha256:'4'.repeat(64),
|
|
46
|
+
...bindingOverride};
|
|
47
|
+
const threeBody = {...common, seq:3, action:'transaction_restore', update_id:'r126-update',
|
|
48
|
+
grant_sha256:sha(newGrantBytes), grant_instance_nonce:newNonce, restore_binding:restoreBinding,
|
|
49
|
+
previous_event_sha256:two.hash, policy_sha256:'b'.repeat(64), policy_envelope_sha256:nextEnvelope};
|
|
50
|
+
const three = signEvent(threeBody);
|
|
51
|
+
for (const [seq, signed] of [[1,one],[2,two],[3,three]])
|
|
52
|
+
await writeFile(path.join(eventDir, eventName(seq, signed.hash)), signed.bytes);
|
|
53
|
+
await writeFile(path.join(runtimeDir, 'authority-epochs', 'process', 'head.json'), Buffer.from(canonicalJson({
|
|
54
|
+
schema:'deadbyte.authority-epoch-head.r65.v1', plane:'process', seq:3,
|
|
55
|
+
action:threeBody.action, update_id:threeBody.update_id,
|
|
56
|
+
grant_sha256:threeBody.grant_sha256, grant_instance_nonce:newNonce,
|
|
57
|
+
restore_binding:restoreBinding, previous_event_sha256:two.hash,
|
|
58
|
+
policy_sha256:threeBody.policy_sha256, policy_envelope_sha256:nextEnvelope,
|
|
59
|
+
event_sha256:three.hash, event_file:eventName(3,three.hash)
|
|
60
|
+
})));
|
|
61
|
+
return {root, runtimeDir, newGrantBytes, newNonce};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const [name, override] of [
|
|
65
|
+
['predecessor policy envelope', {predecessor_policy_envelope_sha256:'f'.repeat(64)}],
|
|
66
|
+
['successor policy envelope', {successor_policy_envelope_sha256:'e'.repeat(64)}]
|
|
67
|
+
]) test(`R126 signed restore rejects wrong ${name} despite valid signature`, async () => {
|
|
68
|
+
const fixture = await signedRestoreFixture(override);
|
|
69
|
+
try {
|
|
70
|
+
const store = createAuthorityEpochStore({runtimeDir:fixture.runtimeDir});
|
|
71
|
+
await assert.rejects(store.readHead({plane:'process'}), /authority epoch transition invalid/);
|
|
72
|
+
assert.equal((await store.verifyGrant({plane:'process', grantBytes:fixture.newGrantBytes,
|
|
73
|
+
grantInstanceNonce:fixture.newNonce})).trusted, false);
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(fixture.root, {recursive:true, force:true, maxRetries:8, retryDelay:100});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('R126 correctly bound signed restore stays readable', async () => {
|
|
80
|
+
const fixture = await signedRestoreFixture({});
|
|
81
|
+
try {
|
|
82
|
+
const store = createAuthorityEpochStore({runtimeDir:fixture.runtimeDir});
|
|
83
|
+
assert.equal((await store.readHead({plane:'process'})).action, 'transaction_restore');
|
|
84
|
+
assert.equal((await store.verifyGrant({plane:'process', grantBytes:fixture.newGrantBytes,
|
|
85
|
+
grantInstanceNonce:fixture.newNonce})).trusted, true);
|
|
86
|
+
} finally {
|
|
87
|
+
await rm(fixture.root, {recursive:true, force:true, maxRetries:8, retryDelay:100});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,rm} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
8
|
+
|
|
9
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
test('R127 wrong signed successor policy envelope fails before native lock or event write',async()=>{
|
|
11
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r127-binding-'));
|
|
12
|
+
try{
|
|
13
|
+
const policy=Buffer.from(JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:false}));
|
|
14
|
+
const nonce='a'.repeat(64);
|
|
15
|
+
const grant=Buffer.from(JSON.stringify({instance_nonce:nonce,policy_sha256:sha(policy)}));
|
|
16
|
+
const binding={prior_grant_sha256:sha(grant),prior_grant_instance_nonce:'b'.repeat(64),
|
|
17
|
+
predecessor_policy_envelope_sha256:'c'.repeat(64),
|
|
18
|
+
successor_policy_envelope_sha256:'d'.repeat(64),
|
|
19
|
+
candidate_manifest_sha256:'e'.repeat(64),continuity_subject_sha256:'f'.repeat(64)};
|
|
20
|
+
const store=createAuthorityEpochStore({runtimeDir:path.join(root,'runtime'),policyBytes:policy});
|
|
21
|
+
await assert.rejects(store.append({plane:'coding',action:'transaction_restore',grantBytes:grant,
|
|
22
|
+
grantInstanceNonce:nonce,updateId:'r127',expectedHeadSha256:'1'.repeat(64),restoreBinding:binding}),
|
|
23
|
+
/successor policy envelope mismatch/);
|
|
24
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
25
|
+
});
|
|
@@ -4,13 +4,14 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import { mkdtemp, mkdir, readFile, rm, writeFile, readdir } from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
7
8
|
import { spawn } from 'node:child_process';
|
|
8
9
|
import * as parity from '../scripts/release-parity-r39.mjs';
|
|
9
10
|
import { createToolAudit } from '../src/tool-audit.mjs';
|
|
10
11
|
import { verifyAuditChain } from '../src/audit-verify.mjs';
|
|
11
12
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
12
13
|
|
|
13
|
-
const root = path.resolve(new URL('../', import.meta.url)
|
|
14
|
+
const root = path.resolve(fileURLToPath(new URL('../', import.meta.url)));
|
|
14
15
|
const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
15
16
|
const temp = prefix => mkdtemp(path.join(os.tmpdir(), prefix));
|
|
16
17
|
const run = (command,args,options={}) => new Promise((resolve,reject)=>{
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
const target=process.env.R54_RUNTIME_UPDATE_PATH
|
|
7
7
|
? path.resolve(process.env.R54_RUNTIME_UPDATE_PATH)
|
|
8
|
-
:
|
|
8
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
9
9
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r54='+Date.now());
|
|
10
10
|
|
|
11
11
|
test('R54 exposes behavior-testable offline recovery blocker selection',()=>{
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
const target=process.env.R55_RUNTIME_UPDATE_PATH
|
|
7
7
|
? path.resolve(process.env.R55_RUNTIME_UPDATE_PATH)
|
|
8
|
-
:
|
|
8
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
9
9
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r55='+Date.now());
|
|
10
10
|
const nonce='a'.repeat(64);
|
|
11
11
|
|
|
@@ -2,13 +2,13 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { readFile } from 'node:fs/promises';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
|
|
9
9
|
const target=process.env.R56_RUNTIME_UPDATE_PATH
|
|
10
10
|
? path.resolve(process.env.R56_RUNTIME_UPDATE_PATH)
|
|
11
|
-
:
|
|
11
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
12
12
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r56='+Date.now());
|
|
13
13
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
14
14
|
const stateRoot=path.resolve(process.env.R56_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
7
|
const target=process.env.R57_RUNTIME_UPDATE_PATH
|
|
8
8
|
? path.resolve(process.env.R57_RUNTIME_UPDATE_PATH)
|
|
9
|
-
:
|
|
9
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
10
10
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r57='+Date.now());
|
|
11
11
|
const coreRoot='C:\\slot\\core';
|
|
12
12
|
const bridge={pid:20396,component:'bridge',script_path:'C:\\slot\\bridge\\src\\bridge.mjs',script_sha256:'a'.repeat(64)};
|
|
@@ -6,7 +6,7 @@ import {fileURLToPath} from 'node:url';
|
|
|
6
6
|
import {spawn} from 'node:child_process';
|
|
7
7
|
import {setTimeout as delay} from 'node:timers/promises';
|
|
8
8
|
|
|
9
|
-
test('
|
|
9
|
+
test('R75 simultaneous owner-unverified recovery attempts both retain authority and marker', {timeout:30000},async()=>{
|
|
10
10
|
const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
|
|
11
11
|
const root=await mkdtemp(path.join(base,'concurrent-recovery-'));await mkdir(path.join(root,'bin'));await mkdir(path.join(root,'runtime'));
|
|
12
12
|
const trace=path.join(root,'trace.txt'),markerPath=path.join(root,'runtime','remote-session.json');
|
|
@@ -47,14 +47,17 @@ exit 0
|
|
|
47
47
|
const c=spawn(process.execPath,[fileURLToPath(new URL('../src/deadbyte-cli.mjs',import.meta.url)),'remote'],{env:{...process.env,DEADBYTE_STATE_ROOT:root},stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
48
48
|
const o={stdout:'',stderr:''};c.stdout.on('data',b=>{o.stdout+=b;});c.stderr.on('data',b=>{o.stderr+=b;});o.exit=new Promise(resolve=>c.once('exit',resolve));children.push(c);outputs.push(o);
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const exits=await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(15000,null,{ref:false}).then(()=>null)]);
|
|
51
|
+
assert.ok(exits,'both owner-unverified attempts must terminate');
|
|
52
|
+
const commands=await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error));
|
|
53
|
+
await writeFile(path.join(root,'observation.json'),JSON.stringify({commands,exits,outputs:outputs.map(o=>({stdout:o.stdout,stderr:o.stderr})),simulation:'concurrent owner-unverified stale marker'}),{flag:'wx'});
|
|
54
|
+
assert.ok(exits.every(code=>code!==0));
|
|
55
|
+
for(const output of outputs)assert.match(output.stderr,/ownership unverified.*marker retained|already claimed \(startup\/recovery lock\)/i);
|
|
56
|
+
assert.equal(commands,'','neither attempt may touch authority');
|
|
57
|
+
assert.equal(JSON.parse(await readFile(markerPath,'utf8')).pid,2147483000);
|
|
55
58
|
}finally{
|
|
56
59
|
for(const c of children)if(c.exitCode===null)c.stdin.end();
|
|
57
|
-
await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(30000)]);
|
|
60
|
+
await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(30000,null,{ref:false})]);
|
|
58
61
|
for(const c of children)if(c.exitCode===null&&c.signalCode===null)c.kill();
|
|
59
62
|
}
|
|
60
63
|
});
|
|
@@ -46,7 +46,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
|
|
|
46
46
|
assert.notEqual(code,0);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
test('
|
|
49
|
+
test('R75 bound stale birth identity retains marker without touching a reused PID or authority', {timeout:60000}, async()=>{
|
|
50
50
|
const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';
|
|
51
51
|
await mkdir(base,{recursive:true});const root=await mkdtemp(path.join(base,'stale-birth-'));
|
|
52
52
|
await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
|
|
@@ -61,11 +61,10 @@ test('R58 bound stale birth identity reconciles on the first invocation without
|
|
|
61
61
|
let stdout='',stderr='';child.stdout.on('data',b=>{stdout+=b;});child.stderr.on('data',b=>{stderr+=b;});
|
|
62
62
|
const code=await new Promise(resolve=>child.once('close',resolve));
|
|
63
63
|
await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stdout,stderr,simulation:'birth identity drift at a live unrelated numeric PID'}),{flag:'wx'});
|
|
64
|
-
assert.
|
|
65
|
-
assert.
|
|
66
|
-
assert.
|
|
67
|
-
assert.
|
|
68
|
-
await assert.rejects(readFile(markerPath),{code:'ENOENT'});
|
|
64
|
+
assert.notEqual(code,0);
|
|
65
|
+
assert.match(stderr,/owner identity mismatch; marker retained/i);
|
|
66
|
+
assert.equal(await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error)),'','identity drift must cause no authority commands');
|
|
67
|
+
assert.deepEqual(JSON.parse(await readFile(markerPath,'utf8')),marker);
|
|
69
68
|
assert.equal(foregroundAlive(),true,'unrelated live process must not be killed');
|
|
70
69
|
function foregroundAlive(){try{process.kill(process.pid,0);return true;}catch{return false;}}
|
|
71
70
|
});
|
|
@@ -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'].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'].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');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-63a78ad159ad3003',version:'0.15.41',manifest_sha256:'63a78ad159ad3003db3fefafcd468bef22599c829a867f4f10a46c7a20748b3e',generation_manifest_sha256:'19ae2efcdcde9f80c474f0e55851d4ada51c22813d3e05af03b11acb28c3fca5'};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'a'.repeat(64)};
|
|
@@ -19,7 +20,7 @@ function fixture(initial={}){
|
|
|
19
20
|
let current={state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:authorityEntries(initialVector,initial.epochNative===true),policy_sha256:policies,
|
|
20
21
|
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,active_generation_manifest_sha256:parent.generation_manifest_sha256,...initial};
|
|
21
22
|
if(initial.authority)current={...current,authority:authorityEntries(initial.authority,initial.epochNative===true)};
|
|
22
|
-
return {calls,observe:async()=>current,command:async name=>{
|
|
23
|
+
return {calls,observe:async()=>withAuthorityProof(current),command:async name=>{
|
|
23
24
|
calls.push(name);
|
|
24
25
|
if(name.startsWith('disarm')){
|
|
25
26
|
const plane=name.slice(6);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {classifyActivationPreflight,authorityTransitionPlan} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-63a78ad159ad3003',version:'0.15.41',manifest_sha256:'63a78ad159ad3003db3fefafcd468bef22599c829a867f4f10a46c7a20748b3e',generation_manifest_sha256:'19ae2efcdcde9f80c474f0e55851d4ada51c22813d3e05af03b11acb28c3fca5'};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'a'.repeat(64)};
|
|
@@ -8,7 +9,7 @@ const authority={process:true,coding:true,autonomous:true,desktop:false,host:fal
|
|
|
8
9
|
const policies={process:'1'.repeat(64),coding:'2'.repeat(64),autonomous:'3'.repeat(64),desktop:'4'.repeat(64),host:'5'.repeat(64)};
|
|
9
10
|
const online={state:'ONLINE',local_ready:true,bridge:{alive:true,reason:'verified',pid:10},core:{alive:true,reason:'verified',pid:11},cloud:{alive:true,reason:'verified',pid:12},supervisor:{running:true},authority,policy_sha256:policies,
|
|
10
11
|
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
11
|
-
const input={pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,observation:online};
|
|
12
|
+
const input={pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,observation:withAuthorityProof(online)};
|
|
12
13
|
|
|
13
14
|
test('preflight accepts exact online predecessor with full observed identity and authority vector',()=>{
|
|
14
15
|
const result=classifyActivationPreflight(input);
|
|
@@ -19,7 +20,7 @@ test('preflight accepts exact online predecessor with full observed identity and
|
|
|
19
20
|
|
|
20
21
|
test('host armed is not safe; plan only disarms Host and restores exact captured vector',()=>{
|
|
21
22
|
const armed={...authority,host:true};
|
|
22
|
-
const result=classifyActivationPreflight({...input,observation:{...online,authority:armed}});
|
|
23
|
+
const result=classifyActivationPreflight({...input,observation:withAuthorityProof({...online,authority:armed})});
|
|
23
24
|
assert.equal(result.state,'ONLINE_HOST_ARMED');assert.equal(result.safe_to_activate,false);
|
|
24
25
|
const plan=authorityTransitionPlan(result.authority);
|
|
25
26
|
assert.deepEqual(plan.before,['disarmhost']);
|
|
@@ -27,9 +28,9 @@ test('host armed is not safe; plan only disarms Host and restores exact captured
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
test('parked dirty, generation drift and identity loss fail closed without an activation command',()=>{
|
|
30
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,state:'PARKED'}}).state,'PARKED_DIRTY');
|
|
31
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,state:'PARKED'})}).state,'PARKED_DIRTY');
|
|
31
32
|
assert.equal(classifyActivationPreflight({...input,pointer:{...parent,generation_manifest_sha256:'b'.repeat(64)}}).state,'POINTER_DRIFT');
|
|
32
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,active_release_id:'wrong'}}).state,'POINTER_DRIFT');
|
|
33
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,policy_sha256:{...policies,host:null}}}).state,'UNKNOWN');
|
|
34
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,cloud:{alive:false,reason:'missing'}}}).state,'RUNTIME_DRIFT');
|
|
33
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,active_release_id:'wrong'})}).state,'POINTER_DRIFT');
|
|
34
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,policy_sha256:{...policies,host:null}})}).state,'UNKNOWN');
|
|
35
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,cloud:{alive:false,reason:'missing'}})}).state,'RUNTIME_DRIFT');
|
|
35
36
|
});
|
|
@@ -19,7 +19,9 @@ test('dead-owner epoch lock is recovered but a live-owner lock is never stolen',
|
|
|
19
19
|
const runtime=await prepareAuthorityTestTrust(root);
|
|
20
20
|
const dir=path.join(runtime,'authority-epochs','coding');
|
|
21
21
|
await mkdir(dir,{recursive:true});
|
|
22
|
-
await writeFile(path.join(dir,'.lock'),JSON.stringify({schema:'deadbyte.authority-epoch-lock.r65.v1',pid:2147483647,token:'dead',created_at_utc:'2026-10-06T00:00:00Z'
|
|
22
|
+
await writeFile(path.join(dir,'.lock'),JSON.stringify({schema:'deadbyte.authority-epoch-lock.r65.v1',pid:2147483647,token:'dead',created_at_utc:'2026-10-06T00:00:00Z',
|
|
23
|
+
owner_identity:{schema:'deadbyte.foreground-process.v1',pid:2147483647,parent_pid:0,creation_utc:'2026-01-01T00:00:00Z',
|
|
24
|
+
executable_path:process.execPath,command_line_sha256:'0'.repeat(64)}}));
|
|
23
25
|
const store=createAuthorityEpochStore({runtimeDir:runtime});
|
|
24
26
|
const nonce='1'.repeat(64),grant=grantBytes(nonce);
|
|
25
27
|
const head=await store.append({plane:'coding',action:'grant',grantBytes:grant,grantInstanceNonce:nonce,updateId:null});
|
|
@@ -4,7 +4,7 @@ import {createHash} from 'node:crypto';
|
|
|
4
4
|
import {mkdtemp,mkdir,readFile,rm,writeFile} from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
7
|
+
import {createAuthorityEpochStore,deriveSecurityEnvelope} from '../src/authority-continuity-r65.mjs';
|
|
8
8
|
import {prepareAuthorityTestTrust} from './r65-authority-test-trust.mjs';
|
|
9
9
|
|
|
10
10
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
@@ -32,9 +32,12 @@ async function fixture(run){
|
|
|
32
32
|
}finally{await rm(root,{recursive:true,force:true});}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function restoreBinding(oldGrant,oldNonce){
|
|
35
|
+
function restoreBinding(oldGrant,oldNonce,policyBytes){
|
|
36
|
+
// A successful restore must describe the real signed policy envelope.
|
|
37
|
+
// This fixture keeps the same policy on both sides of the transaction.
|
|
38
|
+
const envelope=deriveSecurityEnvelope('process',JSON.parse(policyBytes)).sha256;
|
|
36
39
|
return {prior_grant_sha256:sha(oldGrant),prior_grant_instance_nonce:oldNonce,
|
|
37
|
-
predecessor_policy_envelope_sha256:
|
|
40
|
+
predecessor_policy_envelope_sha256:envelope,successor_policy_envelope_sha256:envelope,
|
|
38
41
|
candidate_manifest_sha256:'3'.repeat(64),continuity_subject_sha256:'4'.repeat(64)};
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -63,7 +66,7 @@ test('stale transaction CAS cannot overwrite a later user revoke',()=>fixture(as
|
|
|
63
66
|
const nextNonce='e'.repeat(64),nextGrant=Buffer.from(JSON.stringify({...f.grant,instance_nonce:nextNonce}));
|
|
64
67
|
await assert.rejects(()=>f.store.append({plane:'process',action:'transaction_restore',grantBytes:nextGrant,
|
|
65
68
|
grantInstanceNonce:nextNonce,updateId:'r65-store-test',expectedHeadSha256:tx.event_sha256,
|
|
66
|
-
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce)}),/compare-and-swap conflict/);
|
|
69
|
+
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce,f.policyBytes)}),/compare-and-swap conflict/);
|
|
67
70
|
}));
|
|
68
71
|
|
|
69
72
|
test('replaying an older signed head fails closed when a later event remains',()=>fixture(async f=>{
|
|
@@ -83,11 +86,11 @@ test('transaction restore requires a fresh nonce and becomes the trusted head',(
|
|
|
83
86
|
grantInstanceNonce:f.grant.instance_nonce,updateId,expectedHeadSha256:granted.event_sha256});
|
|
84
87
|
await assert.rejects(()=>f.store.append({plane:'process',action:'transaction_restore',grantBytes:f.grantBytes,
|
|
85
88
|
grantInstanceNonce:f.grant.instance_nonce,updateId,expectedHeadSha256:revoked.event_sha256,
|
|
86
|
-
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce)}),/fresh nonce|resurrect/i);
|
|
89
|
+
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce,f.policyBytes)}),/fresh nonce|resurrect/i);
|
|
87
90
|
const newNonce='f'.repeat(64),newGrant=Buffer.from(JSON.stringify({...f.grant,instance_nonce:newNonce}));
|
|
88
91
|
const restored=await f.store.append({plane:'process',action:'transaction_restore',grantBytes:newGrant,
|
|
89
92
|
grantInstanceNonce:newNonce,updateId,expectedHeadSha256:revoked.event_sha256,
|
|
90
|
-
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce)});
|
|
93
|
+
restoreBinding:restoreBinding(f.grantBytes,f.grant.instance_nonce,f.policyBytes)});
|
|
91
94
|
const verified=await f.store.verifyGrant({plane:'process',grantBytes:newGrant,grantInstanceNonce:newNonce,policyBytes:f.policyBytes});
|
|
92
95
|
assert.equal(verified.trusted,true);
|
|
93
96
|
assert.equal(verified.event_sha256,restored.event_sha256);
|
|
@@ -7,6 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
8
8
|
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
9
|
import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
|
|
10
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
10
11
|
|
|
11
12
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
13
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'a'.repeat(64),generation_manifest_sha256:'b'.repeat(64)};
|
|
@@ -26,7 +27,7 @@ test('orchestrator reports RESTORED_SAFE when all restored authority is safe but
|
|
|
26
27
|
return {authority_outcomes:{process:'RESTORED_SAFE',coding:'NOT_APPLICABLE',autonomous:'NOT_APPLICABLE',desktop:'NOT_APPLICABLE',host:'NOT_APPLICABLE'},restored_nonces:{process:'8'.repeat(64)},reauth_planes:[]};}
|
|
27
28
|
};
|
|
28
29
|
const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
|
|
29
|
-
observe:async()=>current,command:async()=>{throw new Error('generic command forbidden');},
|
|
30
|
+
observe:async()=>withAuthorityProof(current),command:async()=>{throw new Error('generic command forbidden');},
|
|
30
31
|
activate:async()=>({verified:true,release_id:'v0.15.42-candidate'}),continuityEngine});
|
|
31
32
|
assert.equal(result.state,'ACTIVATED');
|
|
32
33
|
assert.equal(result.authority_continuity,'RESTORED_SAFE');
|
|
@@ -11,8 +11,9 @@ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
|
11
11
|
test('deploy dry preflight observes isolated authority without opening an update transaction', {skip:process.platform!=='win32'},async()=>{
|
|
12
12
|
const isolated=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-deploy-preflight-'));
|
|
13
13
|
try{
|
|
14
|
-
const state=path.join(isolated,'state'),bin=path.join(state,'bin');
|
|
14
|
+
const state=path.join(isolated,'state'),bin=path.join(state,'bin'),runtime=path.join(state,'runtime');
|
|
15
15
|
await mkdir(bin,{recursive:true});
|
|
16
|
+
await mkdir(runtime,{recursive:true});
|
|
16
17
|
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
17
18
|
const parent=release.parent;
|
|
18
19
|
const generationRoot=path.join(state,'generations',`${parent.release_id}-fixture`);
|
|
@@ -20,9 +21,11 @@ test('deploy dry preflight observes isolated authority without opening an update
|
|
|
20
21
|
await mkdir(generationRoot,{recursive:true});await mkdir(policyRoot,{recursive:true});
|
|
21
22
|
const fields={process:'ProcessPolicyFile',coding:'CodingPolicyFile',autonomous:'AutonomousPolicyFile',desktop:'DesktopPolicyFile',host:'HostPolicyFile'};
|
|
22
23
|
const policyLines=[];
|
|
24
|
+
const grantField={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
|
|
23
25
|
for(const [plane,key] of Object.entries(fields)){
|
|
24
26
|
const file=path.join(policyRoot,`${plane}.json`);
|
|
25
|
-
await writeFile(file,JSON.stringify({schema:`
|
|
27
|
+
await writeFile(file,JSON.stringify({schema:`deadbyte.${plane}-policy.v${plane==='host'?2:1}`,enabled:true,
|
|
28
|
+
[grantField[plane]]:path.join(runtime,`${plane}-grant.json`)}));
|
|
26
29
|
policyLines.push(`${key} = '${file.replaceAll("'","''")}'`);
|
|
27
30
|
}
|
|
28
31
|
await writeFile(path.join(generationRoot,'controller-config.psd1'),`@{ ${policyLines.join('; ')} }\n`);
|
|
@@ -30,26 +33,22 @@ test('deploy dry preflight observes isolated authority without opening an update
|
|
|
30
33
|
release_root:path.join(state,'releases',parent.release_id),generation_root:generationRoot,
|
|
31
34
|
launcher_sha256:'a'.repeat(64),config_path:path.join(generationRoot,'controller-config.psd1')}));
|
|
32
35
|
const ps1=`param([string]$Command,[switch]$Json)\nif($Command -ne 'status' -or -not $Json){throw 'unexpected mutating command'}\n@{schema='deadbyte.controller-observation.v1';state='ONLINE';local_ready=$true;bridge=@{alive=$true;reason='verified';pid=10};core=@{alive=$true;reason='verified';pid=11};cloud=@{alive=$true;reason='verified';pid=12};supervisor=@{running=$true};authority=@{process=@{armed=$true;configured=$true;reason='fixture'};coding=@{armed=$true;configured=$true;reason='fixture'};autonomous=@{armed=$true;configured=$true;reason='fixture'};desktop=@{armed=$false;configured=$true;reason='fixture'};host=@{armed=$false;configured=$true;reason='fixture'}}} | ConvertTo-Json -Depth 6 -Compress\n`;
|
|
33
|
-
|
|
36
|
+
const cleanPs1=ps1.replaceAll('armed=$true','armed=$false').replaceAll("reason='fixture'","reason='disarmed'");
|
|
37
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),cleanPs1);
|
|
34
38
|
const run=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
|
|
35
39
|
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
|
|
36
40
|
assert.equal(run.status,0,run.stderr);
|
|
37
41
|
const result=JSON.parse(run.stdout.trim());
|
|
38
42
|
assert.equal(result.state,'ONLINE_READY');
|
|
39
|
-
assert.equal(result.authority_continuity,'
|
|
40
|
-
assert.deepEqual(result.reauth_planes,[
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
.replace('autonomous=@{armed=$true','autonomous=@{armed=$false')
|
|
44
|
-
.replace('host=@{armed=$false','host=@{armed=$true');
|
|
45
|
-
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),hostOnly);
|
|
43
|
+
assert.equal(result.authority_continuity,'NOT_APPLICABLE');
|
|
44
|
+
assert.deepEqual(result.reauth_planes,[]);
|
|
45
|
+
const unverified=cleanPs1.replace("host=@{armed=$false;configured=$true;reason='disarmed'", "host=@{armed=$false;configured=$true;reason='unverified'");
|
|
46
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),unverified);
|
|
46
47
|
const hostRun=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
|
|
47
48
|
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
|
|
48
49
|
assert.equal(hostRun.status,0,hostRun.stderr);
|
|
49
50
|
const hostResult=JSON.parse(hostRun.stdout.trim());
|
|
50
|
-
assert.equal(hostResult.state,'
|
|
51
|
-
assert.
|
|
52
|
-
assert.deepEqual(hostResult.reauth_planes,['host']);
|
|
53
|
-
assert.deepEqual((await readdir(state)).sort(),['active-release.json','bin','generations','policies']);
|
|
51
|
+
assert.equal(hostResult.state,'AUTHORITY_UNVERIFIED');
|
|
52
|
+
assert.deepEqual((await readdir(state)).sort(),['active-release.json','bin','generations','policies','runtime']);
|
|
54
53
|
}finally{await rm(isolated,{recursive:true,force:true});}
|
|
55
54
|
});
|
|
@@ -7,6 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
8
8
|
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
9
|
import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
|
|
10
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
10
11
|
|
|
11
12
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
13
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'a'.repeat(64),generation_manifest_sha256:'b'.repeat(64)};
|
|
@@ -44,7 +45,7 @@ test('epoch-native orchestrator handles BOM-encoded process grant with transacti
|
|
|
44
45
|
const grant=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(bytes));
|
|
45
46
|
armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
|
|
46
47
|
}
|
|
47
|
-
return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
48
|
+
return withAuthorityProof({state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
48
49
|
process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
|
|
49
50
|
coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
50
51
|
autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
@@ -53,7 +54,7 @@ test('epoch-native orchestrator handles BOM-encoded process grant with transacti
|
|
|
53
54
|
},policy_sha256:{process:sha(policyBytes),...dummy},
|
|
54
55
|
active_release_id:switched?'v0.15.42-candidate':parent.release_id,
|
|
55
56
|
active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
|
|
56
|
-
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
|
|
57
|
+
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256});
|
|
57
58
|
};
|
|
58
59
|
const engine=createEpochNativeContinuityEngine({stateRoot:state,
|
|
59
60
|
predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
|
|
@@ -107,7 +108,7 @@ test('epoch-native explicit user revoke after transaction revoke wins over orche
|
|
|
107
108
|
const grant=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(bytes));
|
|
108
109
|
armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
|
|
109
110
|
}
|
|
110
|
-
return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
111
|
+
return withAuthorityProof({state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
111
112
|
process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
|
|
112
113
|
coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
113
114
|
autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
@@ -116,7 +117,7 @@ test('epoch-native explicit user revoke after transaction revoke wins over orche
|
|
|
116
117
|
},policy_sha256:{process:sha(policyBytes),...dummy},
|
|
117
118
|
active_release_id:switched?'v0.15.42-candidate':parent.release_id,
|
|
118
119
|
active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
|
|
119
|
-
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
|
|
120
|
+
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256});
|
|
120
121
|
};
|
|
121
122
|
const engine=createEpochNativeContinuityEngine({stateRoot:state,
|
|
122
123
|
predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {classifyAuthorityContinuity,orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'1'.repeat(64),generation_manifest_sha256:'2'.repeat(64)};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'3'.repeat(64)};
|
|
@@ -15,7 +16,7 @@ function fixture(vector){
|
|
|
15
16
|
authority:Object.fromEntries(Object.entries(vector).map(([plane,armed])=>[plane,legacyEntry(armed)]))};
|
|
16
17
|
return {
|
|
17
18
|
calls,
|
|
18
|
-
observe:async()=>observation,
|
|
19
|
+
observe:async()=>withAuthorityProof(observation),
|
|
19
20
|
command:async command=>{
|
|
20
21
|
calls.push(command);
|
|
21
22
|
if(command.startsWith('disarm')){
|