Skip to main content

Humanwork Sandbox Supervisor — Design

Status: Target design locked for implementation; production cutover remains NO_GO until ADR-022 has real AlexD / Paul / Eusden cosign. Base inspected: humanwork/dev and rig/main@49d3dd6. Decision hook: ADR-022.

Goal

Ship a Humanwork-owned sandbox supervisor that satisfies the runtime isolation/security needs without Docker, gVisor, or Firecracker as the planned substrate. The production health/cutover gate must stay NO_GO while ADR cosign is pending; implementation evidence is not a substitute for recorded approval.

Target shape:

API runtime control plane
→ RemoteSandboxWorkerBackend / SANDBOX_WORKER_URL
→ sandbox-worker HTTP service
→ Humanwork sandbox supervisor
→ supervised bubblewrap sandbox
→ sandbox-init / Zig capability shell
→ typed operation handler

bubblewrap is a load-bearing part of the solution. We are not dropping it. The self-written custom solution is the supervisor contract around it: policy translation, profile generation, cgroups, proxy/no-network semantics, process reaping, cancellation, TTL cleanup, evidence, and API fail-closed verification.

Production cutover gate

The design is finite and locked: Humanwork Supervisor + supervised bubblewrap is the shipped target for hostile user-space isolation, and Firecracker is only the separate-kernel contingency described in ADR-022. There are no unresolved substrate choices for proxy-only, Landlock, host class, cgroup CPU/IO/storage, seccomp format, daemon/cancel model, or evidence trust.

Current cutover state is NO_GO: adr_cosign_pending because the required AlexD / Paul / Eusden cosign is not recorded. Worker/supervisor health must surface that as a blocker. Do not fake signatures, do not treat a missing cosign as a warning, and do not route production traffic to direct spawn, bwrap-alone, Firecracker, raw-bwrap success, or degraded-success fallback.

Non-goals

  • Do not expose raw shell.
  • Do not bind host .ssh, Docker socket, broad repo roots, or host home into production sandboxes.
  • Do not silently fall back to unsandboxed execution in production.
  • Do not reimplement bwrap's namespace/mount jailer before evidence says bwrap is a blocker.
  • Do not claim microVM-equivalence against kernel zero-days. If the threat model requires a separate guest kernel, reopen the ADR with evidence.
  • Do not build a general-purpose PaaS.

End-state technical description

The end result is a shipped Humanwork sandbox supervisor that owns the execution boundary from request admission through teardown. It is not a wrapper script around a shell and not a generic container launch. It is a runtime subsystem with a stable protocol, a supervised bwrap backend, evidence fields the API can verify, and a hostile-code proof battery.

Locked final decisions

These decisions are part of the target design, not follow-up questions:

AreaFinal decision
Supervisor process modelLong-lived daemon over a Unix socket. --run-json is test/smoke only and cannot be the production worker path.
Wire schemaCanonical JSON is camelCase. Internal snake_case must be covered by conversion tests. Unknown wire fields reject in production.
Evidence trust boundarySupervisor constructs the outer result and evidence. Sandboxed child output cannot set or override evidence fields.
Operation policyVersioned operation manifest maps API ops to capability-shell ops, network mode, workspace policy, and executable allowlist requirements.
Resource policyProduction requires cgroup v2 pids/memory/cpu/io plus workspace byte quota. Missing required controllers or quota support is NO_GO.
SeccompInner bwrap profile is generated default-deny seccomp BPF passed by FD. OCI JSON is only an outer container profile.
LandlockEvidence is always present. If ABI is available, sandbox-init must apply it or fail closed; unavailable ABI is explicit defense-in-depth absence.
network=proxy-onlyImplemented as supervised netns/veth + proxy + nftables allow-only-proxy, or returns proxy_only_unavailable; no silent downgrade.
Deployment host classNative/systemd dedicated worker is recommended. Dedicated K8s node is allowed only after primitive smoke proves cgroup/quota/netns/seccomp behavior.
RollbackRollback routes away from sandbox capability execution or to a previous supervisor build; never to unsandboxed direct spawn.

Components

