Rocko's round 2 candidate, approved by Darkwing (#1519 comment 26757).
build.patch 40d7e838, manifest 61519059, 24 files under packages/bus,
schema v3b (179ffe35, lead decision 60). Integration gate in a git
worktree of 942dca9e (S1 in the tree) plus the patch: bus 43/43 and
business 60/60 on Node 24 and 26, every package test and every
scripts/test-*.sh green, test-task 98/98 with the live-provider cases.
Rulings from lead decisions 62 and 63: the human proof is cooperative in
slice 1, and a self-raised cross-role decision routes to the human.
Single-use gated approvals follow in row 43.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
194 lines
8.1 KiB
JavaScript
194 lines
8.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { fork } from 'node:child_process';
|
|
import { mkdtempSync, rmSync, existsSync, writeFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { once } from 'node:events';
|
|
import { Client } from '../src/client.mjs';
|
|
const businesses = {
|
|
demo: {
|
|
id: 'demo',
|
|
human: 'jason',
|
|
arbiters: { technical: 'cto', delivery: 'cto' },
|
|
roles: { cto: { authority: { withinRole: ['message.send'], crossRole: [] } } },
|
|
},
|
|
};
|
|
function launch(t) {
|
|
const child = fork(new URL('../src/process.mjs', import.meta.url), [], {
|
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
});
|
|
let logs = '';
|
|
child.stdout.on('data', (b) => (logs += b));
|
|
child.stderr.on('data', (b) => (logs += b));
|
|
t.after(() => {
|
|
if (child.exitCode === null) child.kill('SIGKILL');
|
|
});
|
|
return { child, logs: () => logs };
|
|
}
|
|
async function boot(child, config) {
|
|
const reply = Promise.race([
|
|
once(child, 'message'),
|
|
once(child, 'exit').then(() => {
|
|
throw Error('exited-before-reply');
|
|
}),
|
|
]);
|
|
child.send({ op: 'boot', config });
|
|
return (await reply)[0];
|
|
}
|
|
test('broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-process-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const { child } = launch(t);
|
|
const ready = await boot(child, {
|
|
dataRoot: root,
|
|
businesses,
|
|
launches: [{ business: 'demo', role: 'cto', run: 'r1', harness: 'pi', pid: process.pid, startTime: '1' }],
|
|
readers: ['demo'],
|
|
});
|
|
assert.equal(ready.ok, true);
|
|
const agent = new Client({ path: ready.path, cap: ready.launches[0].cap });
|
|
await agent.call('role.claim');
|
|
const reader = new Client({ path: ready.path, cap: ready.readers[0].cap });
|
|
assert.equal((await reader.call('agents'))[0].holder_run, 'r1');
|
|
await assert.rejects(reader.call('launch.revoke'), /read-only/);
|
|
await assert.rejects(
|
|
new Client({
|
|
path: ready.path,
|
|
human: { business: 'demo', pid: process.pid, startTime: '1', nonce: 'f'.repeat(64) },
|
|
}).call('launch.revoke'),
|
|
/human-required/,
|
|
);
|
|
const exit = once(child, 'exit');
|
|
child.send({ op: 'close' });
|
|
assert.equal((await exit)[0], 0);
|
|
assert.equal(existsSync(join(root, 'bus/writer.lock')), false);
|
|
});
|
|
test('startup token refusal returns safe code without value or partial listening broker', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-boot-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const data = join(root, 'data');
|
|
mkdirSync(data);
|
|
const secret = 'fixture-token-never-in-db-948723';
|
|
const file = join(root, 'secret');
|
|
writeFileSync(file, secret, { mode: 0o644 });
|
|
const config = structuredClone(businesses);
|
|
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
|
|
const { child, logs } = launch(t);
|
|
const exit = once(child, 'exit');
|
|
const ready = await boot(child, { dataRoot: data, businesses: config, launches: [], readers: [] });
|
|
assert.equal(ready.ok, false);
|
|
assert.equal(ready.error, 'credential-file');
|
|
assert.equal((await exit)[0], 2);
|
|
assert.ok(!logs().includes(secret));
|
|
assert.equal(existsSync(join(data, 'bus/broker.sock')), false);
|
|
});
|
|
test('loaded fixture token is absent from socket replies and SQLite, including refusal evidence', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-secret-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const data = join(root, 'data');
|
|
mkdirSync(data);
|
|
const token = 'fixture-opaque-token-e9c39140';
|
|
const file = join(root, 'token');
|
|
writeFileSync(file, token, { mode: 0o600 });
|
|
const config = structuredClone(businesses);
|
|
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
|
|
const { child, logs } = launch(t);
|
|
const ready = await boot(child, {
|
|
dataRoot: data,
|
|
businesses: config,
|
|
launches: [{ business: 'demo', role: 'cto', run: 'r2', harness: 'pi', pid: process.pid, startTime: '1' }],
|
|
});
|
|
assert.equal(ready.ok, true);
|
|
const c = new Client({ path: ready.path, cap: ready.launches[0].cap });
|
|
await c.call('role.claim');
|
|
await assert.rejects(c.call('message.send', { to: 'cto', body: token }), /credential-leak/);
|
|
const exit = once(child, 'exit');
|
|
child.send({ op: 'close' });
|
|
await exit;
|
|
assert.ok(!readFileSync(join(data, 'bus/bus.sqlite')).includes(Buffer.from(token)));
|
|
assert.ok(!logs().includes(token));
|
|
});
|
|
test('killed broker leaves an explicit stale lock; another process cannot silently reclaim it', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-crash-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const first = launch(t);
|
|
const config = { dataRoot: root, businesses, launches: [] };
|
|
assert.equal((await boot(first.child, config)).ok, true);
|
|
const killed = once(first.child, 'exit');
|
|
first.child.kill('SIGKILL');
|
|
await killed;
|
|
assert.equal(existsSync(join(root, 'bus/writer.lock')), true);
|
|
const second = launch(t),
|
|
ended = once(second.child, 'exit');
|
|
const refusal = await boot(second.child, config);
|
|
assert.equal(refusal.ok, false);
|
|
assert.equal((await ended)[0], 2);
|
|
assert.equal(existsSync(join(root, 'bus/writer.lock')), true);
|
|
});
|
|
test('trusted host registers later launches; socket clients never have a registration verb', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-add-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const { child } = launch(t);
|
|
const ready = await boot(child, { dataRoot: root, businesses, launches: [] });
|
|
assert.equal(ready.ok, true);
|
|
const message = once(child, 'message');
|
|
child.send({
|
|
op: 'bindLaunch',
|
|
record: { business: 'demo', role: 'cto', run: 'later', harness: 'pi', pid: process.pid, startTime: '1' },
|
|
});
|
|
const bound = (await message)[0];
|
|
assert.equal(bound.ok, true);
|
|
const c = new Client({ path: ready.path, cap: bound.launch.cap });
|
|
await c.call('role.claim');
|
|
await assert.rejects(c.call('bindLaunch', { role: 'cto' }), /unknown-verb/);
|
|
const exit = once(child, 'exit');
|
|
child.send({ op: 'close' });
|
|
assert.equal((await exit)[0], 0);
|
|
});
|
|
test('runtime excludes declared project roots even when host supplies no repoRoots', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-project-token-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const data = join(root, 'data');
|
|
mkdirSync(data);
|
|
const project = join(root, 'project');
|
|
mkdirSync(project);
|
|
const file = join(project, 'token');
|
|
writeFileSync(file, 'fixture-do-not-load-from-project', { mode: 0o600 });
|
|
const config = structuredClone(businesses);
|
|
config.demo.projects = { stack: { root: project } };
|
|
config.demo.roles.cto.credentials = { gitea: { file, rotateBy: '2099-01-01' } };
|
|
const { child } = launch(t),
|
|
exit = once(child, 'exit');
|
|
const result = await boot(child, { dataRoot: data, businesses: config });
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.error, 'credential-location');
|
|
assert.equal((await exit)[0], 2);
|
|
});
|
|
test('a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it', async (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-bind-refusal-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const { child } = launch(t);
|
|
const record = {
|
|
business: 'demo',
|
|
role: 'cto',
|
|
run: 'one',
|
|
harness: 'pi',
|
|
pid: process.pid,
|
|
startTime: '1',
|
|
};
|
|
const ready = await boot(child, { dataRoot: root, businesses, launches: [record] });
|
|
assert.equal(ready.ok, true);
|
|
const client = new Client({ path: ready.path, cap: ready.launches[0].cap });
|
|
await client.call('role.claim');
|
|
const reply = once(child, 'message');
|
|
child.send({ op: 'bindLaunch', record });
|
|
assert.deepEqual((await reply)[0], { ok: false, error: 'duplicate-run' });
|
|
assert.equal((await client.call('agents'))[0].holder_run, 'one');
|
|
const bad = once(child, 'message'),
|
|
exit = once(child, 'exit');
|
|
child.send({ op: 'not-a-protocol-verb' });
|
|
assert.equal((await bad)[0].ok, false);
|
|
assert.equal((await exit)[0], 2);
|
|
});
|