manzanas

Drive iOS simulators from Claude Code, Cursor or Codex by accessibility label instead of screenshot-and-tap-coordinates, with leases and a warm pool so multiple agents can share one Mac.

Documentation

manzanas

Release CI License: MIT AllMCPs Verified

A Mac daemon for multi-agent iOS simulator fleet orchestration: leases, actions, streaming, deterministic state, and an exportable run journal for AI agents (and humans) sharing simulators.

https://github.com/user-attachments/assets/deb577d0-1123-4a89-90d8-8d9c7684a3cf

The launch film — leases, actions, live streaming, warm pools, the broker, and the run journal in 52 seconds. Sound on.

manzanasd runs on each Mac host and owns everything stateful — the simulator registry, the lease table, the warm pool, action backends, streamers, golden images, and the run journal. Clients (manzanas CLI, MCP facade, SDKs) are thin and cross-platform, speaking a versioned JSON protocol over HTTP + WebSocket. manzanas-broker federates N daemons behind one endpoint — start with one Mac, and when you outgrow it the same clients lease across the whole fleet without changing a line.

manzanas CLI demo: two agents lease warm-pool simulators, drive one, and export journal evidence

Two agents lease pre-warmed simulators — parked sims thaw on grant instead of paying a 5 s+ boot — then drive one (warm taps land in ~50 ms end-to-end) and export the run journal as PR-ready evidence.

Website: manzanasgo.vercel.app (source in site/).

Install

brew tap baribarigood/tap https://github.com/BariBariGood/homebrew-tap
brew trust baribarigood/tap      # Homebrew >= 6 requires trusting third-party taps
brew install manzanasd            # daemon (+ pulls in the manzanas CLI)
brew services start manzanasd     # launchd service on port 7433

See docs/install.md for launchd tarball installs and alternatives, and docs/quickstart.md for a single-Mac walkthrough from install to first screenshot.

Why

Agents driving simulators over raw SSH + CLI tools trip over each other and pay huge fixed costs. manzanasd removes both, with measured numbers (M3 Pro, macOS 26.5, Xcode 26.5; reproduce with make bench):

  • Leases, not locks: TTL-bounded exclusive claims with FIFO queues — no two agents ever drive the same sim.
  • Park/thaw warm pool: idle sims are SIGSTOPped (a parked tree is unschedulable — ~0 idle host CPU no matter what the sim's daemons are doing) and thawed on lease grant: ~0.28 s lease-to-live vs ~7 s for a cold boot (~29 s first boot). The thaw itself is a cached-PID SIGCONT and takes under a millisecond.
  • Warm actions: a resident per-sim helper makes an end-to-end tap ~36 ms vs ~950 ms cold (per-action AXe spawn) — ~3 s cold on Intel.
  • Deterministic state: snapshots, fixtures, per-lease auto-reset, and golden images that stamp out slimmed sims in seconds (~0.75 GB vs ~5 GB stock, via simslim) — how one Mac runs a dozen sims.
  • Evidence: every mutating op under a lease is journaled, with content-addressed artifacts and a PR-ready markdown export.
LeasesTTL-bounded exclusive claims, labels, FIFO queues, auto-reset
Warm poolpark/thaw (SIGSTOP) pool sims: ~0.28 s lease-to-live, ~0 idle CPU
Actionscold (AXe) + warm (resident helper) taps/swipes/typing, composite tap_element with a structured predicate DSL, batches
Auditdeterministic UI checks (touch targets, clipping, alignment, spacing, safe area, missing labels) → findings + annotated screenshot in the journal
StreamingMJPEG fan-out, browser /view page, WS frames
Videoper-lease simctl recordings that land in the journal
Statesnapshots, fixtures, per-lease auto-reset, golden images
Journalappend-only evidence per run, artifacts, export.md
Dashboardread-only web dashboard at /dash: fleet, leases, multiview, journal browser
Devicesphysical iPhones via devicectl + WebDriverAgent (--devices)
Fleetmanzanas-broker federates N Macs behind one endpoint
Clientsmanzanas CLI, MCP tools over stdio, npm wrapper, GitHub Action
Live MJPEG view of a leased simulator at /viewBuilt-in fleet dashboard at /dash
the browser /view MJPEG live pagethe built-in /dash fleet dashboard

Quickstart

New here? docs/quickstart.md walks through a full single-Mac session (install → lease → tap → screenshot). The short version:

make build            # builds bin/manzanasd, bin/manzanas, bin/manzanas-broker

# on a Mac with Xcode:
./bin/manzanasd --addr :7433

# anywhere (Linux/dev/CI), with a mock fleet:
./bin/manzanasd --addr :7433 --mock

# list simulators
curl -s localhost:7433/v0/targets | jq

# lease one by label, boot it, release it
curl -s -X POST localhost:7433/v0/leases \
  -d '{"labels":["ios26"],"agent_id":"me","ttl_seconds":300}' | jq
curl -s -X POST localhost:7433/v0/targets/<udid>/boot -d '{"lease_id":"<id>"}' | jq
curl -s -X DELETE localhost:7433/v0/leases/<id> | jq

Or the thin client (--json for machine output; see clients/):

manzanas targets
manzanas lease acquire --labels ios26 --agent me --wait
manzanas tap 200 400 --lease lse_...
manzanas mcp        # lease-scoped MCP tools over stdio for agents

Production installs (launchd, releases): docs/install.md.

MCP (Claude Code, Cursor, Codex)

manzanas mcp serves the whole toolset over the Model Context Protocol (stdio), so any MCP-capable agent can lease and drive simulators — with per-session auto-release of leases and self-describing tool errors. Ready-to-paste client configs and troubleshooting: docs/mcp.md.

claude mcp add manzanas -e MANZANASD_ADDR=mac-host:7433 -- /path/to/manzanas mcp

Architecture

┌ Linux / CI / anywhere ────────────┐        ┌ each Mac host ─────────────────────────────┐
│ manzanas (thin client, Go)         │   WS   │ manzanasd (Go daemon)  :7433                │
│  - CLI: lease/tap/observe/...     │◄──────►│  registry ── warm pool (park/thaw, gates)  │
│  - MCP facade (stdio)             │  HTTP  │  leases (TTL, labels, FIFO, auto-reset)    │
│  - eval harness (manzanas-eval)    │        │  actions ── cold AXe / warm simbridge      │
├───────────────────────────────────┤        │  streams (MJPEG fan-out, browser view)     │
│ manzanas-broker  :7440             │        │  state (snapshots, fixtures, golden images)│
│  fleet-wide placement: leases are │───────►│  journal (evidence, artifacts, export.md)  │
│  scheduled across N daemons, then │ probe/ └────────────────────────────────────────────┘
│  clients talk to the owning       │ lease         × one daemon per Mac in the fleet
│  daemon directly (host_addr)      │
└───────────────────────────────────┘

Documentation

Protocol:

  • proto/PROTOCOL.md — the v0 wire protocol: targets, leases, actions (incl. composite tap_element/type_into_element and actions:batch), streams, state, images, journal, WS surface.

Subsystems:

Operations:

The physical fleet this runs on (machines, locks, build caching) is site-specific; docs/fleet.md covers the daemon's part.

Status

Everything above is implemented and running on the fleet — leases + queues, warm pool with safety gates, cold (AXe) + warm (simbridge) action backends, composite/batch actions, MJPEG streaming, video capture, snapshots/fixtures/auto-reset, golden images, journal, dashboard, physical-device support, broker, eval harness, CLI + MCP. Verify with go build ./... && go vet ./... && go test ./... (Linux-safe; simctl paths are mocked).

License

MIT — see LICENSE.