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]>
102 lines
3.1 KiB
JavaScript
102 lines
3.1 KiB
JavaScript
import { readFileSync, lstatSync } from 'node:fs';
|
|
import { basename, resolve } from 'node:path';
|
|
import { BusError } from './broker.mjs';
|
|
const MARKERS = [
|
|
'MOSAIC_BUS_CAP',
|
|
'MOSAIC_RUN_ID',
|
|
'MOSAIC_AGENT_RUN',
|
|
'CLAUDECODE',
|
|
'CLAUDE_CODE_ENTRYPOINT',
|
|
'CODEX_THREAD_ID',
|
|
'PI_AGENT_DIR',
|
|
];
|
|
const refuse = () => {
|
|
throw new BusError('human-required');
|
|
};
|
|
export function readProcess(pid, { readFile = readFileSync, stat = lstatSync } = {}) {
|
|
if (!Number.isSafeInteger(pid) || pid < 1) refuse();
|
|
try {
|
|
const dir = `/proc/${pid}`,
|
|
raw = readFile(dir + '/stat', 'utf8'),
|
|
fields = raw.slice(raw.lastIndexOf(')') + 2).split(' ');
|
|
let env = {};
|
|
let environment;
|
|
try {
|
|
environment = readFile(dir + '/environ', 'utf8');
|
|
} catch (e) {
|
|
if (e.code !== 'EACCES') throw e;
|
|
env = null;
|
|
}
|
|
if (environment !== undefined)
|
|
for (const entry of environment.split('\0')) {
|
|
const i = entry.indexOf('=');
|
|
const key = entry.slice(0, i);
|
|
if (key === 'MOSAIC_BUS_CLI_NONCE' || MARKERS.includes(key)) env[key] = entry.slice(i + 1);
|
|
}
|
|
return {
|
|
pid,
|
|
ppid: Number(fields[1]),
|
|
startTime: fields[19],
|
|
uid: stat(dir).uid,
|
|
argv: readFile(dir + '/cmdline', 'utf8')
|
|
.split('\0')
|
|
.filter(Boolean),
|
|
env,
|
|
};
|
|
} catch {
|
|
refuse();
|
|
}
|
|
}
|
|
// Cooperative same-UID process checks, not peer credentials or a hostile-process wall.
|
|
// A nonce in the CLI's launch environment binds the submitted PID to a live CLI.
|
|
export function verifyHuman(proof, { cliPath, launches = [], readProcess: read = readProcess }) {
|
|
if (
|
|
!proof ||
|
|
typeof proof !== 'object' ||
|
|
Object.keys(proof).some((k) => !['pid', 'startTime', 'nonce', 'business'].includes(k)) ||
|
|
!Number.isSafeInteger(proof.pid) ||
|
|
proof.pid < 2 ||
|
|
typeof proof.business !== 'string' ||
|
|
typeof proof.nonce !== 'string' ||
|
|
!/^[a-f0-9]{64}$/.test(proof.nonce)
|
|
)
|
|
refuse();
|
|
try {
|
|
const p = read(proof.pid);
|
|
if (
|
|
p.uid !== process.getuid() ||
|
|
p.startTime !== proof.startTime ||
|
|
p.env?.MOSAIC_BUS_CLI_NONCE !== proof.nonce ||
|
|
p.argv[1] !== resolve(cliPath)
|
|
)
|
|
refuse();
|
|
const seen = new Set();
|
|
let current = p;
|
|
for (let depth = 0; depth < 128; depth++) {
|
|
if (seen.has(current.pid)) refuse();
|
|
seen.add(current.pid);
|
|
if (
|
|
(current.env !== null && MARKERS.some((k) => current.env[k])) ||
|
|
launches.some((r) => r.pid === current.pid && r.startTime === current.startTime)
|
|
)
|
|
refuse();
|
|
const command = basename(current.argv[0] ?? '');
|
|
if (
|
|
/^(pi|claude|claude-code|codex)(?:\.js)?$/.test(command) ||
|
|
current.argv.some((v) => /\/(?:pi-coding-agent|codex)\/(?:dist|bin)\//.test(v))
|
|
)
|
|
refuse();
|
|
if (current.ppid === 1) {
|
|
const again = read(proof.pid);
|
|
if (again.startTime !== proof.startTime || again.ppid !== p.ppid) refuse();
|
|
return { business: proof.business };
|
|
}
|
|
if (current.ppid < 2) refuse();
|
|
current = read(current.ppid);
|
|
}
|
|
} catch {
|
|
refuse();
|
|
}
|
|
refuse();
|
|
}
|