/// import { spawn, type ChildProcess } from 'node:child_process'; import { closeSync } from 'node:fs'; import { ptr } from 'bun:ffi'; import { awaitOutcome, cleanupOwned, holdOwnershipUntilEmpty, type SemanticOutcome, } from './host-supervisor-cleanup'; import { childOutcome, launchProtectedSidecar } from './host-supervisor-launch'; import { EINTR, POLLERR, POLLHUP, POLLIN, POLLNVAL, PR_GET_CHILD_SUBREAPER, PR_SET_CHILD_SUBREAPER, PR_SET_DUMPABLE, errno, installSupervisorProtection, libc, native, pidfdOpen, pidfdSendSignal, } from './host-supervisor-native'; import { closeTrackedIdentities, discoverOwnedProcesses, initializeProcessTracker, processIdentity, trackSemanticRoot, } from './host-supervisor-tracker'; import { promiseGate } from './promise-gate'; const PROTOCOL_VERSION = 1; const DISCOVERY_INTERVAL_MS = 5; const DEFAULT_GRACE_MS = 250; const DEFAULT_MAX_TRACKED_IDENTITIES = 4_096; const DEFAULT_REAP_TIMEOUT_MS = 2_000; type SignalName = NodeJS.Signals | null; let semanticStarted = false; let cancellationRequested = false; let trackingUncertain = false; let supervisorStartTime: string | undefined; let discoveryTimer: NodeJS.Timeout | undefined; let ownerPidfd: number | undefined; let ownerPollDescriptor: Buffer | undefined; let semanticChild: ChildProcess | undefined; const cancellationGate = promiseGate(); const ownerPidAtStart = process.ppid; function requestCancellation(): void { cancellationRequested = true; cancellationGate.resolve(); } function emit(value: object): void { process.stdout.write(`${JSON.stringify(value)}\n`); } function errorAttestation( code: 'capability-unavailable' | 'cleanup-uncertain' | 'invalid-invocation' ): void { emit({ protocolVersion: PROTOCOL_VERSION, type: 'cleanup-attestation', status: 'error', code, semanticStarted, }); } function assertCapability(): void { if (process.platform !== 'linux' || !['x64', 'arm64'].includes(process.arch)) { throw new Error('unsupported Linux supervisor platform'); } const selfIdentity = processIdentity(process.pid); if (selfIdentity === undefined) throw new Error('supervisor process identity unavailable'); supervisorStartTime = selfIdentity.startTime; if (native.prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) !== 0) { throw new Error('PR_SET_CHILD_SUBREAPER unavailable'); } const observed = new Uint32Array(1); if (native.prctl(PR_GET_CHILD_SUBREAPER, ptr(observed), 0, 0, 0) !== 0 || observed[0] !== 1) { throw new Error('child subreaper state could not be attested'); } if (native.prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) !== 0) { throw new Error('supervisor signal hardening unavailable'); } const selfPidfd = pidfdOpen(process.pid); if (selfPidfd < 0) throw new Error('pidfd_open unavailable'); try { if (pidfdSendSignal(selfPidfd, 0) !== 0) { throw new Error('pidfd_send_signal unavailable'); } } finally { native.close(selfPidfd); } const ownerBefore = processIdentity(ownerPidAtStart); if (ownerPidAtStart <= 1 || ownerBefore === undefined) { throw new Error('parent process identity unavailable'); } ownerPidfd = pidfdOpen(ownerPidAtStart); const ownerAfter = processIdentity(ownerPidAtStart); if ( ownerPidfd < 0 || ownerAfter?.startTime !== ownerBefore.startTime || process.ppid !== ownerPidAtStart ) { if (ownerPidfd >= 0) native.close(ownerPidfd); ownerPidfd = undefined; throw new Error('parent pidfd ownership unavailable'); } ownerPollDescriptor = Buffer.alloc(8); ownerPollDescriptor.writeInt32LE(ownerPidfd, 0); ownerPollDescriptor.writeInt16LE(POLLIN, 4); } function ownerProcessExited(): boolean { if (process.ppid !== ownerPidAtStart) return true; if (ownerPidfd === undefined || ownerPollDescriptor === undefined) { throw new Error('parent pidfd poll state unavailable'); } ownerPollDescriptor.writeInt16LE(0, 6); const result = native.poll(ptr(ownerPollDescriptor), 1, 0); if (result < 0) { if (errno() === EINTR) return false; throw new Error('parent pidfd poll failed'); } const events = ownerPollDescriptor.readInt16LE(6); return result > 0 && (events & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) !== 0; } async function main(): Promise { if (process.argv[2] === '--launch-protected') { try { await launchProtectedSidecar(); } catch { process.exitCode = 70; } return; } process.once('SIGTERM', requestCancellation); process.once('SIGINT', requestCancellation); try { assertCapability(); } catch { errorAttestation('capability-unavailable'); process.exitCode = 70; return; } if (process.argv[2] === '--probe') { try { installSupervisorProtection(ownerPidAtStart); } catch { errorAttestation('capability-unavailable'); process.exitCode = 70; return; } emit({ protocolVersion: PROTOCOL_VERSION, type: 'cleanup-attestation', status: 'clean', mode: 'linux-subreaper-pidfd', subreaper: true, pidfd: true, terminalBuffered: true, ownedProcessCount: 0, cancelled: false, semantic: { exitCode: 0, signal: null }, }); return; } const [sidecarPath, requestPath, graceSource, reapSource, identityCapSource] = process.argv.slice(2); const graceMs = Number(graceSource ?? DEFAULT_GRACE_MS); const reapTimeoutMs = Number(reapSource ?? DEFAULT_REAP_TIMEOUT_MS); const identityCap = Number(identityCapSource ?? DEFAULT_MAX_TRACKED_IDENTITIES); if ( !sidecarPath || !requestPath || !Number.isSafeInteger(graceMs) || graceMs < 0 || !Number.isSafeInteger(reapTimeoutMs) || reapTimeoutMs <= 0 || !Number.isSafeInteger(identityCap) || identityCap < 8 || identityCap > DEFAULT_MAX_TRACKED_IDENTITIES ) { errorAttestation('invalid-invocation'); process.exitCode = 64; return; } initializeProcessTracker(supervisorStartTime as string, identityCap); let outcomePromise: Promise; try { semanticChild = spawn( process.execPath, [ process.argv[1] as string, '--launch-protected', String(process.pid), sidecarPath, requestPath, ], { cwd: process.cwd(), detached: true, env: process.env, shell: false, stdio: ['ignore', 4, 'inherit', 3], windowsHide: true, } ); semanticStarted = true; if (semanticChild.pid === undefined || semanticChild.pid <= 1) { throw new Error('semantic PID unavailable'); } outcomePromise = childOutcome(semanticChild); if (!trackSemanticRoot(semanticChild.pid)) { const fastOutcome = await awaitOutcome(outcomePromise, Date.now() + 100); if (fastOutcome === undefined) throw new Error('semantic PID identity unavailable'); } discoveryTimer = setInterval(() => { try { if (ownerProcessExited()) requestCancellation(); discoverOwnedProcesses(); } catch { trackingUncertain = true; requestCancellation(); try { semanticChild?.kill('SIGTERM'); } catch { // Cleanup remains fail-closed and cannot attest clean after tracking uncertainty. } } }, DISCOVERY_INTERVAL_MS); discoveryTimer.unref(); } catch { if (semanticStarted) await holdOwnershipUntilEmpty(); errorAttestation('cleanup-uncertain'); process.exitCode = 70; return; } let outcome: SemanticOutcome | undefined; try { const first = await Promise.race([ outcomePromise.then((value) => ({ kind: 'outcome' as const, value })), cancellationGate.promise.then(() => ({ kind: 'cancel' as const })), ]); if (first.kind === 'outcome') outcome = first.value; outcome = await cleanupOwned( semanticChild.pid as number, outcome === undefined ? outcomePromise : Promise.resolve(outcome), cancellationRequested ? graceMs : 0, reapTimeoutMs ); if (trackingUncertain) throw new Error('owned process tracking became uncertain'); emit({ protocolVersion: PROTOCOL_VERSION, type: 'cleanup-attestation', status: 'clean', mode: 'linux-subreaper-pidfd', subreaper: true, pidfd: true, terminalBuffered: true, ownedProcessCount: 0, cancelled: cancellationRequested, semantic: outcome, }); } catch { await holdOwnershipUntilEmpty(); errorAttestation('cleanup-uncertain'); process.exitCode = 70; } finally { clearInterval(discoveryTimer); process.removeListener('SIGTERM', requestCancellation); process.removeListener('SIGINT', requestCancellation); closeTrackedIdentities(); if (ownerPidfd !== undefined) native.close(ownerPidfd); libc.close(); try { closeSync(3); } catch { // Credential input may already be closed by the semantic child lifecycle. } } } void main();