Context
Phase 1b made agentgateway the sole holder of LLM keys — workflow code now only ever holds a 60s JWT. The post-implementation security review (oracle) confirmed the objective is met at the application layer, but flagged that the OS-level "untrusted workflow code can't read raw keys off the host" property only fully holds under Linux + bwrap. On macOS today:
- Native macOS (no Docker): code-node execution is hard-blocked at setup (
platform/services/environment.py:303 — "macOS requires Docker or another container runtime."). No sandbox exists; there is no sandbox-exec/Seatbelt path (the docstring at platform/components/_agent_shared.py:191 claiming one is inaccurate — separate doc-fix).
- macOS + Docker → "container mode": env-var scrubbing only (
sandboxed_backend.py _build_sandbox_env). No filesystem or PID isolation — a code node / agent shell can still read .env, $AGENTGATEWAY_DIR/keys/*.key, or /proc/<agentgateway_pid>/environ, and (with the shared FIELD_ENCRYPTION_KEY) decrypt the key files.
Net: on macOS there is no real isolation of untrusted workflow code from host secrets. This is the top residual risk from the Phase 1b review.
Proposal
Add Apple container (apple/container, backed by Virtualization.framework / apple/containerization) as the macOS sandbox backend for code-node / agent-shell execution. Each execution runs in its own lightweight Linux VM with its own kernel — the host filesystem and process table simply don't exist in the guest, so secrets are unreachable by construction.
Keep bwrap on Linux (near-free, decade-proven); Apple container is the macOS path only.
bwrap vs Apple container
| Dimension |
bwrap |
Apple container |
| Isolation boundary |
Linux namespaces + seccomp, shared host kernel |
hardware VM, own kernel (Virtualization.framework) |
Vs. our threat (read .env / env / /proc) |
blocked if flags are correct (no default policy) |
blocked by construction |
| Vs. worst-case kernel exploit |
shared-kernel → full escape |
stronger — needs a hypervisor break |
| Platform |
Linux (unprivileged userns) |
Apple Silicon + macOS 26 (no Intel; degraded on 15) |
| Per-exec cost |
~ms, ~0 extra memory |
~0.7s VM boot (anecdotal) + resident kernel/VM |
| Filesystem |
supply a rootfs, --ro-bind |
OCI images; slow unpack of large/many-layer images |
| Networking |
--unshare-net loopback-only default |
routable IP per container; no-network mode unconfirmed |
| Privileges |
fully unprivileged at runtime |
admin install + container system service |
| Maturity |
~10 yrs (Flatpak, toolbox) |
2025, fast-moving, breaking changes pre-1.0 |
Design notes
- Do NOT spin one VM per code-node run — ~0.7s boot + per-VM memory is too costly at scale. Use a warm-pool / VM-reuse design.
- Wire a new
"apple-container" mode into the existing selector (platform/services/environment.py resolution chain) and sandboxed_backend.py::execute().
plit/src/commands/init/prereqs.rs: detect Apple container availability on macOS instead of the current "container (not sandboxed)" fallback.
- Pairs with the broader "container-mode key isolation" hardening (run agentgateway as a separate UID/container; stop passing keys via env; split
AGENTGATEWAY_ENCRYPTION_KEY from FIELD_ENCRYPTION_KEY).
Open questions to verify before building
Acceptance criteria
- A macOS
code node executes inside an Apple container VM and cannot read the host .env, $AGENTGATEWAY_DIR/keys/*, or the host /proc.
- Warm-pool keeps per-exec overhead within an acceptable budget (define target once measured).
- Graceful, clearly-messaged fallback where unavailable (Intel / pre-26 macOS).
References
Context
Phase 1b made agentgateway the sole holder of LLM keys — workflow code now only ever holds a 60s JWT. The post-implementation security review (oracle) confirmed the objective is met at the application layer, but flagged that the OS-level "untrusted workflow code can't read raw keys off the host" property only fully holds under Linux + bwrap. On macOS today:
platform/services/environment.py:303— "macOS requires Docker or another container runtime."). No sandbox exists; there is nosandbox-exec/Seatbelt path (the docstring atplatform/components/_agent_shared.py:191claiming one is inaccurate — separate doc-fix).sandboxed_backend.py_build_sandbox_env). No filesystem or PID isolation — acodenode / agent shell can still read.env,$AGENTGATEWAY_DIR/keys/*.key, or/proc/<agentgateway_pid>/environ, and (with the sharedFIELD_ENCRYPTION_KEY) decrypt the key files.Net: on macOS there is no real isolation of untrusted workflow code from host secrets. This is the top residual risk from the Phase 1b review.
Proposal
Add Apple
container(apple/container, backed byVirtualization.framework/ apple/containerization) as the macOS sandbox backend for code-node / agent-shell execution. Each execution runs in its own lightweight Linux VM with its own kernel — the host filesystem and process table simply don't exist in the guest, so secrets are unreachable by construction.Keep bwrap on Linux (near-free, decade-proven); Apple container is the macOS path only.
bwrap vs Apple
containercontainer.env/ env //proc)--ro-bind--unshare-netloopback-only defaultcontainer systemserviceDesign notes
"apple-container"mode into the existing selector (platform/services/environment.pyresolution chain) andsandboxed_backend.py::execute().plit/src/commands/init/prereqs.rs: detect Apple container availability on macOS instead of the current"container (not sandboxed)"fallback.AGENTGATEWAY_ENCRYPTION_KEYfromFIELD_ENCRYPTION_KEY).Open questions to verify before building
vminitd; don't design around Kata assumptions.Acceptance criteria
codenode executes inside an Apple container VM and cannot read the host.env,$AGENTGATEWAY_DIR/keys/*, or the host/proc.References
docs/architecture/gateway-mcp-design.md; sandbox code:platform/components/sandboxed_backend.py,platform/services/environment.py.