Simlock: Self-hosted simulator cloud for coding agents

Simlock

5 min read Original article ↗

Turn your Mac into a simulator cloud that manages itself.

Simlock leases, serves and manages iOS and Android devices for your coding agents. Start on one Mac, then add machines behind a gateway when you need more. Free and open source.

One machine. Your agents lease its devices three ways:
the simlock CLI, agent-device, or HTTP.

CLIclaude code · on your-mac

$ simlock lease --platform ios

granted iPhone 17 Pro · warm

AGENT-DEVICEcodex · on your-mac

$ agent-device open MyApp --platform ios

streaming iPhone 17 Pro

HTTPci · → your-mac:4701

→ POST /v1/lease-requests

granted Pixel 8

gateway · gw.example

one URL for every agent
picks a machine with room

your-macmacOS

LIVEiPhone 17 Proclaude code

Works with iOS simulators, Android emulators, Physical iPhones, Physical Android phones, macOS workers, Linux workers, CLI, MCP, HTTP API, agent-device.

[01] How it works

Start on one Mac. Grow into a fleet.

  1. 01

    Install

    Install the CLI. The daemon starts on demand, so there is no setup step.

    $ npm i -g simlock
  2. 02

    Lease

    Ask for a platform and a model. You get a booted, checked device no other agent will touch.

    $ simlock lease --platform ios --device "iPhone 16"
  3. 03

    Add machines

    Run one daemon as a gateway, mint a join token, and point each Mac or Linux worker at it.

    $ simlock --gateway token create --role worker

Simlockzsh

simlock lease --platform ios --device "iPhone 16"
simlock simctl install <device.address> ./MyApp.app
simlock release <lease.id>





simlockFor agents on the machine itself. Lease results are one JSON line on stdout.Set up CLI

[02] Fleet

One URL in front of every machine you own.

A gateway owns no devices. Workers dial out to it, so a Mac behind NAT joins with a URL and a token, and each request goes to a warm device first, then to the machine with the most room.

Set up a gateway

Simlockzsh

# on the gateway
simlock --gateway daemon start
simlock --gateway token create --role worker

# on each worker, Mac or Linux
simlock config set gateway '{"url":"wss://gw.example","token":"slk_..."}'
simlock daemon stop
simlock daemon start

[03] Console

Open one page. See who holds what.

The daemon serves the console itself once HTTP is on. Sign in with an operator token, or through the sign-in proxy you already run, and watch every worker, lease and request on the gateway update within about a second.

Read about the console

Overview

  • Leases held3
  • Waiting1
  • Longest wait3s
  • Workers3 of 3

Workersleasedreadybootingfree

WorkerPlatformsDevicesLeases

mac-studio-1iOS · Android1

mac-studio-2iOS · Android1

linux-ci-1Android1

Waiting1 request

1agent-4iPhone 17 Prostarting3s

Leases3 held

agent-114:42iPhone 17 Pro · mac-studio-1

ci-runner-314:51iPhone 17 Pro · mac-studio-2

agent-314:56Pixel 8 · linux-ci-1

ScreenLive

agent-1 iPhone 17 Pro · mac-studio-1

Expires in 14:42Stop

ActivityAll events

  1. 09:41:20lease.requestedagent-4 · iPhone 17 Pro
  2. 09:41:16lease.grantedagent-3 · Pixel 8 · linux-ci-1
  3. 09:41:11lease.grantedci-runner-3 · iPhone 17 Pro · mac-studio-2
  4. 09:41:10device.readyiPhone 17 Pro · mac-studio-2
  5. 09:41:05lease.requestedci-runner-3 · iPhone 17 Pro

Workers

Every worker that joined: its CPU, memory and disk, and a square for each device slot.

Read more

Leases

Who holds which device on which worker, how long it has left, and its screen, live.

Read more

Waiting

Each request still in line, how long it has waited, and why no worker has taken it yet.

Read more

Inbox

Disconnected and drained workers, RAM over budget, quarantined, stalled and absent devices, long waits and leases about to end.

Read more

[04] What you get

Devices that manage themselves, for agents that don’t wait.

One lease, one device

A device on lease belongs to one agent until it releases it or the TTL runs out.

Read more

Devices on demand

Nothing free that fits? Simlock creates one, up to what the machine’s CPU and RAM can hold.

Read more

A fair queue

Past capacity, requests wait their turn instead of failing. Use --timeout or --no-wait to change that.

Read more

Warm devices

Keep the models you use booted and clean, so the first lease is granted without a boot.

Read more

Idle devices cleaned up

An unused device is shut down to free RAM, then deleted to free disk.

Read more

Crash recovery

When a leased device dies, Simlock reboots it under the same lease and tells the holder.

Read more

Physical phones too

Enroll iPhones and Android phones; on release, Simlock uninstalls the apps they did not have.

Read more

Four ways in

The CLI, an MCP server, the HTTP API and a typed client, all on one contract.

Read more

[05] Safe by default

It only touches what it made.

Your devices stay yours

Simlock only shuts down, erases or deletes devices it created. Everything else is read-only to it.

Loopback only by default

The HTTP API and the console listen on loopback only, and both ask for a token. Nothing is reachable from another machine until you change http.host.

Tokens with roles

Agents, operators and workers get their own tokens, hashed at rest and revocable.

Passthrough with limits

simlock simctl and simlock adb refuse verbs that would change a device behind its lease.

[06] FAQ

Frequently asked questions

Does Simlock sandbox my agents?
No. It is advisory: agents are told to use only devices a lease hands them, and Simlock does not intercept simctl or avdmanager. What it does enforce is its own reach: it only shuts down, erases or deletes devices it created.

What happens when an agent dies holding a device?
Every lease has a TTL and lives only while it is renewed. simlock lease renews on a timer and releases when it exits, so a killed holder frees its device at once, and one that vanishes costs the device only until the TTL runs out.

What if every device is busy?
Simlock creates another one, up to a capacity it derives from the machine’s CPU and RAM. After that the request waits in a fair queue, with --timeout and --no-wait for callers that want something else.

Does a worker need an open port to join a fleet?
No. A worker dials out to the gateway over one WebSocket, with the gateway URL and a join token from simlock --gateway token create --role worker, so a Mac behind NAT joins without an inbound port.

Can a Linux machine be a worker?
Yes, for Android. Emulators run headless with android.emulator.headless, as on a Linux CI runner. iOS simulators need a Mac with Xcode.

How do agents on other machines reach it?
Over the HTTP API, with a token: POST /v1/lease-requests on the gateway, then POST /v1/leases/{id}/exec to run simctl or adb on whichever worker holds the device. agent-device can use the gateway as its proxy too.

Get started