ComponentPathResponsibility
API dispatch gateapi/src/runtime-control-plane/sandbox-capability-executor.service.tsValidate typed operation shape, reject raw shell/env passthrough, attach manifest-derived executable allowlist, require supervisor evidence in production.
API ledger gateapi/src/runtime-control-plane/sandbox-capability-gateway.service.tsPersist redacted substrate evidence: backend/version/profile/seccomp/limits/network/cleanup.
Worker HTTP shimrunners/sandbox-worker/worker.pyToken auth, request size cap, JSON normalization, forward to supervisor, expose cancel endpoint. No direct capability-shell spawn in production.
Supervisor service/binaryrunners/sandbox-supervisor/Operation registry, readiness probe, bwrap profile generation, cgroup lifecycle, cancellation, reaping, output caps, evidence emission.
bwrap backendrunners/sandbox-supervisor/src/backend/linux_bwrap.zigBuild and execute deterministic bwrap command from a Humanwork profile. This is the production backend when supervised.
sandbox-initrunners/sandbox-supervisor/src/sandbox_init.zigOptional pre-exec helper launched inside bwrap to apply Landlock where available, finalize env, then exec capability-shell.
Capability shellrunners/zig-capability-shell/Typed op executor that remains inside the sandbox: argv-only exec, artifact ops, path normalization, env scrub.
Hostile-code probesagent/evals/test_sandbox_capability_ops.py, runners/sandbox-supervisor/tests/probes/Prove the boundary against direct adversarial attempts and prevent audit-theater greens.

Request lifecycle

  1. API receives a typed sandbox capability request.
  2. API validates operation, args, timeout, output cap, network mode, and manifest allowlist.
  3. API dispatches to SANDBOX_WORKER_URL with no arbitrary env passthrough.
  4. Worker authenticates request and forwards normalized dispatch to supervisor.
  5. Supervisor allocates an op slot under $HUMANWORK_SANDBOX_ROOT/runs/<org>/<run>/<op>/.
  6. Supervisor generates synthetic rootfs and minimal /etc (passwd, group, nsswitch.conf) plus read-only loader/TLS files when present.
  7. Supervisor creates cgroup v2 limits and a deterministic bwrap profile.
  8. Supervisor launches bwrap with namespaces, bind plan, tmpfs, /proc, minimal /dev, net mode, seccomp FD/profile, --die-with-parent, and --new-session.
  9. bwrap launches sandbox-init or capability-shell --protocol=json inside the sandbox.
  10. Supervisor drains bounded stdout/stderr, handles timeout/cancel, reaps process tree, records cgroup stats, tears down or schedules TTL cleanup.
  11. Worker returns structured SandboxResult.
  12. API verifies required evidence and appends redacted ledger events.

Production evidence contract

A production success response is invalid unless the supervisor-owned outer result contains all fields below. The sandboxed child process may emit stdout/stderr or an operation payload, but it cannot set or override any substrate evidence field.

{
"schemaVersion": 1,
"sandboxBackend": "linux-bwrap-supervisor",
"supervisorVersion": "...",
"supervisorBuildId": "sha256:...",
"bwrapVersion": "...",
"bwrapPath": "/usr/bin/bwrap",
"capabilityShellHash": "sha256:...",
"operationManifestHash": "sha256:...",
"sandboxProfileHash": "sha256:...",
"seccompProfileHash": "sha256:...",
"requestedNetwork": "off",
"effectiveNetwork": "off",
"limits": {
"timeoutMs": 30000,
"stdioMaxBytes": 262144,
"pidsMax": 64,
"memoryMaxBytes": 536870912,
"cpuMax": "50000 100000",
"ioMax": [{ "major": 8, "minor": 0, "rbps": 10485760, "wbps": 10485760, "riops": 1000, "wiops": 1000 }],
"workspaceMaxBytes": 1073741824,
"workspaceOutputMaxBytes": 268435456,
"workspaceLogsMaxBytes": 67108864
},
"landlock": { "available": true, "enabled": true, "abi": 4, "rulesetApplied": true, "reason": null },
"cleanup": { "processTreeReaped": true, "cgroupKilled": false, "cgroupRemoved": true, "workspaceTornDown": true, "quotaReleased": true }
}

If any required field is missing or inconsistent with the dispatch/configured production policy, the API treats the worker response as unsandboxed evidence and fails closed. status=ok is the only success status. denied, timeout, cancelled, and error map to blocked/failed/cancelled ledger events, not success. Hash fields are sha256:<64 lowercase hex> over the exact manifest/profile/seccomp/runtime bytes used for that operation.

Cleanup object is required and canonical: processTreeReaped, cgroupKilled, cgroupRemoved, workspaceTornDown, quotaReleased. On success, processTreeReaped, cgroupRemoved, workspaceTornDown, and quotaReleased must be true; cgroupKilled is factual evidence (true if supervisor used cgroup subtree kill during timeout/cancel/reaper fallback, false for normal exit) and is not required to be true.

