Fleets and VMs

The safest place for an agent to do risky work is a Mac you can throw away. The single-host half of that shipped: macos-mcp vm clones a golden macOS image, runs the clone on this Mac and destroys it. The hub — the part that turns many Apple Silicon hosts into one pool behind the same MCP config entry as your own Mac — is still in development.

Status: half of this ships today. Shipped in v0.2.0 (2026-09-07): single-host macOS VMs on native Virtualization.framework — macos-mcp vm clone, start, stop, destroy, status, screenshot, ip, input, grant and verify, as CLI subcommands rather than MCP tools. In development: the hub — machine registry and enrollment, leases, the machine parameter that routes any tool to any Mac, display streams and the orchestrator API. That is the project's Phase 3: designed, prototyped and measured on macOS 27, with the working notes in the repository's knowledge/ directory, but not in a release. The single-Mac server on the rest of this site needs none of it.

Shipped today: one host

Everything here runs on a single Apple Silicon Mac, from your shell, with no daemon and no enrollment. Disposable macOS VMs is the hands-on page — commands, output and the failure modes.

These are CLI subcommands, not MCP tools: the frozen tool surface is unchanged at 44 tools, 24 on by default.

In development: the hub

What we measured on macOS 27

Most of what is written about macOS virtualization is aspirational. These were verified on a macOS 27 beta host running macOS 27 guests, and they shape the design — and now the shipped code:

FindingConsequence
Two macOS VMs per Apple Silicon host, and it is a kernel quota. The third is refused instantly with VZErrorDomain code 6 while 39% of RAM is free.Fleet capacity is hosts × 2. More RAM never helps; multi-host scheduling is the answer. Linux guests are uncapped. Shipped: the driver refuses the over-quota clone or start with a typed error, so a scheduler can route elsewhere instead of retrying into a wall.
A guest renders nothing unless a view is attached and the host session is logged in and unlocked. Headless, locked and lost-WindowServer all produce an all-zero framebuffer.UI automation in a VM depends on the host's session. A locked host blacks out every VM on it, so hosts get a specific profile: never lock, never sleep. Shipped: a running VM is a window in the console user's session, launched there through launchctl asuser when the caller is not already in it.
Provisioning does not finish the job. Skipping Setup Assistant still leaves the per-user first-login wizard.The golden-image recipe pre-suppresses it with a verified key set — including the managed SetupAssistant domain, which is what actually silences the Apple Account pane — so a clone boots to a usable desktop.
Exactly one entitlementcom.apple.security.virtualization. Over-entitling is fatal: an unauthorized networking entitlement is killed at exec with no crash report.The shipped binary carries that one key and nothing else, under its Developer ID signature; the VM runner is the same binary, launched into the console session. A build from source needs Scripts/sign-dev.sh once per build to get the entitlement.
Process health means nothing. Every failure mode presented as a healthy process with a black screen.Health checks sample the framebuffer for non-black content from the host and watch for WindowServer port death. Shipped: vm screenshot reports a non-black fraction, and vm verify wakes a slept guest display before it samples, because a healthy guest that idled into display sleep also captures black.
An ungranted guest has no input channel. Host-synthesized events never reach a guest; a guest's own screen sharing serves black and discards input; its TCC database is SIP-protected; a manual PPPC profile grants nothing. Six routes measured and ruled out.The accessibility grant costs one physical click per golden image, once. Clones inherit it. That is honest, and it is the design — vm grant around the click, vm verify after it.

The full write-ups, with build numbers and the measurement recipes, are in the repository: knowledge/README.md.

Why a VM, not a sandbox

Agents make mistakes at machine speed. A confirmation token catches the destructive call you anticipated; a disposable clone catches the one you did not. Risky work — untrusted downloads, installers, “try this script”, a hostile web page — runs on a machine whose entire state is a clone you delete afterwards, while your own Mac's grants stay untouched. The same agent in a VM also gateways to that VM's Safari MCP, so browser work is isolated too.

Roadmap

  1. Done — VM lifecycle on native Virtualization.framework, behind the VMDriver protocol: clone, start, stop, destroy, capture and input on one host (v0.2.0).
  2. Done — golden-image TCC: one physical click per image, none per clone — vm grant around the click, vm verify as the per-clone acceptance.
  3. Hub daemon: machine registry, enrollment, leases, routing via the machine parameter, central audit.
  4. Golden-image build and clone pipeline as a first-class command, from the verified recipe.
  5. Display streams and the orchestrator API that Codeman consumes.

Progress is tracked in IMPLEMENTATION.md. If you run Mac fleets today and want to shape this, open a discussion. For the half that already runs, start at disposable macOS VMs.