Busymate · iOS farm · How-to

Pairing a new iPhone to the Busymate farm

From plugged into the host to in control & capturing decrypted traffic — the real tool-backed path, with the evidence to check at every step.

ios farm host required devices:edit host opt-in ON

The path at a glance

1connect 2provision 3control 4sign in 5capture 6verify
Step 1 is the only hands-on-hardware moment; everything after runs over the tools.

The six steps

1

Physically connect the iPhone to the host

Plug it into the ios-host Mac by USB (or its dock/hub) and tap Trust This Computer on the phone. The farm daemon on that Mac watches for newly-enumerated devices.

The phone shows up in ios_list_phones / farm_mobile_list_devices before you move on.
2

Provision the phone's route-key device row

The host mints a UUID route-key and creates the child ios device row (model + iOS version) via provision_ios_phone — normally by itself, the moment it enumerates the phone.

This RPC re-checks the caller is the paired ios-host itself — invoking it by hand as a human/MCP caller is refused by design.
list_ios_phones now lists it as a child of the host with is_farm_child:true.
3

Onboard the control engine (devicekit)

Install the on-device XCUITest control runner over the wire with setup_ios_devicekit{ child_uuid, confirm:true } — the host builds, re-signs and installs it via devicectl, no host terminal needed. A build takes a few minutes; WebDriverAgent serves the phone in the meantime.

Watch list_ios_phones until engine flips to devicekit.
4

Sign the DevTools app in, hands-free

Auto-enroll the Busymate DevTools capture app with enroll_farm_phone_app{ child_uuid, confirm:true }: the host mints a PKCE pair, signs an HMAC assertion for the phone's route-key, and opens a one-shot federation deep link on the device. The app exchanges it, persists the session, then chains straight into device-pair.

The federated account is a capped-viewer identity scoped to the phone — it can never claim an existing admin/native account. opened only proves the deep link fired, not that sign-in landed.
Confirm the app actually reached signed-in + paired on screen before continuing.
5

Enroll it for decrypted capture

One confirm — enroll_device_capture{ scope:"all_hosts"|"hosts", confirm:true } — sets the connection type (VPN), turns on inspect-all-requests, applies the decrypt scope, and starts the tunnel (the consolidated version of the old 4-call chain).

On the phone: approve the VPN "Allow" prompt, and make sure the MITM root cert is installed & trusted (Settings → General → VPN & Device Management, then About → Certificate Trust Settings).
6

Verify it's really in control & capturing

Don't take "configured" for "working" — read the honest evidence back with get_farm_phone_health + get_device_health.

rollup

fully-in-control · degraded · blocked

engine_state

armed · inert (+ why)

capture.rung

configured → proxy_unreachable / ca_untrusted → ok

ca_loaded / ca_trusted

profile installed + trusted

A device with no telemetry yet is a normal "configured" result — never reported "ok" without decrypted-flow evidence.

If a step gets stuck — the recovery ladder

Try these in order before touching hardware again.

RungToolWhat it does
1farm_retry_phoneForce an immediate reconcile + control re-attempt (clears backoff / circuit-breaker).
2farm_restart_tunnelRecycle a wedged per-phone go-ios tunnel.
3farm_reap_tunnelsHost-wide: free orphaned tunnels squatting on ports (a per-phone restart won't clear these).
4restart_farm_daemonRecycle the whole daemon supervisor — phones stay running, only remote control blips.
5power_cycle_farm_hubLast resort: cut + restore power to the whole USB rack. Every phone on the hub briefly drops — only when several are stuck at needs_power_cycle.

Name it, then drive it

Once paired, drive and query the phone by its display name (e.g. "BMDEV0") — get_device resolves an exact name or an unambiguous label prefix in one call. The route-key UUID only ever goes in tool arguments, never in conversation.