Summary
Add macOS hypervisor support to cocoon by wrapping Tart CLI as the macOS backend, alongside the existing Cloud Hypervisor backend on Linux. This enables cocoon to manage Linux and macOS VMs on Apple Silicon.
Backend Evaluation
| Candidate |
Language |
Go Integration |
Linux Guest |
macOS Guest |
GPU |
Verdict |
| Code-Hex/vz |
Go |
Native library |
✅ |
✅ (arm64) |
PVG |
In-process VM — cocoon exit = VM death, needs daemon or fork wrapper |
| tart |
Swift |
CLI wrapper |
✅ |
✅ |
PVG |
Recommended — external process like CH, simplest integration |
| libkrun/krunvm |
Rust |
CLI only |
✅ |
❌ |
✅ Metal |
No macOS guest support |
| Lima |
Go |
Not embeddable |
✅ |
❌ |
❌ |
Full VM manager, too much overlap with cocoon |
Why Tart over Code-Hex/vz
Code-Hex/vz is a native Go binding for Apple's Virtualization.framework with full PVG support (MacGraphicsDeviceConfiguration, VirtioGraphicsDeviceConfiguration). However, VF VMs are in-process Go objects — the VM dies when the process exits. This forces one of:
- (A) cocoon becomes a long-running daemon (major architecture change)
- (B) fork a
cocoon _vm-runner subprocess per VM (reinventing tart)
Tart solves this naturally: tart run is a standalone process (like cloud-hypervisor), tracked via PID file. cocoon's existing fork + PID file + signal architecture maps 1:1.
Graphics & Remote Access
macOS guests on Apple Silicon get GPU acceleration via ParavirtualizedGraphics.framework:
- Guest sees a Metal GPU device (PVG — not CPU software rendering)
- Metal calls are forwarded to the host's real GPU via virtio
- Performance is near-native
For headless remote access:
| Method |
How |
Performance |
Recommendation |
| macOS Screen Sharing (VNC) |
Built-in, enable via launchctl |
Adequate for daily use |
Default option |
| Apple Remote Desktop |
Enhanced VNC + management |
Better than raw VNC |
Enterprise |
| Sunshine + Moonlight |
GPU-encoded H.265 streaming |
<10ms latency, 60fps |
Best for interactive use |
cocoon does not need to handle display at the hypervisor layer — the VM runs Screen Sharing or Sunshine internally.
Networking
macOS has no CNI/netns. Tart uses macOS vmnet.framework:
| Mode |
Command |
IP Assignment |
Isolation |
Use Case |
| Shared (NAT) |
tart run (default) |
macOS DHCP, 192.168.64.x |
Low |
Development |
| Bridged |
tart run --net-bridged=en0 |
LAN DHCP, same subnet as host |
None |
Production — VM directly reachable |
| Softnet |
tart run --net-softnet |
macOS DHCP |
High, inter-VM isolation |
Multi-tenant |
cocoon's macOS network provider is thin:
- Does NOT create/manage network resources (macOS vmnet handles everything)
- Passes
--net-bridged=<iface> to tart run (configurable interface)
- Queries IP post-boot via
tart ip <vm-name>
- Delete is a no-op (macOS auto-reclaims vmnet resources)
For remote access to headless Mac hosts: use bridged mode so VMs get same-subnet IPs, directly reachable from the network.
Architecture
hypervisor/
├── hypervisor.go # Hypervisor interface (unchanged)
├── cloudhypervisor/ # Linux backend (unchanged)
└── tart/ # NEW: macOS backend
├── tart.go # Hypervisor interface implementation
├── create.go # tart clone/create
├── start.go # tart run --no-graphics --net-bridged=en0 &
│ # fork + PID file (same pattern as CH)
├── stop.go # tart stop <name>
├── console.go # tart ip → VNC connect, or SSH
└── gc.go # GC module
network/
├── network.go # Network interface (unchanged)
├── cni/ # Linux CNI (unchanged)
└── tart/ # NEW: macOS no-op network
└── tart.go # Thin wrapper — delegates to macOS vmnet
images/
├── images.go # Images interface (unchanged)
└── tart/ # NEW: delegates to tart CLI
└── tart.go # tart pull/list/delete wrapper
Key design: tart backend mirrors CH backend
| Aspect |
Cloud Hypervisor (Linux) |
Tart (macOS) |
| Process model |
fork cloud-hypervisor |
fork tart run |
| Process tracking |
PID file + /proc |
PID file + kill(pid, 0) |
| Stop |
API socket → SIGTERM → SIGKILL |
tart stop → SIGTERM |
| Console |
Unix socket / PTY |
VNC to guest IP |
| Network |
CNI + netns + TAP + TC redirect |
macOS vmnet (bridged/NAT) |
| Images |
Custom OCI pull + EROFS convert |
tart pull from OCI registry |
| Graphics |
N/A (serial/VNC) |
PVG Metal acceleration |
Prerequisite changes
- Add
HypervisorBackend field to config.Config (e.g., "cloud-hypervisor", "tart")
- Make
cmd/core/helpers.go InitHypervisor() a factory with switch conf.HypervisorBackend
- Platform-aware defaults in
DefaultConfig() (Linux → CH, Darwin → tart)
- Network interface: skip netns on macOS (tart network provider is a no-op)
- Existing
Hypervisor interface needs no changes
References
Summary
Add macOS hypervisor support to cocoon by wrapping Tart CLI as the macOS backend, alongside the existing Cloud Hypervisor backend on Linux. This enables cocoon to manage Linux and macOS VMs on Apple Silicon.
Backend Evaluation
Why Tart over Code-Hex/vz
Code-Hex/vz is a native Go binding for Apple's Virtualization.framework with full PVG support (
MacGraphicsDeviceConfiguration,VirtioGraphicsDeviceConfiguration). However, VF VMs are in-process Go objects — the VM dies when the process exits. This forces one of:cocoon _vm-runnersubprocess per VM (reinventing tart)Tart solves this naturally:
tart runis a standalone process (likecloud-hypervisor), tracked via PID file. cocoon's existing fork + PID file + signal architecture maps 1:1.Graphics & Remote Access
macOS guests on Apple Silicon get GPU acceleration via ParavirtualizedGraphics.framework:
For headless remote access:
launchctlcocoon does not need to handle display at the hypervisor layer — the VM runs Screen Sharing or Sunshine internally.
Networking
macOS has no CNI/netns. Tart uses macOS vmnet.framework:
tart run(default)tart run --net-bridged=en0tart run --net-softnetcocoon's macOS network provider is thin:
--net-bridged=<iface>totart run(configurable interface)tart ip <vm-name>For remote access to headless Mac hosts: use bridged mode so VMs get same-subnet IPs, directly reachable from the network.
Architecture
Key design: tart backend mirrors CH backend
cloud-hypervisortart runtart stop→ SIGTERMtart pullfrom OCI registryPrerequisite changes
HypervisorBackendfield toconfig.Config(e.g.,"cloud-hypervisor","tart")cmd/core/helpers.goInitHypervisor()a factory withswitch conf.HypervisorBackendDefaultConfig()(Linux → CH, Darwin → tart)Hypervisorinterface needs no changesReferences