Byte-limit names are intentionally distinct: stdioMaxBytes caps stdout/stderr captured inline in the result envelope; workspaceOutputMaxBytes caps /workspace/output; workspaceLogsMaxBytes caps /workspace/logs; workspaceMaxBytes caps the whole per-op workspace. Production validators must reject legacy byte-limit aliases and accept only these canonical field names.

Canonical cleanup evidence object:

{
"processTreeReaped": true,
"cgroupKilled": false,
"cgroupRemoved": true,
"workspaceTornDown": true,
"quotaReleased": true
}

This object is required in every supervisor result. The API validator must check presence and boolean type for all five fields. For status=ok, processTreeReaped, cgroupRemoved, workspaceTornDown, and quotaReleased must be true; cgroupKilled is required factual evidence and may be false on normal exit or true when the supervisor used cgroup subtree kill during timeout/cancel/reaper fallback.

Current Humanwork state

LayerCurrent stateGap
Control planesandbox-capability-executor.service.ts validates typed ops, rejects raw shell-shaped inputs, caps time/output, and dispatches to remote worker.Needs worker-side substrate evidence and fail-closed backend verification.
Capability shellrunners/zig-capability-shell/ enforces path checks, argv-only exec, env scrub, executable allowlist.Runs as a normal child of worker.py; syscalls are not boundary-enforced by supervisor/bwrap yet.
Remote workerrunners/sandbox-worker/worker.py exposes /v1/sandbox/ops and invokes the capability shell directly.No supervised bwrap sandbox, cgroup slot, cancel endpoint, operation registry, or process-tree reaper.
Deployment scaffoldsseccomp.json, K8s hardening, Firecracker config.Mostly inert until a runtime layer applies them and proves behavior.

Rig reference

Rig has the useful orchestration pattern:

  • backend abstraction;
  • deterministic bwrap argv/profile generation;
  • sandbox wrapping before spawn;
  • tests around backend selection and wrapped command construction.

Humanwork should reuse that shape, but not copy Rig's host-convenience policy. Rig is local-runtime/developer oriented and binds host conveniences. Humanwork is tenant/runtime/security oriented and must keep a smaller, auditable surface.

Reference map

Humanwork source-of-truth chain:

Current repo seams this design changes or depends on:

  • api/src/runtime-control-plane/sandbox-capability-executor.service.ts — typed op validation, remote worker dispatch, production evidence gate.
  • api/src/runtime-control-plane/sandbox-capability-gateway.service.ts — policy decision + run-event ledger surface.
  • runners/sandbox-worker/worker.py — current direct subprocess.run([capability-shell]) path that must be removed.
  • runners/zig-capability-shell/ — existing typed operation executor that remains inside the sandbox.
  • agent/evals/test_sandbox_capability_ops.py — deny-battery entry point to expand from capability tests to hostile-code substrate probes.

Adjacent implementation references:

Feasibility call

This is feasible and directionally good only with a scoped claim:

  • Claim we can make: Humanwork can cover the Firecracker features it needs for hostile user-space runtime code with a supervisor+bwrap substrate.
  • Claim we cannot make: Humanwork can recreate Firecracker's separate guest-kernel/hypervisor boundary without building or adopting a VMM.

That distinction is the load-bearing line. If the required boundary is “no host file/process/network/resource/syscall escape for adversarial user-space code,” the supervisor+bwrap target is the right target. If the required boundary is “survive kernel zero-days or malicious kernel-level exploits,” use a separate-kernel substrate.

Recommended path:

  1. Build the Humanwork supervisor contract once.
  2. Make bwrap the supervised production backend.
  3. Keep bwrap profiles narrow and generated by Humanwork, not by caller input.
  4. Fail closed in production unless supervisor+bwrap evidence is present.

Options evaluated

