Skip to content

Latest commit

 

History

History
77 lines (66 loc) · 4.22 KB

File metadata and controls

77 lines (66 loc) · 4.22 KB

Simlock

Simlock is a control plane for iOS simulators and Android emulators, built for environments where multiple coding agents run in parallel — on one machine, or across a fleet of them behind a single address.

The problem

Agents that need a simulator grab whatever simctl / avdmanager shows them. Two agents that pick the same device start fighting over it — booting, erasing, and installing over each other — without ever knowing the other exists.

The solution

Simlock is a CLI-first control plane (backed by a local daemon) that is the same for both platforms and gives agents one primitive: lease a device. An optional local stdio MCP integration exposes the focused lease/release workflow to compatible agent clients; the CLI remains the full operator interface. An optional, token-authenticated HTTP API lets remote agents lease devices from a self-hosted simlock host over the network (see HTTP-API.md). A host process that wants to allocate devices without spawning a simlock command at all — agent-device, for example — can instead depend on simlock/client/simlock/admin, the typed programmatic client the CLI and MCP frontends are themselves built on (see CLIENT.md).

  • simlock lease returns a ready device — booted and health-checked — that no other agent will touch for the duration of the lease.
  • If no matching device is free, simlock provisions one, up to a configurable capacity limit derived from the machine's CPU and RAM.
  • If the limit is reached, the CLI blocks and waits in a fair queue until a device frees up (with --timeout and --no-wait escape hatches).
  • Devices that sit unused are cleaned up in tiers: shut down after a short idle period (reclaim RAM), deleted after a longer one (reclaim disk).

More than one machine: fleets

One machine's worth of devices runs out eventually, and a team with several Macs should not have to hand each agent a specific one. So a simlock daemon runs in one of two modes. A worker is what every daemon is today — it owns the devices on its machine — and joining a fleet costs it two config keys pointing at a gateway; it keeps serving its own local agents exactly as before. A gateway owns no devices at all: workers dial out to it over a single WebSocket uplink (so a machine behind NAT needs no inbound port), and it fronts them with one fleet-wide queue, dispatching each request to the worker best placed to serve it — a warm device if one is free, otherwise the machine with the most free capacity. Agents and the web console point at one URL and stop caring which machine a device lives on: a gateway speaks the same contract a worker does, so lease, renew, release, status, and even simlock simctl / simlock adb (proxied to the owning worker) are the same commands they always were. The gateway never touches a device itself — the capacity accounting, the registry, and every safety invariant stay on the worker.

Key properties

  • Advisory coordination. Simlock does not sandbox anything. It works because agents are instructed to never call simctl / avdmanager directly and to only use devices handed to them by a lease.
  • TTL leases, one kind everywhere. Every lease carries a TTL and lives until it expires, is renewed, or is released — on the socket, over HTTP, and over MCP alike. simlock lease stays running in the background, renewing on a timer and releasing when it exits, so killing it normally frees the device at once; --detach skips the staying alive and leaves the renewing to you. A holder that dies without releasing costs its device only until the TTL runs out.
  • One lease per agent (v1) — fleet-wide when a gateway issued it.
  • Managed-device registry. Simlock only ever shuts down, erases, or deletes devices it created itself. Everything else on the machine is read-only to it.
  • Agent-first output. CLI lease results are one JSON line on stdout; progress (e.g. provisioning ETAs) streams as JSON lines on stderr. The optional MCP server reserves stdout for MCP JSON-RPC.

See CLI.md for the command surface, CLIENT.md for the programmatic client, and HTTP-API.md for the network API.