Files
chimera-Public/test/server/ch-1106-ps5upload-runtime.test.js

749 lines
31 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const test = require('node:test');
const { EventEmitter } = require('node:events');
const { PS5UPLOAD_RUNTIME, createPs5UploadRuntimeService } = require('../../src/services/ps5upload-runtime-service');
const { LINUX_LISTENER_PROCESS_EVIDENCE, LINUX_PROCESS_EVIDENCE } = require('../../src/services/linux-listener-process');
const digest = value => crypto.createHash('sha256').update(value).digest('hex');
const TEST_BOOT_ID = '11111111-2222-4333-8444-555555555555';
const TEST_NAMESPACES = Object.freeze({ net: 'net:[4001]', pid: 'pid:[4002]', mnt: 'mnt:[4003]' });
function listenerIdentity(input, overrides = {}) {
return {
verified: true,
bootId: TEST_BOOT_ID,
pid: 4242,
startTimeTicks: '123456',
socketInode: '555',
exePath: input.enginePath,
exeDev: '11',
exeIno: '22',
sha256: input.expectedSha256,
uid: 10001,
namespaces: TEST_NAMESPACES,
cgroup: '0::/docker/chimera',
evidence: LINUX_LISTENER_PROCESS_EVIDENCE,
...overrides,
};
}
function processIdentity(input, overrides = {}) {
const { socketInode: _socketInode, ...identity } = listenerIdentity(input, overrides);
return { ...identity, evidence: LINUX_PROCESS_EVIDENCE };
}
test('A5 production pin selects the clean static 5.4.19 engine proven on the deployed host', () => {
assert.equal(PS5UPLOAD_RUNTIME.version, '5.4.19');
assert.equal(PS5UPLOAD_RUNTIME.revision, 'ec0e5c4e95520f2dc26c226a909dc5a95e385f41');
assert.equal(PS5UPLOAD_RUNTIME.enginePath, '/payloads/ps5upload/5.4.19/ps5upload-engine-static');
assert.equal(PS5UPLOAD_RUNTIME.engineSha256, 'd4055f43a57b36d1d2dec3e70ec03f73c9c47bc311d2b2f87145509ed53c8ba5');
});
function fixture(t, overrides = {}) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-ps5upload-runtime-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const engine = Buffer.from('engine-5.4.19');
const payload = Buffer.from('elf-5.4.19');
const dpi = Buffer.from('dpi-5.4.19');
const enginePath = path.join(root, 'ps5upload-engine');
const payloadPath = path.join(root, 'ps5upload.elf');
const dpiPath = path.join(root, 'ezremote-dpi.elf');
fs.writeFileSync(enginePath, engine, { mode: 0o750 });
fs.writeFileSync(payloadPath, payload, { mode: 0o640 });
fs.writeFileSync(dpiPath, dpi, { mode: 0o640 });
let payloadGeneration = 1;
const customFetch = overrides.fetch;
const customTcpSend = overrides.tcpSend;
const remainingOverrides = { ...overrides };
delete remainingOverrides.fetch;
delete remainingOverrides.tcpSend;
if (remainingOverrides.processPlatform === 'linux' && !remainingOverrides.inspectSpawnedProcess) {
remainingOverrides.inspectSpawnedProcess = async input => processIdentity(input);
}
return createPs5UploadRuntimeService({
enginePath,
payloadPath,
dpiPath,
expectedEngineSha256: digest(engine),
expectedPayloadSha256: digest(payload),
expectedDpiSha256: digest(dpi),
consoleHost: () => '192.0.2.55',
fetch: customFetch || (async url => {
const href = String(url);
if (href.endsWith('/api/version')) return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
if (href.includes('/api/ps5/status')) return { ok: true, status: 200, json: async () => ({ version: '5.4.19', instance_id: String(payloadGeneration), started_at_unix: payloadGeneration }) };
return { ok: true, status: 200, json: async () => ({ ok: true, users: [] }) };
}),
tcpProbe: async () => true,
enginePortProbe: async () => false,
tcpSend: async input => {
payloadGeneration += 1;
return customTcpSend ? customTcpSend(input) : { ok: true, bytes: input.bytes.length };
},
delay: async () => {},
processPlatform: 'win32',
inspectExistingEngine: async input => listenerIdentity(input),
terminateProvenProcess: async () => ({ terminated: true, identityGone: true, forced: false }),
...remainingOverrides,
});
}
test('runtime preflight proves exact engine and payload artifacts before reporting ready', async t => {
const runtime = fixture(t);
const result = await runtime.preflight();
assert.equal(result.ready, true);
assert.equal(result.engine.version, '5.4.19');
assert.equal(result.payload.verified, true);
assert.equal(result.loader.reachable, true);
assert.equal(result.loader.port, 9021);
});
test('runtime blocks hash drift and never offers the loader action', async t => {
const runtime = fixture(t, { expectedPayloadSha256: '0'.repeat(64) });
const result = await runtime.preflight();
assert.equal(result.ready, false);
assert.equal(result.payload.reason, 'sha256-mismatch');
await assert.rejects(() => runtime.startPayload(), error => error.code === 'PS5UPLOAD_PREFLIGHT_BLOCKED');
});
test('engine start uses a fixed executable without shell and keeps stdin open', async t => {
const calls = [];
const child = new EventEmitter();
child.stdin = { destroyed: false, end() { this.destroyed = true; child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async () => { throw new Error('not listening yet'); },
spawn: (file, args, options) => { calls.push({ file, args, options }); return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
});
const result = await runtime.startEngine();
assert.equal(result.available, true);
assert.equal(calls.length, 1);
assert.deepEqual(calls[0].args, []);
assert.equal(calls[0].options.shell, false);
assert.equal(calls[0].options.env.PS5_ADDR, '192.0.2.55:9113');
assert.equal(calls[0].options.env.PS5UPLOAD_ENGINE_PORT, '19113');
await runtime.stopEngine();
assert.equal(child.stdin.destroyed, true);
});
test('Linux engine start goes through the fixed parent-death process guard', async t => {
const calls = [];
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let running = false;
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() { running = false; child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async url => {
if (!running || !String(url).endsWith('/api/version')) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
},
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: (file, args, options) => { calls.push({ file, args, options }); running = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
enginePortProbe: async () => running,
});
await runtime.startEngine();
assert.equal(calls.length, 1);
assert.equal(calls[0].file, guardPath);
assert.equal(calls[0].args[0], '--pdeathsig-exec');
assert.equal(path.basename(calls[0].args[1]), 'ps5upload-engine');
assert.equal(calls[0].options.shell, false);
await runtime.stopEngine();
});
test('Linux start rejects a matching endpoint unless its listener PID is the spawned child', async t => {
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let running = false;
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() { running = false; child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async url => {
if (!running || !String(url).endsWith('/api/version')) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
},
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: () => { running = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
inspectExistingEngine: async input => listenerIdentity(input, { pid: 5252 }),
enginePortProbe: async () => running,
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_START_FAILED'
&& error.details?.reason === 'spawned-listener-unproven'
&& error.details?.cleanup?.stopped === true,
);
});
test('Linux spawned fallback revalidates and terminates through pidfd without numeric child kill', async t => {
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let running = false;
let childKills = 0;
const terminations = [];
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() {} };
child.stderr = new EventEmitter();
child.kill = () => { childKills += 1; return true; };
const runtime = fixture(t, {
fetch: async url => {
if (!running || !String(url).endsWith('/api/version')) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
},
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: () => { running = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
inspectExistingEngine: async input => listenerIdentity(input),
terminateProvenProcess: async identity => {
terminations.push(identity);
running = false;
return { terminated: true, identityGone: true, forced: false };
},
enginePortProbe: async () => running,
});
await runtime.startEngine();
assert.deepEqual(await runtime.stopEngine(), {
stopped: true,
endpointReleased: true,
identityGone: true,
forced: false,
});
assert.equal(terminations.length, 1);
assert.equal(terminations[0].pid, 4242);
assert.equal(childKills, 0);
});
test('Linux spawned ownership can be re-attested after a transient read-only sample drift', async t => {
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let running = false;
let inspections = 0;
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() { running = false; child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async url => {
if (!running || !String(url).endsWith('/api/version')) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
},
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: () => { running = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
inspectExistingEngine: async input => {
inspections += 1;
return listenerIdentity(input, { pid: inspections === 3 ? 5252 : 4242 });
},
enginePortProbe: async () => running,
});
await runtime.startEngine();
assert.equal((await runtime.status()).engine.processOwned, false);
const recovered = await runtime.status();
assert.equal(recovered.engine.processOwned, true);
assert.equal(recovered.engine.processOwnership, 'spawned');
await runtime.stopEngine();
});
test('engine stop waits for the shared endpoint to be released before allowing a replacement runtime', async t => {
let stopping = false;
let started = false;
let portProbes = 0;
let waits = 0;
const child = new EventEmitter();
child.stdin = { end() { stopping = true; child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async url => {
if (!String(url).endsWith('/api/version') || !stopping) throw new Error('not listening');
throw new Error('HTTP endpoint released');
},
spawn: () => { started = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
enginePortProbe: async () => { if (!started) return false; portProbes += 1; return portProbes < 3; },
delay: async () => { waits += 1; },
});
await runtime.startEngine();
const stopped = await runtime.stopEngine();
assert.deepEqual(stopped, { stopped: true, endpointReleased: true, identityGone: true, forced: false });
assert.equal(portProbes, 3);
assert.equal(waits, 2);
});
test('not-owned stop reports success only after proving the shared endpoint is already released', async t => {
const released = fixture(t, {
fetch: async () => { throw new Error('endpoint unavailable'); },
enginePortProbe: async () => false,
});
assert.deepEqual(await released.stopEngine(), {
stopped: true,
endpointReleased: true,
identityGone: true,
alreadyStopped: true,
});
const occupied = fixture(t, { enginePortProbe: async () => true });
assert.deepEqual(await occupied.stopEngine(), {
stopped: false,
endpointReleased: false,
identityGone: false,
reason: 'not-owned',
});
});
test('engine stop fails closed without signaling again when a replacement endpoint remains occupied', async t => {
let kills = 0;
let portProbes = 0;
let started = false;
const child = new EventEmitter();
child.stdin = { end() { child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => { kills += 1; return true; };
const runtime = fixture(t, {
fetch: async () => { throw new Error('HTTP endpoint unavailable'); },
spawn: () => { started = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
enginePortProbe: async () => { if (!started) return false; portProbes += 1; return true; },
delay: async () => {},
});
await runtime.startEngine();
const stopped = await runtime.stopEngine();
assert.deepEqual(stopped, { stopped: false, endpointReleased: false, identityGone: true, reason: 'engine-endpoint-still-active' });
assert.equal(portProbes, 20);
assert.equal(kills, 0);
});
test('failed spawned stop retains the child identity so a later safe retry can finish', async t => {
let allowExit = false;
const child = new EventEmitter();
child.stdin = { end() { if (allowExit) child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => { if (allowExit) child.emit('exit', 0, 'SIGTERM'); return true; };
const runtime = fixture(t, {
fetch: async () => { throw new Error('endpoint unavailable'); },
spawn: () => child,
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
enginePortProbe: async () => false,
});
await runtime.startEngine();
assert.deepEqual(await runtime.stopEngine(), {
stopped: false,
endpointReleased: false,
identityGone: false,
reason: 'engine-process-still-active',
});
allowExit = true;
assert.deepEqual(await runtime.stopEngine(), {
stopped: true,
endpointReleased: true,
identityGone: true,
forced: false,
});
});
test('failed engine readiness performs the same proven bounded cleanup', async t => {
const child = new EventEmitter();
child.stdin = { end() { child.emit('exit', 0, null); } };
child.stderr = new EventEmitter();
child.kill = () => true;
const runtime = fixture(t, {
fetch: async () => { throw new Error('endpoint unavailable'); },
spawn: () => child,
waitForEngine: async () => ({ available: false, version: '', reason: 'unreachable' }),
enginePortProbe: async () => false,
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_START_FAILED'
&& error.details?.cleanup?.stopped === true
&& error.details.cleanup.endpointReleased === true
&& error.details.cleanup.identityGone === true,
);
});
test('Linux failed readiness re-attests the spawned listener before proven pidfd cleanup', async t => {
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let running = false;
let childKills = 0;
const terminations = [];
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() {} };
child.stderr = new EventEmitter();
child.kill = () => { childKills += 1; return true; };
const runtime = fixture(t, {
fetch: async url => {
if (!running || !String(url).endsWith('/api/version')) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: 'wrong-profile' }) };
},
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: () => { running = true; return child; },
waitForEngine: async () => ({ available: false, version: 'wrong-profile', reason: 'runtime-profile-mismatch' }),
inspectExistingEngine: async input => listenerIdentity(input),
inspectSpawnedProcess: async input => processIdentity(input),
terminateProvenProcess: async identity => {
terminations.push(identity);
running = false;
return { terminated: true, identityGone: true, forced: false };
},
enginePortProbe: async () => running,
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_START_FAILED'
&& error.details?.cleanup?.stopped === true
&& error.details.cleanup.endpointReleased === true
&& error.details.cleanup.identityGone === true,
);
assert.equal(terminations.length, 1);
assert.equal(terminations[0].pid, 4242);
assert.equal(childKills, 0);
});
test('Linux failed readiness terminates an exact spawned PID that never opens a listener', async t => {
const guardPath = path.join(os.tmpdir(), 'chimera-process-guard-test');
let childKills = 0;
const terminations = [];
const child = new EventEmitter();
child.pid = 4242;
child.stdin = { end() {} };
child.stderr = new EventEmitter();
child.kill = () => { childKills += 1; return true; };
const runtime = fixture(t, {
fetch: async () => { throw new Error('listener never opened'); },
processPlatform: 'linux',
processGuardPath: guardPath,
verifyProcessGuard: () => ({ verified: true, path: guardPath }),
spawn: () => child,
waitForEngine: async () => ({ available: false, version: '', reason: 'unreachable' }),
inspectExistingEngine: async () => ({ verified: false, reason: 'listener-not-found' }),
inspectSpawnedProcess: async input => processIdentity(input),
terminateProvenProcess: async identity => {
terminations.push(identity);
return { terminated: true, identityGone: true, forced: false };
},
enginePortProbe: async () => false,
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_START_FAILED'
&& error.details?.cleanup?.stopped === true
&& error.details.cleanup.endpointReleased === true
&& error.details.cleanup.identityGone === true,
);
assert.equal(terminations.length, 1);
assert.equal(terminations[0].pid, 4242);
assert.equal(terminations[0].evidence, LINUX_PROCESS_EVIDENCE);
assert.equal(childKills, 0);
});
test('payload start sends only the pinned ELF and reports management readiness without user data', async t => {
const sends = [];
const inspections = [];
let spawnCalls = 0;
const runtime = fixture(t, {
inspectExistingEngine: async input => { inspections.push(input); return listenerIdentity(input); },
spawn: () => { spawnCalls += 1; throw new Error('must adopt before payload start'); },
tcpSend: async input => { sends.push(input); return { ok: true, bytes: input.bytes.length }; },
});
const result = await runtime.startPayload();
const status = await runtime.status();
assert.equal(sends.length, 1);
assert.equal(spawnCalls, 0);
assert.equal(inspections.length >= 4, true);
assert.equal(inspections.every(input => input.host === '127.0.0.1' && input.port === 19113), true);
assert.equal(status.engine.processOwned, true);
assert.equal(status.engine.processOwnership, 'adopted');
assert.equal(sends[0].host, '192.0.2.55');
assert.equal(sends[0].port, 9021);
assert.equal(sends[0].bytes.toString(), 'elf-5.4.19');
assert.deepEqual(result, { sent: true, bytes: 10, managementReady: true, managementPort: 9114 });
});
test('payload start ignores a stale ready daemon until the new management instance is observable', async t => {
let sent = false;
let postSendStatusReads = 0;
const runtime = fixture(t, {
fetch: async url => {
const href = String(url);
if (href.endsWith('/api/version')) return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
if (href.includes('/api/ps5/status')) {
if (sent) postSendStatusReads += 1;
const transitioned = sent && postSendStatusReads >= 3;
return { ok: true, status: 200, json: async () => ({ version: '5.4.19', instance_id: transitioned ? '2' : '1', started_at_unix: transitioned ? 2 : 1 }) };
}
return { ok: true, status: 200, json: async () => ({ ok: true, users: [] }) };
},
tcpSend: async input => { sent = true; return { ok: true, bytes: input.bytes.length }; },
});
const result = await runtime.startPayload();
assert.equal(result.managementReady, true);
assert.equal(postSendStatusReads, 3);
const identity = await runtime.runtimeIdentity();
assert.equal(identity.status, 'unproven');
assert.equal(identity.payload.evidence, 'loader-input-hash-and-instance-transition');
});
test('payload start does not attest or report readiness when the old instance never transitions', async t => {
let statusReads = 0;
const runtime = fixture(t, {
fetch: async url => {
const href = String(url);
if (href.endsWith('/api/version')) return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
if (href.includes('/api/ps5/status')) {
statusReads += 1;
return { ok: true, status: 200, json: async () => ({ version: '5.4.19', instance_id: '1', started_at_unix: 1 }) };
}
return { ok: true, status: 200, json: async () => ({ ok: true, users: [] }) };
},
});
const result = await runtime.startPayload();
assert.equal(result.managementReady, false);
assert.equal(statusReads, 13);
assert.equal((await runtime.runtimeIdentity()).status, 'unproven');
});
test('DPI fallback sends only the pinned standalone daemon and waits for port 9040', async t => {
const probes = []; const sends = [];
const runtime = fixture(t, {
tcpProbe: async input => { probes.push(input); return input.port === 9021 || probes.filter(item => item.port === 9040).length > 1; },
tcpSend: async input => { sends.push(input); return { ok: true, bytes: input.bytes.length }; },
});
const result = await runtime.ensureDpi();
assert.equal(result.ready, true);
assert.equal(result.port, 9040);
assert.equal(result.alreadyRunning, false);
assert.equal(sends.length, 1);
assert.equal(sends[0].port, 9021);
assert.equal(sends[0].bytes.toString(), 'dpi-5.4.19');
assert.equal(probes.some(item => item.port === 9040), true);
});
test('missing console host blocks engine and payload setup', async t => {
const runtime = fixture(t, { consoleHost: () => '' });
await assert.rejects(() => runtime.startEngine(), error => error.code === 'PS5UPLOAD_CONSOLE_HOST');
const result = await runtime.preflight();
assert.equal(result.ready, false);
assert.equal(result.console.reason, 'host-missing');
});
test('A5 runtime identity stays unproven until Chimera owns the exact engine and payload transition', async t => {
const child = new EventEmitter();
child.stdin = { end() {} }; child.stderr = new EventEmitter(); child.kill = () => true;
let engineReady = false; let payloadLoaded = false;
const runtime = fixture(t, {
fetch: async url => {
const href = String(url);
if (href.endsWith('/api/version')) {
if (!engineReady) throw new Error('not listening');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
}
if (href.includes('/api/ps5/status')) {
return { ok: true, status: 200, json: async () => ({ version: '5.4.19', instance_id: payloadLoaded ? '2' : '1', started_at_unix: payloadLoaded ? 2 : 1 }) };
}
return { ok: true, status: 200, json: async () => ({ ok: true, users: [] }) };
},
spawn: () => { engineReady = true; return child; },
waitForEngine: async () => ({ available: true, version: '5.4.19' }),
tcpProbe: async input => input.port !== 9040,
tcpSend: async input => { payloadLoaded = true; return { ok: true, bytes: input.bytes.length }; },
});
assert.equal((await runtime.runtimeIdentity()).status, 'unproven');
await runtime.startEngine();
assert.equal((await runtime.runtimeIdentity()).status, 'unproven');
await runtime.startPayload();
const exact = await runtime.runtimeIdentity();
assert.equal(exact.status, 'exact');
assert.equal(exact.mutationAllowed, true);
assert.equal(exact.engine.evidence, 'chimera-owned-process-artifact');
assert.equal(exact.payload.evidence, 'loader-input-hash-and-instance-transition');
});
test('an exact same-namespace listener can be re-attested after a server restart', async t => {
let spawnCalls = 0;
const runtime = fixture(t, {
spawn: () => { spawnCalls += 1; throw new Error('must not spawn over an exact existing listener'); },
tcpProbe: async input => input.port !== 9040,
inspectExistingEngine: async input => listenerIdentity(input),
});
const adopted = await runtime.startEngine();
assert.equal(adopted.available, true);
assert.equal(adopted.processOwned, true);
assert.equal(adopted.adopted, true);
assert.equal(spawnCalls, 0);
const beforePayload = await runtime.runtimeIdentity();
assert.equal(beforePayload.status, 'unproven');
assert.equal(beforePayload.engine.evidence, LINUX_LISTENER_PROCESS_EVIDENCE);
await runtime.startPayload();
const exact = await runtime.runtimeIdentity();
assert.equal(exact.status, 'exact');
assert.equal(exact.mutationAllowed, true);
assert.equal(exact.engine.evidence, LINUX_LISTENER_PROCESS_EVIDENCE);
});
test('an existing endpoint is never adopted without exact local process provenance', async t => {
let spawnCalls = 0;
const runtime = fixture(t, {
spawn: () => { spawnCalls += 1; throw new Error('must not spawn over an unowned endpoint'); },
inspectExistingEngine: async () => ({ verified: false, reason: 'listener-owner-not-found' }),
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_UNOWNED'
&& error.details?.reason === 'listener-owner-not-found',
);
assert.equal(spawnCalls, 0);
assert.equal((await runtime.runtimeIdentity()).status, 'unproven');
});
test('a reachable wrong-lane profile is never inspected, signaled or spawned over', async t => {
let inspections = 0;
let spawnCalls = 0;
let terminations = 0;
const runtime = fixture(t, {
fetch: async url => {
if (String(url).endsWith('/api/version')) {
return {
ok: true,
status: 200,
json: async () => ({ version: '5.4.19', diagnostic_identity: 'chimera-p15-shellui-observe-v1' }),
};
}
throw new Error('unexpected request');
},
inspectExistingEngine: async input => { inspections += 1; return listenerIdentity(input); },
spawn: () => { spawnCalls += 1; throw new Error('must not spawn over wrong lane'); },
terminateProvenProcess: async () => { terminations += 1; return { terminated: true, identityGone: true }; },
});
await assert.rejects(
() => runtime.startEngine(),
error => error.code === 'PS5UPLOAD_ENGINE_UNOWNED'
&& error.details?.reason === 'runtime-profile-mismatch',
);
assert.equal(inspections, 0);
assert.equal(spawnCalls, 0);
assert.equal(terminations, 0);
});
test('an adopted engine is revalidated before a bounded graceful stop', async t => {
let terminated = false;
const terminations = [];
const runtime = fixture(t, {
fetch: async url => {
const href = String(url);
if (href.endsWith('/api/version')) {
if (terminated) throw new Error('released');
return { ok: true, status: 200, json: async () => ({ version: '5.4.19' }) };
}
if (href.includes('/api/ps5/status')) return { ok: true, status: 200, json: async () => ({ version: '5.4.19', instance_id: '1', started_at_unix: 1 }) };
return { ok: true, status: 200, json: async () => ({ ok: true, users: [] }) };
},
inspectExistingEngine: async input => listenerIdentity(input),
terminateProvenProcess: async identity => {
terminations.push(identity);
terminated = true;
return { terminated: true, identityGone: true, forced: false };
},
enginePortProbe: async () => !terminated,
});
await runtime.startEngine();
const stopped = await runtime.stopEngine();
assert.deepEqual(stopped, { stopped: true, endpointReleased: true, identityGone: true, forced: false, adopted: true });
assert.equal(terminations.length, 1);
assert.equal(terminations[0].pid, 4242);
});
test('adopted process identity drift fails closed without signaling any process', async t => {
let inspections = 0;
const terminations = [];
const runtime = fixture(t, {
inspectExistingEngine: async input => {
inspections += 1;
return listenerIdentity(input, { pid: inspections <= 2 ? 4242 : 5252 });
},
terminateProvenProcess: async identity => { terminations.push(identity); return { terminated: true, identityGone: true }; },
});
await runtime.startEngine();
const stopped = await runtime.stopEngine();
assert.deepEqual(stopped, { stopped: false, endpointReleased: false, identityGone: false, reason: 'adopted-process-identity-drift' });
assert.deepEqual(terminations, []);
});
test('status clears adopted ownership and attestation immediately on identity drift', async t => {
let inspections = 0;
const runtime = fixture(t, {
inspectExistingEngine: async input => {
inspections += 1;
return listenerIdentity(input, { startTimeTicks: inspections <= 2 ? '123456' : '654321' });
},
});
await runtime.startEngine();
const status = await runtime.status();
assert.equal(status.engine.available, true);
assert.equal(status.engine.processOwned, false);
assert.equal(status.engine.processOwnership, null);
const identity = await runtime.runtimeIdentity();
assert.equal(identity.status, 'unproven');
assert.equal(identity.mutationAllowed, false);
});
test('adopted helper failure remains fail-closed and keeps ownership available for retry', async t => {
let terminations = 0;
const runtime = fixture(t, {
terminateProvenProcess: async () => {
terminations += 1;
return { terminated: false, identityGone: false, reason: 'pidfd-exit-timeout' };
},
});
await runtime.startEngine();
const stopped = await runtime.stopEngine();
assert.deepEqual(stopped, {
stopped: false,
endpointReleased: false,
identityGone: false,
reason: 'pidfd-exit-timeout',
});
assert.equal(terminations, 1);
assert.equal((await runtime.status()).engine.processOwned, true);
});