OptionWhat it givesWhat it costsFit
Docker/task boundaryFamiliar packaging and deploy surfaceDocker daemon/runtime coupling, weak per-op lifecycle, platform policy driftReject as substrate; okay as outer packaging only
Hardened K8s podPod security, seccomp profile, NetworkPolicy, node schedulingCluster dependency, still not a per-op product contractDeployment wrapper, not the runtime substrate
bwrap aloneLinux namespace/mount sandboxNo Humanwork lifecycle/evidence/cgroups/proxy/reaper contractMechanism only
Humanwork supervisor + bwrapMature Linux sandbox mechanism plus owned product contractRequires worker-host primitives and careful profile generationRecommended target
nsjail/minijail + supervisorMature jailer-style alternativesAnother external policy surface, less aligned with Rig/hwork codeViable fallback if bwrap blocks
gVisor/runscUser-space kernel, stronger syscall mediation than plain namespacesCompatibility testing, runtime integration, host support, operational opacityUseful contingency, overkill for default
FirecrackerSeparate guest kernel and microVM isolationRootfs/kernel image lifecycle, jailer ops, boot/snapshot complexity, lower reuseOnly for explicit separate-kernel threat model
WASI/WasmtimeVery strong capability model for wasm modulesRequires workload rewrite/compile to WASM; poor fit for Python/Node/browser toolsFuture plugin lane, not general runtime substrate
Humanwork direct Linux backendOwns primitive orchestration without bwrap dependencyReimplements bwrap's sharpest surface; higher security riskFuture fallback only after bwrap evidence says it is needed

Firecracker feature coverage we need

Yes: cover the Firecracker features Humanwork needs ourselves, without shipping Firecracker.

The replacement is not “write a VMM.” It is “own the isolation contract around bwrap.”

Needed featureSupervisor+bwrap mechanismProof gate
Fresh isolated runtimeper-op/per-run slot, synthetic rootfs, deterministic bind planno cross-run file/process state survives cleanup
Read-only machine image feelbwrap tmpfs root + read-only runtime bindsrootfs write probe denied
Writable scratch/outputtmpfs + output/log bind slots onlywrites outside allowed slots denied
Process boundarypid namespace, new session, cgroup membership, /proc scoped to sandboxsibling-process probe denied; descendants reaped
Network boundarynet namespace with no routes, or explicit proxy-only pathraw egress/metadata/localhost probes denied
Resource boundarycgroups v2 pids/memory/cpu/iofork bomb + memory pressure probes contained
Syscall boundarybwrap --seccomp + no_new_privs + dropped capsmount, ptrace, setns, bpf, keyring probes denied with expected errno
Filesystem defense-in-depthgenerated bind plan; optional Landlock in sandbox-init where availablehost-path read/write probes denied even under path tricks
Secret isolationenv scrub, no host home, no .ssh, no cloud credential mountsenv/credential probes empty
Audit / forensicsbackend version + profile/seccomp hashes + denial/cleanup eventsAPI rejects worker success without evidence

Only missing Firecracker property: separate guest kernel. We should not pay that complexity unless kernel-escape resistance becomes an explicit product/security requirement.

No-bypass invariant

The implementation target is the supervisor substrate itself, with no production bypass path.

Forbidden production paths:

sandbox-worker -> capability-shell
sandbox-worker -> subprocess.run([capability-shell])
API -> worker success without linux-bwrap-supervisor evidence
API/worker -> backend=none
API/worker -> raw bwrap without supervisor evidence
API/worker -> degraded sandbox success

Required production path:

API -> sandbox-worker -> sandbox-supervisor -> supervised bwrap backend -> isolated capability-shell

The worker may retain test fakes, but they must be compile/runtime gated to tests and must never satisfy production evidence checks. Any health failure in required host primitives returns NO_GO; it does not route to an unsandboxed executor.

Rootfs and /etc strategy

The sandbox root is synthetic. Do not bind host /etc wholesale.

Generate a minimal /etc under the op slot and bind it read-only:

/etc/passwd         # sandbox user only
/etc/group # sandbox group only
/etc/nsswitch.conf # files-only resolution

Read-only loader/TLS files may be bound when present:

/etc/ld.so.cache
/etc/ld.so.conf
/etc/ld.so.conf.d
/etc/ssl/certs
/etc/ca-certificates
/etc/pki

TLS trust roots do not imply network access. They are only useful once explicit proxy-only exists.

Filesystem policy

Allowed roots inside sandbox:

/workspace/input    read-only
/workspace/output read-write
/workspace/tmp tmpfs
/workspace/logs read-write bounded
/runtime/bin read-only capability-shell/runtime binaries
/tmp tmpfs
/proc sandbox proc only
/dev minimal device set

Forbidden:

  • host home;
  • .ssh;
  • Docker socket;
  • repo root by default;
  • cloud credential files;
  • /var/run, /run except explicitly generated control-plane proxy socket;
  • broad /etc;
  • sibling run directories.

Network policy

network=off

