import {materializeCopies,withCopies} from '../../organization/src/agent-copies.mjs'; import {loadRootAgents} from '../../organization/src/agent-levels.mjs'; import { approveTemplateHire, ownerConsole } from './hire-fixtures.mjs'; // P2: real private Credentials, fake loopback management/model server. No live config. import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import http from 'node:http'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { createHash, randomBytes } from 'node:crypto'; import { Credentials } from '../src/credentials.mjs'; import { ManagementClient } from '../../model-proxy/src/client.mjs'; import { ModelKeys, REVOKE_TIMEOUT_MS, REVOKE_POLL_MS } from '../src/model-keys.mjs'; import { startBroker } from '../src/runtime.mjs'; import { busOrganization } from '../src/organization-context.mjs'; import { Registry } from '../../seat/src/session.mjs'; import { loadBusiness } from '../../organization/src/organization-config.mjs'; import { resolveInstance } from '../../organization/src/resolve.mjs'; import { businessDoc, rolesCopy, systemFor, writeJson } from '../../organization/tests/helpers.mjs'; const hash = s => createHash('sha256').update(s).digest('hex').slice(0, 12); const failCode = code => e => e.code === code; async function fixture(t, { delayed = 0 } = {}) { const root = fs.mkdtempSync(join(tmpdir(), 'p2-keys-')), configDir = join(root, 'config'); const storeRoot = join(configDir, 'secrets', 'agents'); fs.mkdirSync(storeRoot, { recursive: true, mode: 0o700 }); const prefix = `msk-${hash(configDir)}-`, makeKey = () => prefix + randomBytes(32).toString('base64url'); const sentinel = prefix + 'sentinel-' + randomBytes(32).toString('base64url'); const state = { keys: [sentinel], calls: [], delay: delayed, removed: new Map(), failGet: false, failPut: false, hideConfirm: false, probes: 0, status: null }; const server = http.createServer(async (req, res) => { const management = req.url === '/v8/management/config/access/api-keys'; state.calls.push(management ? req.method : 'PROBE'); if (management) { if (req.headers.authorization !== 'Bearer fx331-manage') { res.writeHead(401); res.end('{}'); return; } if ((req.method === 'GET' && state.failGet) || (req.method === 'PUT' && state.failPut)) { res.writeHead(503); res.end('{}'); return; } if (req.method === 'GET') { res.end(JSON.stringify(state.hideConfirm ? [sentinel] : state.keys)); return; } const chunks = []; for await (const c of req) chunks.push(c); const next = JSON.parse(Buffer.concat(chunks)); for (const k of state.keys.filter(k => !next.includes(k))) state.removed.set(k, performance.now()); state.keys = next; res.end('{"status":"ok"}'); return; } state.probes++; const key = req.headers.authorization?.slice(7); const accepted = state.keys.includes(key) || (state.removed.has(key) && performance.now() - state.removed.get(key) < state.delay); state.onProbe?.(state.status ?? (accepted ? 200 : 401)); res.writeHead(state.status ?? (accepted ? 200 : 401)); res.end('{}'); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const baseUrl = `http://127.0.0.1:${server.address().port}`, receipts = [], stopped = [], runs = []; let now = Date.parse('2026-10-24T00:00:00.000Z'); const credentials = new Credentials({ storeRoot, dataRoot: join(root, 'data'), modelAgents: ['demo/coder', 'demo/other'] }); const client = new ManagementClient({ baseUrl, key: 'fx331-manage', receipt: r => receipts.push(r) }); const create = () => new ModelKeys({ credentials, client, configDir, baseUrl, receipt: r => receipts.push(r), now: () => now, runs: instance => runs.filter(r => r.instance === instance), stopRuns: async rs => { stopped.push(...rs.map(r => r.run)); for (const r of rs) runs.splice(runs.indexOf(r), 1); } }); const manager = create(); const closers = []; t.after(async () => { for (const close of closers) await close(); credentials.close(); server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); fs.rmSync(root, { recursive: true, force: true }); }); return { root, configDir, credentials, client, server, closers, state, manager, create, makeKey, sentinel, receipts, baseUrl, stopped, runs, advance: ms => { now += ms; } }; } test('issue confirms GET/PUT/GET before private file and returns P3 shape with only own key', async t => { const f = await fixture(t); await f.manager.reconcile(); f.state.calls.length = 0; const answer = await f.manager.ensure('demo/coder'); assert.deepEqual(Object.keys(answer).sort(), ['baseUrl', 'key']); assert.equal(answer.baseUrl, f.baseUrl); assert.deepEqual(f.state.calls, ['GET', 'PUT', 'GET']); assert.equal(f.state.keys.includes(answer.key), true); assert.equal(f.credentials.status().find(s => s.instance === 'demo/coder').date, '2026-11-23'); assert.equal(JSON.stringify(f.receipts).includes(answer.key), false); const other = await f.manager.ensure('demo/other'); assert.equal(other.key === answer.key, false); }); for (const fault of ['failGet', 'failPut', 'hideConfirm']) test(`proxy-key-issue-failed: ${fault} writes no model record`, async t => { const f = await fixture(t); await f.manager.reconcile(); f.state[fault] = true; await assert.rejects(f.manager.ensure('demo/coder'), failCode('proxy-key-issue-failed')); assert.equal(f.credentials.modelRecord('demo/coder') === null, true); assert.equal(fs.existsSync(join(f.configDir, 'secrets', 'agents', 'demo', 'coder', 'model')), false); }); test('credential-revoke-failed: missing confirming GET keeps model file and revoking status', async t => { const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); f.state.failGet = true; await assert.rejects(f.manager.prepareRevoke('demo/coder'), failCode('credential-revoke-failed')); assert.equal(f.credentials.status().find(s => s.instance === 'demo/coder').state, 'revoking'); }); test('revoke confirms absence then polls delayed client refusal before local removal', async t => { const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder'); f.state.calls.length = 0; const before = performance.now(); await f.manager.prepareRevoke('demo/coder'); assert.ok(performance.now() - before >= 100); assert.deepEqual(f.state.calls.slice(0, 3), ['GET', 'PUT', 'GET']); assert.ok(f.state.probes >= 2); assert.equal(f.state.keys.includes(key), false); assert.equal(f.credentials.modelRecord('demo/coder')?.state, 'revoking'); f.manager.completeRevoke('demo/coder'); assert.equal(f.credentials.modelRecord('demo/coder') === null, true); assert.equal(JSON.stringify(f.receipts).includes(key), false); }); test('reconcile restores missing recorded keys, removes own unknown, and preserves foreign and sentinel', async t => { const f = await fixture(t); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder'); const unknown = f.makeKey(), foreign = 'fx331-human'; f.state.keys = [f.sentinel, unknown, foreign]; f.state.calls.length = 0; await f.create().reconcile(); assert.equal(f.state.keys.includes(key), true); assert.equal(f.state.keys.includes(unknown), false); assert.equal(f.state.keys.includes(foreign), true); assert.equal(f.state.keys.includes(f.sentinel), true); assert.deepEqual(f.state.calls.slice(0, 3), ['GET', 'PUT', 'GET']); }); test('reconcile revoking never restores key and completes delayed revocation after restart', async t => { const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder'); const record = f.credentials.modelRecord('demo/coder'); f.credentials.writeModel('demo/coder', { ...record, state: 'revoking', revokingAt: '2026-10-24T00:00:00.000Z' }); await f.create().reconcile(); assert.equal(f.state.keys.includes(key), false); assert.equal(f.credentials.modelRecord('demo/coder') === null, true); }); test('proxy-open: no own sentinel blocks reconcile and every launch', async t => { const f = await fixture(t); f.state.keys = ['fx331-human']; await assert.rejects(f.manager.reconcile(), failCode('proxy-open')); await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-open')); assert.equal(f.state.probes, 0); }); test('proxy-key-revoking: launch hold occurs before any preflight call', async t => { const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); const record = f.credentials.modelRecord('demo/coder'); f.credentials.writeModel('demo/coder', { ...record, state: 'revoking', revokingAt: '2026-10-24T00:00:00.000Z' }); await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-key-revoking')); assert.equal(f.state.probes, 0); }); test('proxy-revoke-revision-conflict keeps local revoking record and ids-only remote-revoked receipt', async t => { const f = await fixture(t); await f.manager.reconcile(); const { key } = await f.manager.ensure('demo/coder'); await f.manager.prepareRevoke('demo/coder'); assert.throws(() => f.manager.revisionConflict('demo/coder'), failCode('proxy-revoke-revision-conflict')); assert.equal(f.credentials.modelRecord('demo/coder').state, 'revoking'); assert.equal(f.state.keys.includes(key), false); assert.ok(f.receipts.some(r => r.remoteState === 'revoked' && r.localState === 'kept')); assert.equal(JSON.stringify(f.receipts).includes(key), false); await f.create().reconcile(); assert.equal(f.credentials.modelRecord('demo/coder') === null, true); }); for (const status of [401, 503, 302]) test(`proxy-key-refused: preflight rejects HTTP ${status} with exactly one probe`, async t => { const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); f.state.status = status; await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-key-refused')); assert.equal(f.state.probes, 1); }); test('rotate keeps old key while its run lives, new launches use new, exit removes old', async t => { const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder'); f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) }); const next = await f.manager.rotate('demo/coder'); assert.equal(next.key === first.key, false); assert.equal(f.state.keys.includes(first.key), true); assert.equal((await f.manager.ensure('demo/coder')).key === next.key, true); await f.manager.sweep(); assert.equal(f.state.keys.includes(first.key), true); f.runs.length = 0; await f.manager.sweep(); assert.equal(f.state.keys.includes(first.key), false); }); test('rotation grace stops surviving old-key runs before remote revocation', async t => { const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder'); f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) }); await f.manager.rotate('demo/coder'); f.advance(120 * 60_000); await f.manager.sweep(); assert.deepEqual(f.stopped, ['old']); assert.equal(f.state.keys.includes(first.key), false); }); test('revocation polling constants retain the approved five-second bound and 25ms spacing', () => { assert.equal(REVOKE_TIMEOUT_MS, 5000); assert.equal(REVOKE_POLL_MS, 25); }); test('credential-revoke-failed: polling cannot treat management absence as client revocation', async t => { const f = await fixture(t, { delayed: Infinity }); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); const start = performance.now(); await assert.rejects(f.manager.prepareRevoke('demo/coder'), failCode('credential-revoke-failed')); const elapsed = performance.now() - start; assert.ok(elapsed >= 4900 && elapsed < 6500, 'five-second total revocation polling bound'); assert.equal(f.credentials.modelRecord('demo/coder').state, 'revoking'); }); test('proxy-down: unreachable listener refuses preflight', async t => { const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); await new Promise(resolve => f.server.close(resolve)); await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-down')); }); test('proxy-stop-failed: old proxy key and retiring record survive a failed grace stop, next sweep retries', async t => { const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder'); f.runs.push({ instance: 'demo/coder', run: 'old', keyId: hash(first.key) }); let stopped = false; const manager = new ModelKeys({ credentials: f.credentials, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl, now: () => Date.parse('2026-10-24T03:00:00.000Z'), runs: () => f.runs, stopRuns: async () => { if (!stopped) throw Error('fx331-stop'); f.runs.length = 0; } }); await manager.reconcile(); await manager.rotate('demo/coder'); // This manager created a fresh 120-minute window at 03:00; use a second view // with time beyond that deadline to exercise failure then retry of the same record. const overdue = new ModelKeys({ credentials: f.credentials, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl, now: () => Date.parse('2026-10-24T06:00:00.000Z'), runs: () => f.runs, stopRuns: async () => { if (!stopped) throw Error('fx331-stop'); f.runs.length = 0; } }); await overdue.reconcile(); await assert.rejects(overdue.sweep(), failCode('proxy-stop-failed')); assert.equal(f.state.keys.includes(first.key), true); assert.equal(f.credentials.modelRecord('demo/coder').retiring.length, 1); stopped = true; await overdue.sweep(); assert.equal(f.state.keys.includes(first.key), false); }); async function bootBusiness(f, {copyLevel = null,...binding} = {}) { const rolesDir = rolesCopy(f.root), system = systemFor(f.root), doc = businessDoc(f.root, 'demo'); // Aide is needed because shipped chief hireable:[] and security-reviewer has review.verdict outside HIRE_ALLOWED. fs.writeFileSync(join(rolesDir, 'aide.md'), '# Aide\n\n## Authority\n'); writeJson(join(rolesDir, 'aide.json'), { roleVersion: 2, name: 'aide', title: 'Aide', contract: 'aide.md', tools: ['read'], network: 'api-only', authority: { withinRole: ['message.send'], crossRole: [] }, credentials: [], }); doc.plugins = { ...doc.plugins, harness: ['pi'] }; doc.roles['sec-1'] = { definition: 'security-reviewer' }; doc.launch.instances.push('sec-1'); const copyEntry={definition:'aide',vars:{harness:'pi',model:'claude-opus-5-5'}}; if(copyLevel==='project')doc.projects.stack.agents={aide:copyEntry}; if(copyLevel==='root')writeJson(join(f.configDir,'agents.json'),{agentsVersion:1,agents:{aide:copyEntry}}); const copies=copyLevel?materializeCopies({orgId:'demo',doc,rolesDir,root:loadRootAgents(f.configDir,{rolesDir})}):null; const file = writeJson(join(f.configDir, 'businesses', 'demo.json'), doc); fs.writeFileSync(join(f.configDir, 'secrets', 'proxy-management.key'), 'fx331-manage', { mode: 0o600 }); const business = copies?withCopies(doc,file,{rolesDir,snapshot:copies}):loadBusiness('demo', { dir: f.configDir, rolesDir }), resolved = {}; for (const instance of Object.keys(business.roles)) resolved[instance] = resolveInstance({ system, business, instance }); fs.mkdirSync(join(f.root, 'data'), { mode: 0o700 }); // Fresh mkdtemp fixture + exclusive mkdir above: no prior session registry can exist. new Registry(join(f.root, 'data')); const bus = await startBroker({ dataRoot: join(f.root, 'data'), businesses: { demo: busOrganization(business, resolved) }, copies, organization: { dir: f.configDir, rolesDir, system }, proxy: { port: Number(new URL(f.baseUrl).port), binary: '/fixture-unused' }, ...binding }); f.closers.push(() => bus.close()); const chief = bus.broker.bindLaunch({ business: 'demo', role: 'chief', run: 'chief-run', harness: 'pi' }); bus.broker.request(chief, { verb: 'role.claim' }); const fire = () => bus.broker.request(ownerConsole(bus.broker), { verb: 'hire.start.console', args: { op:'fire',level:'org',name: 'sec-1', reason: 'fixture fire' } }); const listed = () => JSON.parse(fs.readFileSync(file, 'utf8')).launch.instances.includes('sec-1'); return { bus, chief, fire, listed, file, rolesDir, system }; } test('bus fire proves remote 401 before delisting the agent and deleting model file', async t => { const f = await fixture(t, { delayed: 105 }); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1'); const b = await bootBusiness(f); let sawRefusal = false; f.state.onProbe = status => { assert.equal(b.listed(), true); assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking'); if (status === 401) sawRefusal = true; }; const result = await b.fire(); f.state.onProbe = null; assert.equal(sawRefusal, true); assert.equal(result.delisted, true); assert.equal(b.listed(), false); assert.equal(f.state.keys.includes(issued.key), false); assert.equal(b.bus.credentials.modelRecord('demo/sec-1') === null, true); }); test('credential-revoke-failed: bus fire on failed remote PUT keeps membership and model record', async t => { const f = await fixture(t); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1'); const b = await bootBusiness(f); const before = fs.readFileSync(b.file, 'utf8'); f.state.failPut = true; await assert.rejects(b.fire(), failCode('credential-revoke-failed')); assert.equal(fs.readFileSync(b.file, 'utf8'), before); assert.equal(b.listed(), true); assert.equal(f.state.keys.includes(issued.key), true); assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking'); }); test('proxy-revoke-revision-conflict: racing organization write keeps record, next boot finishes intent', async t => { const f = await fixture(t); await f.manager.reconcile(); const issued = await f.manager.ensure('demo/sec-1'); const b = await bootBusiness(f); let raced = false; f.state.onProbe = status => { if (status === 401 && !raced) { raced = true; fs.appendFileSync(b.file, '\n'); } }; await assert.rejects(b.fire(), failCode('proxy-revoke-revision-conflict')); f.state.onProbe = null; assert.equal(b.listed(), true); assert.equal(f.state.keys.includes(issued.key), false); assert.equal(b.bus.credentials.modelRecord('demo/sec-1')?.state, 'revoking'); await b.bus.close(); f.closers.length = 0; const record = new Credentials({ storeRoot: join(f.configDir, 'secrets', 'agents'), dataRoot: join(f.root, 'data'), modelAgents: ['demo/sec-1'] }); const manager = new ModelKeys({ credentials: record, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl }); await manager.reconcile(); assert.equal(record.modelRecord('demo/sec-1') === null, true); record.close(); }); test('bus hire issues and confirms implicit model key before publishing new membership', async t => { const f = await fixture(t); const b = await bootBusiness(f); const calls = f.state.calls.length; const result = await approveTemplateHire(b.bus.broker,b.rolesDir,{name:'sec-new',definition:'aide'}); assert.equal(typeof result.transaction, 'string'); assert.equal(JSON.parse(fs.readFileSync(b.file, 'utf8')).launch.instances.includes('sec-new'), true); assert.equal(b.bus.credentials.modelRecord('demo/sec-new')?.state, 'valid'); assert.deepEqual(f.state.calls.slice(calls, calls + 3), ['GET', 'PUT', 'GET']); }); test('proxy-key-issue-failed: bus hire failure publishes neither membership nor model file', async t => { const f = await fixture(t); const b = await bootBusiness(f); f.state.failPut = true; const before = fs.readFileSync(b.file, 'utf8'); await assert.rejects(approveTemplateHire(b.bus.broker,b.rolesDir,{name:'sec-new',definition:'aide'}), failCode('proxy-key-issue-failed')); assert.equal(fs.readFileSync(b.file, 'utf8'), before); assert.equal(b.bus.credentials.modelRecord('demo/sec-new') === null, true); }); test('proxy-open: removing sentinel after boot refuses the next launch before models probe', async t => { const f = await fixture(t); await f.manager.reconcile(); await f.manager.ensure('demo/coder'); f.state.keys = f.state.keys.filter(k => k !== f.sentinel); await assert.rejects(f.manager.preflight('demo/coder'), failCode('proxy-open')); assert.equal(f.state.probes, 0); }); test('newly hired role can use runtime preflight without a broker restart', async t => { const f = await fixture(t); const b = await bootBusiness(f); await approveTemplateHire(b.bus.broker,b.rolesDir,{name:'sec-new',definition:'aide'}); const proxy = await b.bus.modelPreflight({ business: 'demo', role: 'sec-new' }); assert.equal(proxy.baseUrl, f.baseUrl); assert.equal(f.state.keys.includes(proxy.key), true); }); test('first launch issues implicit key and performs exactly one successful models probe', async t => { const f = await fixture(t); await f.manager.reconcile(); const answer = await f.manager.preflight('demo/coder'); assert.equal(f.state.probes, 1); assert.equal(f.state.keys.includes(answer.key), true); }); test('steady boot reconcile reads management list once and writes nothing', async t => { const f = await fixture(t); await f.manager.reconcile(); assert.deepEqual(f.state.calls, ['GET']); }); test('thirty-day rotation due automatically replaces and retires an unused old key', async t => { const f = await fixture(t); await f.manager.reconcile(); const first = await f.manager.ensure('demo/coder'); f.advance(30 * 86400_000); await f.manager.sweep(); const current = await f.manager.ensure('demo/coder'); assert.equal(current.key === first.key, false); assert.equal(f.state.keys.includes(first.key), false); assert.equal(f.state.keys.includes(current.key), true); }); test('org registration preserves a running sibling and fences admission on uncertainty', async t => { const f = await fixture(t); const { createProxyLock } = await import('../src/proxy-lock.mjs'); const stateDir = join(f.root, 'proxy'); fs.mkdirSync(stateDir, { mode: 0o700 }); const lock = createProxyLock({ stateDir, identity: f.baseUrl }); const betaRoot = join(f.root, 'beta-secrets'); fs.mkdirSync(betaRoot, { mode: 0o700 }); const betaCredentials = new Credentials({ storeRoot: betaRoot, dataRoot: join(f.root, 'beta-data'), modelAgents: ['demo/other'] }); t.after(() => betaCredentials.close()); const orgManager = (org, credentials) => new ModelKeys({ credentials, client: f.client, configDir: f.configDir, baseUrl: f.baseUrl, org, lock }); const alpha = orgManager('alpha', f.credentials), beta = orgManager('beta', betaCredentials); await alpha.reconcile(); const first = await alpha.ensure('demo/coder'); await beta.reconcile(); assert.equal(await f.client.probeKey(first.key), 200); assert.ok(first.key.startsWith(`mso-${hash(f.configDir)}-alpha-`)); assert.equal(f.state.keys.filter(k => k.startsWith(`mso-${hash(f.configDir)}-beta-sentinel-`)).length, 1); await beta.reconcile(); assert.equal(f.state.keys.filter(k => k.startsWith(`mso-${hash(f.configDir)}-beta-sentinel-`)).length, 1); await lock.run(async () => lock.fence({ org: 'beta', op: 'test' })); assert.throws(() => alpha.assertLaunchable('demo/coder'), failCode('proxy-uncertain')); await assert.rejects(beta.ensure('demo/other'), failCode('proxy-uncertain')); }); test('synthetic trusted org boot registers an org sentinel and issues an org key', async t => { const f = await fixture(t); fs.mkdirSync(join(f.root, 'proxy'), { mode: 0o700 }); const b = await bootBusiness(f, { org: { id: 'demo' }, instanceDataRoot: f.root, instanceConfigDir: f.configDir }); const answer = await b.bus.modelPreflight({ business: 'demo', role: 'sec-1' }); assert.ok(answer.key.startsWith(`mso-${hash(f.configDir)}-demo-`)); assert.equal(f.state.keys.filter(key => key.startsWith(`mso-${hash(f.configDir)}-demo-sentinel-`)).length, 1); const { createProxyLock } = await import('../src/proxy-lock.mjs'); const lock = createProxyLock({ stateDir: join(f.root, 'proxy'), identity: `${f.configDir}:${new URL(f.baseUrl).port}` }); await lock.run(async () => lock.fence({ org: 'demo', op: 'fixture' })); await assert.rejects(b.bus.modelPreflight({ business: 'demo', role: 'sec-1' }), failCode('proxy-uncertain')); }); test('beta bus registers while alpha bus runs and alpha key stays accepted', async t => { const f = await fixture(t); fs.mkdirSync(join(f.root, 'proxy'), { mode: 0o700 }); const binding = { instanceDataRoot: f.root, instanceConfigDir: f.configDir }; const alpha = await bootBusiness(f, { ...binding, org: { id: 'alpha' } }); const key = (await alpha.bus.modelPreflight({ business: 'demo', role: 'sec-1' })).key; const doc = businessDoc(f.root, 'beta'); doc.plugins = { ...doc.plugins, harness: ['pi'] }; writeJson(join(f.configDir, 'businesses', 'beta.json'), doc); const business = loadBusiness('beta', { dir: f.configDir, rolesDir: alpha.rolesDir }), resolved = {}; for (const instance of Object.keys(business.roles)) resolved[instance] = resolveInstance({ system: alpha.system, business, instance }); const betaRoot = join(f.root, 'beta-bus'); fs.mkdirSync(betaRoot, { mode: 0o700 }); new Registry(betaRoot); const beta = await startBroker({ dataRoot: betaRoot, businesses: { beta: busOrganization(business, resolved) }, organization: { dir: f.configDir, rolesDir: alpha.rolesDir, system: alpha.system }, proxy: { port: Number(new URL(f.baseUrl).port), binary: '/fixture-unused' }, ...binding, org: { id: 'beta' } }); f.closers.push(() => beta.close()); assert.equal(await f.client.probeKey(key), 200); assert.ok(f.state.keys.some(value => value.startsWith(`mso-${hash(f.configDir)}-beta-sentinel-`))); assert.equal((await alpha.bus.modelPreflight({ business: 'demo', role: 'sec-1' })).key, key); }); for(const level of ['root','project'])test(`A4 ${level} fire removes its implicit model key before publication`,async t=>{ const f=await fixture(t),b=await bootBusiness(f,{copyLevel:level}); const instance=level==='root'?'aide':'aide.stack'; const issued=await b.bus.modelPreflight({business:'demo',role:instance}); assert.ok(issued.key); await b.bus.broker.request(ownerConsole(b.bus.broker),{verb:'hire.start.console',args:{op:'fire',level,...(level==='project'?{project:'stack'}:{}),name:'aide',reason:'Copy work finished'}}); assert.equal(f.state.keys.includes(issued.key),false,'remote generation is revoked'); assert.equal(b.bus.credentials.modelRecord('demo/'+instance),null,'private generation is removed'); const human=b.bus.broker.bindHuman({business:'demo',human:'jason',via:'cli',outsideAgent:true}); assert.throws(()=>b.bus.broker.request(human,{verb:'instance.launch',args:{instance}})); }); test('AU resolver guard runs inside the model revocation queue before any remote or local effect',async t=>{ const f=await fixture(t);await f.manager.reconcile();await f.manager.ensure('demo/coder'); const before=f.credentials.modelRecord('demo/coder'),calls=f.state.calls.length; await assert.rejects(f.manager.prepareRevoke('demo/coder',()=>{throw Object.assign(new Error('not-resolver'),{code:'not-resolver'});}),{code:'not-resolver'}); assert.deepEqual(f.credentials.modelRecord('demo/coder'),before);assert.equal(f.state.calls.length,calls); });