# PRD prd-new: macOS Container backend - **Status:** Draft - **Author:** Codex - **Created:** 2026-06-10 - **Issue:** #220 ## Summary Add an experimental `macos-container` backend that integrates Apple's `container` CLI as a host runtime on macOS. The first shipped slice registers the backend, implements the reusable host primitives (`build`, `exec`, `cp`, image inspection, cleanup, active enumeration), and blocks full launch behind an explicit network enforcement guard. This creates a real integration point without weakening bot-bottle's sidecar egress model. ## Problem bot-bottle currently has two local execution paths: - `docker`, which runs the whole bottle topology through Docker Compose. - `smolmachines`, which runs the agent in smolvm but still depends on Docker for the sidecar bundle and image-building pipeline. Issue #220 explored removing Docker as a host dependency. A follow-up comment verified that smolvm can publish guest ports back to host loopback and that another smolvm guest can reach that service through the existing per-bottle loopback alias plus `--allow-cidr` path. That keeps the VM-contained sidecar direction viable and rejects the host-process sidecar fallback. Apple's `container` CLI is another macOS-native way to run OCI images as lightweight Linux VMs. Its current command surface includes Docker-like `build`, `run`, `exec`, `cp`, port publishing, image inspection, and user-defined networks. That makes it a plausible local backend, but it does not remove the need to preserve bot-bottle's sidecar enforcement property: the agent must not have a direct egress path around the egress sidecar. ## Goals / Success Criteria - `--backend=macos-container` and `BOT_BOTTLE_BACKEND=macos-container` are accepted by the existing backend selector. - Backend availability is true only on macOS hosts with `container` on `PATH`. - The backend has tested wrappers for Apple Container image build, image inspection, container `exec`, container `cp`, cleanup, and active-agent enumeration. - Full launch fails loudly with an operator-facing message until the sidecar network enforcement design is implemented. - The PRD records the remaining launch work so the next PR can make the backend runnable without revisiting registration or wrapper plumbing. ## Non-goals - Do not remove or deprecate the Docker backend. - Do not change the default backend from `smolmachines`. - Do not run sidecar daemons as host processes. - Do not launch a degraded backend where the agent can bypass the egress sidecar through direct network access. - Do not require Docker Desktop as part of the macOS Container backend. ## Design ### Backend name The selectable backend name is `macos-container`. The Python package uses `bot_bottle.backend.macos_container` because module names cannot contain hyphens. ### Availability and preflight `MacosContainerBottleBackend.is_available()` returns true only when: - `platform.system() == "Darwin"` - `container` is discoverable on `PATH` `prepare()` calls `require_container()`, which produces a concrete install pointer and rejects non-macOS hosts. ### Implemented primitives The backend owns an Apple Container wrapper module instead of reusing Docker wrappers. The wrapper maps bot-bottle's backend needs to Apple's CLI: | bot-bottle need | Apple Container command | |---|---| | Build provider image | `container build -t [-f Dockerfile] ` | | Run agent commands | `container exec [--interactive --tty] ...` | | Copy files into guest | `container cp :` | | Inspect image identity | `container image inspect ` | | Cleanup stale containers | `container delete --force ` | | Cleanup stale networks | `container network delete ` | | Active enumeration | `container list --quiet` | The bottle handle mirrors `DockerBottle`: it builds a host argv for foreground agent execution, pipes shell snippets through stdin for `Bottle.exec`, and exposes `cp_in` for provisioning. ### Launch guard `launch()` is intentionally not enabled in the first slice. It exits with a fatal message explaining that sidecar network enforcement still needs implementation. This is deliberate. A runnable backend that places the agent on a normal outbound network while relying on environment variables for proxying would violate bot-bottle's egress model. The runnable version must prove one of these shapes: - Apple Container supports the equivalent of Docker's two-network sidecar topology: agent on an internal-only network, sidecar on both internal and egress networks. - The sidecar bundle runs as a separate VM/container with published loopback ports, and the agent runtime can be constrained to only reach that per-bottle loopback alias. - Apple Container init/network hooks can enforce the egress sidecar as the only outbound path before the agent process starts. ## Implementation chunks 1. Register `macos-container`, add availability/preflight, bottle handle, utility wrappers, cleanup, active enumeration, unit tests, and this PRD. 2. Spike Apple Container networking against real macOS 26 hosts: repeated `--network`, internal network egress behavior, published loopback reachability from another container, DNS behavior, and labels/JSON output stability. 3. Implement launch once the enforcement shape is proven. Reuse the existing sidecar bundle image and daemon subset env contract where possible. 4. Add real-runtime integration tests guarded by `container` presence and macOS version. 5. Consider moving smolmachines sidecar/image-building work to VM-contained or Apple Container-backed execution only after the `macos-container` launch path is trustworthy. ## Testing Strategy - Unit tests cover backend registration through `known_backend_names`. - Unit tests cover availability/preflight behavior without requiring macOS. - Unit tests cover `MacosContainerBottle` command construction and stdin-based shell execution. - Unit tests cover cleanup and active enumeration parsing. - Future integration tests must run on a host with Apple Container installed and should verify egress cannot bypass the sidecar. ## References - Issue #220 comment: smolvm `--port/-p` can expose a guest service to host loopback, and another smolvm guest can reach it through the existing per-bottle loopback alias path. - Apple Container command reference: `container run`, `build`, `exec`, port publishing, and network commands.