bwrap creates a network namespace with no usable route. Direct TCP, UDP, DNS, metadata endpoint access, host localhost probing, and raw sockets must fail. Evidence records requestedNetwork=off and effectiveNetwork=off.

network=proxy-only

proxy-only is implemented as a supervised network path, not left as an open design question:

  1. Host runs a Humanwork egress proxy that enforces the operation manifest's egress allowlist.
  2. Supervisor launches bwrap with a new network namespace, --info-fd for namespace metadata, and --sync-fd as the mandatory pause gate before user exec. --info-fd alone does not pause execution.
  3. Supervisor reads the sandbox netns metadata from --info-fd, keeps --sync-fd unreleased, and creates a veth pair into the sandbox netns.
  4. Supervisor's in-flight operation registry atomically leases one free /30 block from the proxy-only pool before veth/IP/route setup. Production default pool: 169.254.128.0/17 subdivided into non-overlapping /30 operation leases. Host side uses the first usable IP, sandbox side uses the second usable IP, network/broadcast addresses remain unused.
  5. Lease ownership is tied to the op slot lifecycle and released only after netns teardown, route removal, and registry cleanup. Occupied /30 blocks are never reused, retried, or shared while an op is in flight.
  6. Supervisor installs nftables/iptables rules in the sandbox netns allowing only TCP to the leased host-proxy IP:port and denying all other egress, metadata ranges, and host-local addresses.
  7. Supervisor releases sandbox-init/capability-shell.
  8. Evidence records proxy config hash, leased /30, host/sandbox IPs, and effectiveNetwork=proxy-only.

If proxy config, CAP_NET_ADMIN, nftables/iptables, veth setup, route insertion, or proxy-only IP lease acquisition fails, a proxy-only dispatch returns status=denied, reason=proxy_only_unavailable, effectiveNetwork=null. Pool exhaustion is a hard fail-closed condition. There is no silent downgrade to off, no host network fallback, and no reuse of an occupied range.

Hostile-code target

The target is hostile arbitrary user-space runtime code under the Humanwork operation contract.

Threat classTarget answer
Read host files / secretssynthetic root + bind allowlist + no ambient secrets
Write outside workspaceread-only root + output/tmp/log-only writes
Raw network exfiltrationnetns off or explicit proxy-only path
Fork bomb/resource burncgroups v2 pids/memory/cpu/io + timeout + reaper
Sibling process inspectionpid namespace + sandbox-scoped proc
Syscall abusebwrap seccomp profile attached by supervisor
Capability-shell bypassno raw shell, argv-only, executable allowlist, policy evidence required
Kernel escapeout of scope; only this class justifies reopening Firecracker

Acceptance bar is hostile-code probes, not just wrapper existence.

Reuse model

Reuse the supervisor service, read-only runtime assets, generated profile builder, bwrap binary probe results, cached binary metadata, and health probes.

Do not reuse mutable sandbox process/workspace state across org/specialist/run trust domains.

LevelReuseAllowed?
Supervisor daemonProcess and profile codeYes
Runtime root/cacheRead-only assetsYes
Same run slotOptional only with identical manifest/profile hashLater
Cross-run mutable slotWorkspace/process stateNo
Cross-org/specialist slotAny mutable stateNo

Operation manifest and event schema

A versioned operation manifest maps every API operation to its capability-shell handler, network policy, workspace policy, and executable allowlist requirement. Production rejects any API op that lacks a manifest entry or a concrete capability-shell handler.

Minimum manifest entries:

API opCapability-shell opChild executable?AllowlistNetwork
artifact_readartifact_readfalseempty / deny-alloff
artifact_writeartifact_writefalseempty / deny-alloff
exec_argvexec_argvtruenon-empty explicit allowlistoff or manifest-approved proxy-only
browser_actionbrowser_action or explicit adaptertruenon-empty browser/runtime allowlistproxy-only

Worker response and API ledger payload include the production evidence contract above plus operation status, duration, exit code, bounded stdout/stderr bytes, denial reason, and sanitized payload metadata. The ledger stores redacted substrate metadata only; it never stores raw secrets or oversized stdout/stderr.

Deployment model

Preferred production deployment is a dedicated native/systemd sandbox worker host with:

  • pinned bubblewrap package or pinned bundled bwrap binary, health-probed and recorded in evidence;
  • sandbox-supervisor and capability-shell installed under a read-only runtime path;
  • $HUMANWORK_SANDBOX_ROOT on a filesystem with project quotas enabled;
  • cgroup v2 delegated to the worker service (Delegate=yes) with writable pids/memory/cpu/io controllers and cgroup.kill or equivalent subtree kill;
  • seccomp enabled and bwrap seccomp FD support;
  • no Docker socket, host home, .ssh, cloud credential, broad /etc, broad /run, or broad /sys mounts;
  • supervisor Unix socket accessible only by the worker service user;
  • CAP_NET_ADMIN only when proxy-only is enabled.

Dedicated K8s node deployment is allowed only when a host-gated smoke proves namespace, seccomp, cgroup v2 delegation, project quota, and proxy-only primitives on that node. Generic nested Docker, Railway, or locked-down PaaS workers are production NO_GO for hostile-code mode unless they prove every required primitive. Outer Docker/K8s may package the worker; it is not the product sandbox boundary.

Failure modes

FailureHandling
supervisor unavailableworker healthcheck not-ready; API fails closed
bwrap missing/version/hash mismatchworker healthcheck NO_GO; no fallback to unsandboxed
required namespace disabledsubstrate unavailable; no fallback to unsandboxed
cgroup pids/memory/cpu/io unavailableproduction NO_GO; cannot emit success evidence
workspace project quota unavailableproduction NO_GO; cannot run writable hostile-code ops
seccomp BPF generation or attach failurefail closed before user code exec
Landlock ABI available but apply failsfail closed before user code exec
Landlock ABI unavailableexplicit evidence kernel_unsupported; allowed only because mount/bind/quota boundary remains load-bearing
proxy-only config/primitives missingdenied: proxy_only_unavailable; no downgrade to host network or off success
timeout/cancelkill process group and cgroup subtree; success requires cleanup evidence
output/log/workspace cap exceededkill sandbox; return bounded reason and cleanup evidence
child emits fake evidence JSONtreated as stdout/payload; supervisor evidence remains authoritative
cleanup/quota release failsoperation cannot be ok; ledger records failure and retry/TTL cleanup state

Firecracker stance

Firecracker is not a planned follow-up. The Humanwork supervisor+bwrap substrate is the intended shipped substrate for the Humanwork runtime threat model, including hostile user-space runtime code.

Reopen Firecracker only if one of these becomes true:

  • hostile-code proof gates show supervisor+bwrap cannot enforce a required boundary;
  • the product explicitly requires separate guest-kernel isolation against kernel escape classes;
  • target host classes cannot support the required Linux primitives while meeting deployment requirements.

Until one of those is true, adding Firecracker is extra complexity without a load-bearing reason.

Test strategy

  • Schema tests for strict camelCase API -> worker -> supervisor protocol and unknown-field rejection.
  • Operation-manifest tests proving every API op maps to a real capability-shell handler and policy.
  • Unit tests for profile generation, forbidden mounts, synthetic /etc, rootfs, and evidence hashing.
  • Unit tests for cgroup pids/memory/cpu/io, project quota, race-free attach-before-exec, and cgroup cleanup.
  • Unit tests for default-deny seccomp BPF generation and exact FD handoff to bwrap.
  • Unit tests for Landlock evidence and apply/fail-closed behavior.
  • Unit tests and integration tests for network=off and proxy-only veth/proxy/nftables behavior.
  • Worker tests proving production direct capability-shell spawn is removed and test fakes cannot satisfy production evidence.
  • API tests proving evidence validation, status/exception mapping, and redacted ledger metadata.
  • Hostile-code probes for path escape, symlink/hardlink cleanup escape, /proc sibling inspection, ptrace, mount, setns, unshare, bpf, io_uring, keyring, fork bomb, memory pressure, CPU burn, disk fill, high IO, raw egress, metadata, host localhost, raw sockets, env dump, rootfs write, executable allowlist bypass, timeout, cancel, and malicious child fake-evidence.
  • Audit-theater guards: probes must call the relevant syscall/helper directly and assert expected denial reason/errno; a wide-open substrate must fail the deny battery.
  • CI split: deterministic units run everywhere; host-gated smoke reports NO_GO/skipped on ineligible GitHub runners and only reports production GO on an eligible worker host.

Closed decisions

There are no remaining substrate open questions in this design. Proxy-only, Landlock, cgroup CPU/IO/storage, canonical protocol, operation manifest, daemon/cancel model, workspace cleanup, deployment host class, seccomp format, and CI smoke semantics are fixed above. Any future change reopens ADR-022 rather than becoming an implicit follow-up phase.