feat: add macos container backend scaffold

This commit is contained in:
2026-06-10 18:14:17 +00:00
committed by didericis
parent b00b0ba4aa
commit fe82dc7f2b
16 changed files with 919 additions and 3 deletions
@@ -0,0 +1,159 @@
# 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 <ref> [-f Dockerfile] <context>` |
| Run agent commands | `container exec [--interactive --tty] <id> ...` |
| Copy files into guest | `container cp <host> <id>:<path>` |
| Inspect image identity | `container image inspect <ref>` |
| Cleanup stale containers | `container delete --force <id>` |
| Cleanup stale networks | `container network delete <name>` |
| 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.