bus: tester identity for PM tests (row 110, #1604)

A tester binds with a process proof from the bus's own tester-cli.mjs
when the business var bus.tester is true. It may send [mos test] messages
to a role, receive replies on the tester address and use the read verbs;
every other verb refuses. Messages store from_role tester and write no
human.input event. turnRequest() marks them as never Jason's instruction.
human.mjs is unchanged.

Candidate manifest bdaf7f476abd0b2b5095ab2f7f43e38540e6ba48ee14a02a4f4995a1469b79df
(Darkwing round 2, packet 9f1563ec1), BASE badfa52b9. Approved by Filbert
(comment 28360) and Sage (comment 28361). Sage gate rerun passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-13 20:43:40 -05:00
co-authored by Claude Opus 5.5
parent 67ac176f2a
commit 179697a8a3
19 changed files with 822 additions and 41 deletions
+8 -3
View File
@@ -4,7 +4,8 @@ import { BusError, TASK_VERBS } from './broker.mjs';
const LIMIT = 65536;
// One request per connection. There is deliberately no reconnect/retry or SQL verb.
// `tasks` ({handle, timeout}) is the trusted S3 adapter; only the eight task verbs reach it.
export async function serve({ broker, path, authenticateHuman = null, timeout = 5000, tasks = null }) {
// A request carries exactly one of cap, human (row S4) and tester (row 110) proof.
export async function serve({ broker, path, authenticateHuman = null, authenticateTester = null, timeout = 5000, tasks = null }) {
try {
lstatSync(path);
throw new BusError('socket-exists');
@@ -44,9 +45,9 @@ export async function serve({ broker, path, authenticateHuman = null, timeout =
!r ||
Array.isArray(r) ||
typeof r !== 'object' ||
Object.keys(r).some((k) => !['cap', 'human', 'verb', 'args'].includes(k)) ||
Object.keys(r).some((k) => !['cap', 'human', 'tester', 'verb', 'args'].includes(k)) ||
typeof r.verb !== 'string' ||
Boolean(r.cap) === Boolean(r.human)
[r.cap, r.human, r.tester].filter(Boolean).length !== 1
)
throw new BusError('invalid-envelope');
let cap = r.cap;
@@ -54,6 +55,10 @@ export async function serve({ broker, path, authenticateHuman = null, timeout =
if (!authenticateHuman) throw new BusError('human-required');
transient = cap = authenticateHuman(r.human);
}
if (r.tester) {
if (!authenticateTester) throw new BusError('tester-required');
transient = cap = authenticateTester(r.tester);
}
if (typeof cap !== 'string') throw new BusError('unauthenticated');
if (tasks && TASK_VERBS.includes(r.verb)) {
const own = transient;