// Started with fork() by the trusted host; boot refs/capabilities travel over IPC, // never command arguments, stdout or service-token-bearing environment variables. import { startBroker } from './runtime.mjs'; import { BusError } from './broker.mjs'; const LAUNCH_OPS = new Set(['identity', 'authorizeLaunch', 'refuse', 'endLaunch', 'credentialStatus']); function launchOp(m) { if (m.op === 'identity') return runtime.identity(m.cap); if (m.op === 'authorizeLaunch') return runtime.authorizeLaunch(m.cap, m.instance); if (m.op === 'refuse') return runtime.refuse(m.cap, m.code); if (m.op === 'endLaunch') return runtime.endLaunch(m.record); return runtime.credentialStatus(m.business, m.role); } // The host's request id comes back on the reply, so a late reply can't // answer a later request (host.mjs). A message without one gets none. const tag = (message, reply) => (Number.isSafeInteger(message?.id) ? { ...reply, id: message.id } : reply); let runtime, booted = false, closing = false; async function close(code) { if (closing) return; closing = true; try { await runtime?.close(); } catch { code = 2; } process.exitCode = code; if (process.connected) process.disconnect(); } if (!process.send) { process.stderr.write('trusted-host-required\n'); process.exitCode = 2; } else { const timer = setTimeout(() => close(2), 10000); process.on('message', async (message) => { try { if (message?.op === 'bindLaunch' && runtime) { try { process.send(tag(message, { ok: true, launch: runtime.bindLaunch(message.record) })); } catch (e) { process.send(tag(message, { ok: false, error: e instanceof BusError ? e.code : 'bind-refused' })); } return; } // S6 launcher ops, one reply each, like bindLaunch. if (LAUNCH_OPS.has(message?.op) && runtime) { try { process.send(tag(message, { ok: true, result: launchOp(message) })); } catch (e) { process.send(tag(message, { ok: false, error: e instanceof BusError ? e.code : 'launch-op-refused' })); } return; } if (message?.op === 'close') { clearTimeout(timer); await close(0); return; } if (message?.op !== 'boot' || booted) throw new BusError('invalid-host-request'); booted = true; clearTimeout(timer); // `trackers` is S3's plain-data boot config; the adapter is loaded only when a business has one. const { trackers, ...config } = message.config ?? {}; if (trackers) { const { tasksAdapter } = await import('../../tasks/src/adapter.mjs'); config.tasks = tasksAdapter({ trackers }); } runtime = await startBroker(config); if (closing) { await runtime.close(); return; } process.send({ ok: true, path: runtime.path, launches: runtime.launches, readers: runtime.readers }); } catch (e) { process.send?.(tag(message, { ok: false, error: e instanceof BusError ? e.code : 'startup-refused' }), () => close(2), ); } }); process.on('disconnect', () => close(2)); process.on('SIGTERM', () => close(0)); process.on('SIGINT', () => close(0)); }