ADR-0003: Pluggable Local Container Runtime Abstraction
Context and Problem Statement
When a human submits feedback, Stet dispatches an AI agent to do the work. That agent must run somewhere isolated: it executes arbitrary tool calls (shell, file writes, test runs) against a checkout of the user's repository. For privacy, cost, and the product's "local" thesis, this must happen on the user's Mac, in a sandbox, never in our cloud.
Two credible local runtimes exist on macOS 26 + Apple silicon: Apple's
container / Containerization framework (per-container lightweight VMs, sub-second
starts, license-free, in-process Swift API) and Docker (ubiquitous, what most
open harnesses target out of the box). We must decide how to depend on them.
Decision Drivers
- Isolation strength. Each dispatched agent should get a fresh, disposable environment with its own filesystem and network boundary.
- Native-first, no forced third-party install. Users shouldn't be required to install Docker Desktop to use Stet.
- Compatibility with existing harnesses. Many harnesses (ADR-0004) assume a Docker-compatible runtime; we shouldn't strand them.
- Future flexibility. The runtime is an implementation detail that will change; the rest of the app should not couple to it.
- Security. Credentials and repo contents inside the sandbox must be scoped and revocable.
Considered Options
- Option A: Apple
containernative default, Docker fallback. - Option B: Pluggable runtime abstraction — Apple
containerand Docker both first-class behind one protocol, no privileged default. - Option C: Docker-first, add Apple
containerlater.
Decision Outcome
Chosen option: Option B — a pluggable ContainerRuntime protocol with Apple
container/Containerization and Docker as equal, first-class backends. The
application depends only on the protocol (create sandbox, mount worktree, run
process, stream I/O, tear down); backends are selected by availability and user
preference. Apple's Containerization Swift package is embedded in-process where
available (ADR-0001); Docker is driven via its API/CLI.
A pluggable abstraction from day one is worth the extra upfront design because the runtime is the single most volatile external dependency, and neither backend should be privileged in the codebase.
Consequences
- Good, because the app never couples to a specific container implementation.
- Good, because users with neither preference get native, license-free execution; users invested in Docker keep their setup.
- Good, because per-dispatch sandboxes (fresh VM/container + isolated worktree + scoped credential) give strong, disposable isolation.
- Bad, because two backends must be built and tested from the start (more upfront work than a single default).
- Bad, because Apple Containerization is a 1.0 framework; edge cases (networking, entitlements) will need workarounds.
- Neutral, because a capability-detection layer is required to pick a backend and degrade gracefully when neither is present.
Confirmation
- A
ContainerRuntimeprotocol exists; no app code outside its implementations references Applecontaineror Docker types directly. - Both backends can: create a sandbox, mount a git worktree, run a process, stream stdout/stderr, and tear down.
- Each dispatch runs in a fresh sandbox with a branch-scoped credential that is revoked on teardown.
- With no runtime available, the app surfaces a clear, actionable error rather than failing opaquely.
Pros and Cons of the Options
Option A: Apple native-first, Docker fallback
- Good, simplest "it just works" default.
- Bad, implies a privileged backend in code; Docker path risks being a second-class afterthought.
Option B: Pluggable, both first-class
- Good, no coupling, no privileged backend, cleanest long-term.
- Bad, most upfront work.
Option C: Docker-first
- Good, fastest path to a working agent against existing harnesses.
- Bad, forces Docker Desktop dependency; least differentiated; contradicts the native thesis (ADR-0001).
More Information
Apple Containerization: https://github.com/apple/containerization (Swift package; modules include Containerization, ContainerizationOCI, ContainerizationEXT4). See ADR-0004 (harness adapter that runs inside these sandboxes) and SPEC-0003.