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.
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.
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). With HTTP on, the daemon also serves a web console
for operators, signed in with an operator token (see
CONSOLE.md).
simlock leasereturns 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
--timeoutand--no-waitescape hatches). - A phone or tablet an admin enrolled is leased the same way, with
simlock lease --physical: one agent at a time, and on release every app that is not on the device's enrollment list is uninstalled, so the next agent starts from the apps it was enrolled with. Simlock never creates, erases or destroys such a device. - 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).
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 healthy, unqueued 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.
- Advisory coordination. Simlock does not sandbox anything. It works
because agents are instructed to never call
simctl/avdmanagerdirectly 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 leasestays running in the background, renewing on a timer and releasing when it exits, so killing it normally frees the device at once;--detachskips 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 by default — raise
lease.maxPerRequesterto allow more; 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, HTTP-API.md for the network API, and CONSOLE.md for the web console.