iOS Device Driver¶
jumpstarter-driver-iosdevice connects existing iOS tools to one leased iPhone
or iPad through usbmux. The exporter owns device pairing; clients use a
lease-scoped proxy identity. Device tools such as go-ios and libimobiledevice
are installed separately.
Installation¶
$ pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple jumpstarter-driver-iosdevice
Choose the tools needed for your workflow. Install these tools separately under their respective licenses; Jumpstarter does not bundle or download them.
Tool |
Common use |
|---|---|
Device management and automation |
|
Device utilities and libraries |
|
Access a remote usbmux endpoint from local tools |
The exporter needs a running usbmux daemon and a device running iOS 17.4 or later. Pair the device on the exporter before leasing it. Developer services also require Developer Mode, a compatible mounted developer image, and any on-device approvals requested by your tools. Jumpstarter does not configure these prerequisites or provide physical power control.
Preparing a physical device¶
Do this once per device, on the exporter host, with the device unlocked:
Connect the device over USB. When iOS asks Trust This Computer?, tap Trust and enter the device passcode. This creates the pairing record the exporter owns; clients never receive it.
Enable Developer Mode: Settings > Privacy & Security > Developer Mode, turn it on and restart when asked. After the restart, swipe up to unlock and tap Enable in the prompt, then confirm with the passcode. If the toggle is not shown, connect the device to Xcode or a developer tool once so iOS offers it.
Verify from the exporter host with libimobiledevice:
idevice_id -llists the UDID andidevicepair validateconfirms the pairing.
Keep the device unlocked while tools install or launch test runners; iOS may ask for further approvals on screen.
Configuration¶
export:
ios:
type: jumpstarter_driver_iosdevice.driver.IosDevice
config:
usb_port: "1-4.2"
http_port: 8100
forward_ports: [9100]
On Linux, usb_port is the device’s USB topology name under
/sys/bus/usb/devices. It selects the bench port, so replacing the device does
not require changing the configuration. On macOS, or to select a particular
device, use udid instead. USB transport requires exactly one selector.
For an operator-provided remote usbmux daemon, set transport: network,
usbmux_host and udid. The endpoint must speak usbmux; it is not the device’s
own IP address. Protect the network connection to the remote daemon separately;
that connection uses plain TCP.
Config parameters¶
Parameter |
Description |
Type |
Required |
Default |
|---|---|---|---|---|
|
Local USB daemon or remote usbmux daemon: |
str |
no |
|
|
Linux USB topology name, for example |
str |
one USB selector |
— |
|
Device identifier |
str |
one USB selector; required for network |
— |
|
Local usbmux daemon socket |
str |
no |
|
|
Remote usbmux daemon host |
str |
for network |
— |
|
Remote usbmux daemon port |
int |
no |
|
|
|
str |
no |
|
|
Device ports available as raw TCP forwards |
list[int] |
no |
|
|
Device HTTP service to expose through verified HTTPS |
int |
no |
— |
|
Discovery and connection timeout, in seconds |
float |
no |
|
|
Target label: |
str |
no |
|
|
Let macOS clients show the device in Xcode’s Device Hub |
bool |
no |
|
|
File that keeps the Device Hub identity and paired Macs |
str |
no |
— |
|
UDID the device presents in Device Hub |
str |
no |
the device’s UDID |
http_port and devicehub require exporter trust mode. http_port must not
also appear in forward_ports. The operator starts the HTTP service. Lockdown port 62078
cannot be configured as http_port or forwarded raw in exporter mode.
Usage¶
The commands below assume an export named ios inside a jmp shell session.
j ios info
j ios serve
j ios -- idevice_id -l
j ios connect
j ios forward 9100
j ios https
j ios devicehub # macOS clients only
serve prints a loopback address and USBMUXD_SOCKET_ADDRESS for your tool.
Keep it running while the tool uses the device. The -- COMMAND... form sets
that variable for one command. connect registers the address with your
installed usbfluxd using usbfluxctl, then removes its registration on exit.
An existing registration is left in place.
forward accepts only configured device ports. Python’s
forward(port, local_port=0) selects a free local port; the default local port
matches the device port. Raw forwards retain the application’s protocol.
Device Hub (macOS clients only)¶
With devicehub: true on the exporter, a Mac holding the lease can view and
control the device in Xcode’s Device Hub while the device stays plugged into
the exporter. This needs a macOS client with Xcode 27 or later, and Python 3.13
or later on the exporter for the tunnel’s TLS-PSK. On other
platforms devicehub is left out of j ios --help, and running it, or calling
devicehub() from Python, fails with an error that says so. On a Mac without
Xcode 27, or before Xcode has been opened once to install its device support,
devicehub warns that the device will not appear in Device Hub.
The Mac reaches the device through a virtual network link, created once per
boot with administrator rights. When it is missing, j ios devicehub explains
what it changes and offers to create it; sudo then asks for your password.
The link lets your account capture packets on every interface until the next
reboot. Without a terminal to answer in, the command prints the setup command
instead; the script is installed with this package, in its
jumpstarter_driver_iosdevice/scripts directory.
Inside a lease:
j ios devicehub
The first time a Mac uses a device, choose File > Pair Nearby Device… in
Device Hub and type the code it shows when the command asks. Later leases show
the device without a code. If the device is not listed in Device Hub, for
example after Unpair, choose File > Pair Nearby Device… while the command runs:
it asks for the new code. --pair asks for a code straight away. When the
command stops, the device stays paired and moves to Device Hub’s Unavailable
section until the next lease.
Set devicehub_state to keep the device’s Device Hub identity across exporter
restarts; without it, every Mac pairs again after a restart. Set
devicehub_udid only when the same Mac can also see the device directly, as on
a test bench, so that Device Hub keeps the two apart.
HTTPS services¶
https(port=0) returns an endpoint with url, ca_file and provider-defined
JSON metadata. It does not interpret metadata or configure a particular tool.
For example, an HTTP service with a /status route can be queried with:
import urllib.request
from jumpstarter.common.utils import env
with env() as root, root.ios.https() as endpoint:
with urllib.request.urlopen(endpoint.url + "/status", context=endpoint.ssl_context(), timeout=10) as response:
print(response.read().decode())
j ios https prints those three fields as JSON and waits until Ctrl+C.
j ios https -- COMMAND... supplies JUMPSTARTER_IOS_HTTPS_URL,
JUMPSTARTER_IOS_HTTPS_CA_FILE, and JSON JUMPSTARTER_IOS_HTTPS_METADATA to the command.
endpoint.environment() copies the caller’s environment and adds Node CA
trust through NODE_EXTRA_CA_CERTS. Existing explicit CA bundles are extended
without modifying their files. Unset Python trust variables remain unset;
Python callers use ssl_context() or their HTTP library’s CA-file option.
Certificate and hostname verification stay enabled, and
NODE_TLS_REJECT_UNAUTHORIZED=0 is rejected.
All listeners bind 127.0.0.1. Closing a context removes its listener and
public CA files; keep the context open for the tool’s entire lifetime. Exporter
services and identities are revoked at lease teardown, reset or device detach.
Externally started device services remain owned by their launcher.
Security and limits¶
Exporter trust: The default mode keeps the real pairing identity on the exporter. Tools receive disposable credentials and establish TLS to the exporter through Jumpstarter. The exporter uses a separate TLS connection to the device and pins its certificate to the pairing record. Service streams are encrypted to the exporter even when the device service uses plaintext.
Bootstrap trust: Proxy credentials and HTTPS CAs arrive through Jumpstarter’s authenticated RPC channel. Their authenticity depends on that deployment; they do not independently protect against infrastructure that can replace the bootstrap metadata. Native TLS terminates at the trusted exporter, not at the device.
Device isolation: Discovery, pair-record reads and connections are scoped to the selected device on its USB link; a Wi-Fi copy of the same device is ignored. Pair-record writes are rejected; exporter mode also rejects client pairing changes.
Lease access: A lease grants full control of the device. The policy above protects the exporter’s pairing identity, not device capabilities: services started through lockdown, including the iOS 17.4+ CoreDevice tunnel, reach every service and TCP port on the device without further filtering.
forward_portsselects convenience forwards; it is not an access boundary. Reset or re-provision devices between untrusted tenants.Device Hub: The Mac pairs with an identity held by the exporter, not with the device, and reaches the device through the lease. Like other services, the tunnel gives the Mac full access to the device. Pairing a Mac happens inside a lease, and a paired Mac sees the device only while it holds a lease and runs
j ios devicehub. The link script makes the user’s account able to capture packets on every interface until the next reboot.Passthrough:
trust_mode: passthroughexposes the selected device’s real pairing record for tools that require direct device TLS. Use it only when credential sharing is acceptable. Opaque lockdown traffic cannot receive the exporter’s pairing-change policy, andhttp_portis unavailable in this mode.HTTP and raw ports: HTTPS for
http_portends on the exporter; its final USB connection uses HTTP. Rawforward_portsdo not gain native TLS.Service compatibility: Services that switch from TLS to plaintext after authentication, including legacy debugserver, instruments, accessibility and testmanager services, are rejected in exporter mode. The driver does not implement device actions or guarantee every external tool’s service support.
Lifetime: Reconnect after device detach. Pending service handles expire after 60 seconds and are single-use; active connections and pending handles are limited to 128 each. Disposable certificates expire after seven days; obtain a fresh lease for longer use.
Local simulators use the separate jumpstarter-driver-iossimulator package.
API Reference¶
Driver:
- class jumpstarter_driver_iosdevice.driver.IosDevice¶
One USB bench port, or one UDID on an operator-provided usbmux endpoint.
usb_portis a Linux sysfs name such as1-4.2. For a Mac exporter, select a device explicitly withudid. Network transport connects to a usbmux daemon, not directly to an iPhone’s IP address.
Client:
- class jumpstarter_driver_iosdevice.client.IosDeviceClient¶
- connect(protocol: str | None = None, *, port: int = 0, usbfluxctl: str = 'usbfluxctl', idb: str = 'idb', timeout: float = 60.0) Generator[str, None, None]¶
Attach with the user’s usbfluxctl or idb and detach on exit.
The user installs and runs usbfluxd before attaching a usbmux endpoint. Cleanup also covers a timed out or failed attach, and stays bounded.
- forward(port: int, *, local_port: int | None = None) Generator[str, None, None]¶
Forward an exporter-configured device port, binding only loopback.
The local port defaults to the device port; use zero for an unused port.
- https(*, port: int = 0) Generator[HttpsEndpoint, None, None]¶
Expose the configured HTTPS service with its public CA and metadata.
The client does not interpret provider metadata or configure tools. Closing the context removes its listener and CA files. The exporter owns service resources until lease teardown or reset.
- run(command: Sequence[str], protocol: str | None = None) int¶
Run a local command with its transport endpoint in the environment.
- serve(protocol: str | None = None, *, port: int = 0) Generator[str, None, None]¶
Serve a protocol on IPv4 loopback without invoking device tools.
The native protocol is the first one advertised by the target.
port=0selects an unused local port. The yielded endpoint is127.0.0.1:port.
HTTPS endpoint:
- class jumpstarter_driver_iosdevice.client.HttpsEndpoint¶
A verified HTTPS target and provider metadata, valid inside its context.
- environment(base=None) dict[str, str]¶
Copy tool environment and add public trust without disabling TLS.
Node keeps its built-in roots. Existing explicit Python CA bundles are extended, but unset Python trust variables stay unset: Python callers can use
ssl_context()or passca_fileto their SDK’s CA option.
- ssl_context() SSLContext¶
Add this endpoint’s CA to Python’s normal trust and hostname checks.