Skip to content

Phase 4 · macOS code-node sandbox via Apple container (close the container-mode key-exposure gap) #190

Description

@theuseless-ai

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

  • Does Apple container offer a no-network / loopback-only mode for code nodes? (unconfirmed)
  • Real per-exec boot latency + per-VM memory (Apple publishes none; ~0.7s is a single anecdote).
  • Does the run path require a persistent privileged daemon?
  • Fleet check: Apple Silicon + macOS 26 (Tahoe) hard requirement. (Dev machine is already arm64 / macOS 26.5.1 ✓.)
  • Dedicated CVE pass on Virtualization.framework guest-escape before relying on the VM boundary in security docs.
  • Note: the "powered by Kata Containers" claim circulating appears false — Apple uses a custom vminitd; don't design around Kata assumptions.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions