Tomacco

Docs Operation reference ios operations

ios operations#

59 operations. Generated from assets/operations.json; all areas.

On this page (59)

ios.sdk_install#

Import Apple SDK

Import the SDK from an Apple Xcode archive you downloaded yourself, with xtool. Tomacco downloads nothing from Apple. Read Xcode's license first.

Annotations: writes, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
slimbooleanfalse
tomaccoctl call ios.sdk_install '{"path":"<path>"}'

ios.toolkit_install#

Install iOS toolkit

Fetch the pinned commit of Joshua Warren's omarchy-apple-dev into Tomacco's data directory as a job. With toolchain:true, also run its install-toolchain.sh --mode full --user-only under a private HOME there: it builds a private xtool fork and helpers, and imports the Xcode archive you name in xcode_xip into a private SDK separate from ios.sdk_install. Never runs sudo or an AUR helper, never downloads from Apple, and leaves your own xtool, SDK and Swift alone. Read Xcode's license first.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
toolchainbooleanfalse
xcode_xipstring""Absolute path of an Xcode.xip or Xcode.app you downloaded from Apple yourself
tomaccoctl call ios.toolkit_install

ios.flutter_setup#

Prepare Flutter for iOS

Run the pinned omarchy-apple-dev flutter/setup.sh as a job, once per Flutter version: it downloads about 7 GB from Google's servers, builds gen_snapshot for iOS on Linux, and replaces that one file in your Flutter install, keeping the original beside it. UNTESTED in Tomacco.

Annotations: writes, not idempotent, open world.

Takes no arguments.

tomaccoctl call ios.flutter_setup

ios.flutter_build#

Build Flutter app for iOS

Build an unsigned Runner.ipa and Runner.app for arm64 iPhones on Linux with the pinned omarchy-apple-dev flutter/build.sh, as a durable job. Release mode only; plugins must ship a Package.swift. Dart defines appear in the job record, so pass no secrets. UNTESTED in Tomacco.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
definesarray of string[]KEY=VALUE Dart defines
bundle_idstring""
flavorstring""
targetstring""Dart entry point, relative to the project
timeoutinteger36001 to 86400
tomaccoctl call ios.flutter_build '{"path":"<path>"}'

ios.crash_symbolicate#

Read crash report

Read a modern JSON .ips crash report with the pinned omarchy-apple-dev ips-symbolicate.py. Returns the exception, the faulting thread and its frames. Frames resolve against the Mach-O files in binaries, matched by LC_UUID. Needs only ios.toolkit_install's checkout.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
reportstringyesnon-empty; Absolute local filesystem path
binariesarray of string[]Absolute paths of local Mach-O files, such as the app's executable
tomaccoctl call ios.crash_symbolicate '{"report":"<report>"}'

ios.ipa_inspect#

Inspect iOS app

Read what an .ipa archive or .app directory says about itself, without a device or an Apple tool: bundle_id, version, build, minimum_os, device_family, architectures, the executable's code signature (signed, ad_hoc, identifier, team_id and the signing certificate's common_name, organizational_unit, not_after and sha1), its embedded entitlements, and provisioning_profile, a summary of embedded.mobileprovision (name, uuid, team_id, team_name, expires, expired, days_left, kind development/ad-hoc/app-store/enterprise, devices count, certificates, bundle_id_matches, signing_certificate_included) or null. warnings names an expired profile, a profile for another bundle or team, a signer the profile does not list, a missing arm64 slice and an unsigned executable. Nothing is verified cryptographically: the files are read as they claim to be. Reads files only.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local path of an .ipa archive or an .app directory
tomaccoctl call ios.ipa_inspect '{"path":"<path>"}'

ios.store_ship#

Prepare for App Store

Build an App Store IPA from an xtool project with the pinned omarchy-apple-dev ship.sh as a durable job, sign it, and validate it offline. Without a key it signs with a local TEST identity, which Apple rejects. upload:true sends the IPA to App Store Connect; it never submits for review. The .p8 stays in the owner-only file you name; the key and issuer IDs appear in the job record. A live upload has not been qualified.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
uploadbooleanfalse
key_idstring""
issuer_idstring""
key_pathstring""Absolute path of the owner-only AuthKey_*.p8 file
timeoutinteger36001 to 86400
tomaccoctl call ios.store_ship '{"path":"<path>"}'

ios.doctor#

Check iPhone development on Linux

Check Swift/xtool, managed swift/swiftc identity, SDK availability, Apple authentication, USB and project compatibility without exposing credentials. Compiler/SDK compatibility is reported as not_checked. No Mac is contacted. linux_toolkit reports the optional omarchy-apple-dev checkout and what it lacks; it does not count towards ready.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
pathstring""
devicestring""
tomaccoctl call ios.doctor

ios.prepare#

Set up iPhone connection

Pair a trusted device, mount its developer image or start a userspace tunnel. Login opens a local terminal; secrets never pass through MCP or job logs.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
actionstringyesone of login, pair, developer-image, tunnel
devicestring""
tomaccoctl call ios.prepare '{"action":"login"}'

ios.device_status#

iPhone readiness

Say in one call whether a USB-connected iPhone is ready for development from Linux, and what to do if not: connected, pairing (paired, trust_pending, denied, locked, not_paired or unknown), trusted, unlocked, developer_mode (true, false, not_applicable before iOS 16, or null when unknown), developer_image (mounted, not_mounted or unknown), ios_version and name, ready, and next_steps: plain sentences, most urgent first, each with the Tomacco operation that does it where there is one (unlock the phone, tap Trust, pair, turn on Developer Mode, mount the developer image, keep a tunnel on iOS 17 and newer). Until the phone is paired only libimobiledevice's simple connection is used, so the check shows no Trust prompt. Without device, the one connected iPhone is checked. The tunnel is not checked.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestring""UDID from device.list; empty: the only connected iPhone
tomaccoctl call ios.device_status

ios.wda_prepare#

Prepare iPhone control

Download the pinned official WebDriverAgent device build, verify SHA-256 and extract it. With device set, xtool signs and installs it using an existing Apple login. Requires no Mac.

Annotations: writes, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestring""
retryobjectTry again after a failure: count more attempts (at most 5), waiting backoff_seconds, doubled each time up to 600. A cancelled or timed-out job is not repeated; the job's result lists its attempts.
retry.countintegeryes0 to 5
retry.backoff_secondsinteger51 to 600
tomaccoctl call ios.wda_prepare

ios.apps#

Show iPhone apps

List applications on the USB-connected iPhone with go-ios, including the installed runner bundle identifier. apps is go-ios's own listing; installed has one row per app with bundle_id, name, version, build and executable. all also lists Apple's system apps.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
allbooleanfalseAlso list system apps
tomaccoctl call ios.apps '{"device":"<device>"}'

ios.app_files#

iPhone app files

List or pull the files in an iPhone app's container (Documents, Library, tmp) with go-ios fsync. list returns files, each path relative to the container as read from go-ios's tree drawing, and the drawing itself as tree; pull returns a job that writes the path as one .tar archive. iOS opens the container of development builds only, such as one installed by project.run; others answer app_container_unavailable.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; iPhone UDID from device.list
bundle_idstringyesBundle id as ios.apps lists it; xtool's XTL- prefix may be left out
actionstring"list"one of list, pull
pathstring"."File or directory inside the app's container, relative to it, such as Documents or Library/Preferences
outputstring""Absolute local path of the .tar archive, for pull
tomaccoctl call ios.app_files '{"device":"<device>","bundle_id":"<bundle_id>"}'

ios.record#

Record iPhone screen

Record a USB-connected iPhone's screen for seconds into an H.264 MP4 at path, as a durable job: go-ios screenshot --stream serves the phone's screenshots as MJPEG on a local port, and the latest picture is handed to ffmpeg fps times a second (ffmpeg must be installed). A picture that has not changed, or one that came late, repeats the last, so the video lasts as long as the recording; how many distinct pictures the phone delivers a second depends on the phone. The job's result names the file and counts frames_written, frames_distinct and pictures_received; a stream that ends stops the recording early with what it has (ended_early). job.send stop ends it early and keeps the video (stopped_early). One recording per iPhone at a time. Needs the developer image mounted (ios.prepare). For a live view use runtime.start.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; iPhone UDID from device.list
pathstringyesnon-empty; Absolute path of the MP4 to write, ending in .mp4; an existing file is replaced when the recording succeeds.
secondsinteger301 to 600; How long to record.
fpsinteger101 to 30; Frames per second of the video.
tomaccoctl call ios.record '{"device":"<device>","path":"<path>"}'

ios.crash_reports#

iPhone crash reports

List the crash reports of the USB-connected iPhone whose names match pattern (a glob; iOS names a report &lt;process&gt;-&lt;date&gt;.ips, so MyApp* selects one app's), with go-ios crash ls; with directory, also copy them there with go-ios crash cp and return copied: each new or changed file's name, path, bytes and sha256, ready for ios.crash_symbolicate. go-ios moves pending reports into the readable area first; reports are not deleted from the phone.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty
patternstring"*"Report name glob, for example MyApp*
directorystring""Absolute host directory to copy the matching reports into; created if missing. Empty: only list them.
tomaccoctl call ios.crash_reports '{"device":"<device>"}'

ios.signing#

iPhone signing overview

List the provisioning profiles installed on a USB-connected iPhone (copied off it with libimobiledevice's ideviceprovision and removed again), those embedded in an xtool project's built apps under path/xtool and any named profiles, each with name, uuid, team, kind, created, expires, days_left, the device ids it covers, covers_device for the given device, free_account (a profile lasting 7 days or less, as a free Apple account's do) and its own warnings. identities lists the signing certificates those profiles name and the project's apps are signed with, once each by SHA-1, with team, expiry and where each was seen. warnings sums up what would stop an app from installing or opening: expired or expiring within 7 days, a free account's 7-day profile, a device a profile does not list. It reads what the files claim, verifies no signature, contacts no Apple service and does not read xtool's login.

Annotations: read-only, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestring""UDID of the USB-connected iPhone whose installed profiles to list and check coverage for; empty: no phone
pathstring""Absolute path of an xtool project; its built apps under xtool/ are read
profilesarray of string[]up to 100 items; Absolute paths of .mobileprovision files to read as well
tomaccoctl call ios.signing

ios.emulator.inspect#

Check iOS emulator

Inspect a local Darwin/SPTM restore-shell or Inferno kernel-probe engine and matching firmware. Prepared images are confined and hash-verified. Neither backend runs graphical iOS apps.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; Local QEMU executable implementing Darwin/SPTM or Apple T8030 hardware.
firmwarestringyesnon-empty; Prepared restore-shell directory (Darwin/SPTM) or extracted IPSW components (Inferno), including BuildManifest.plist.
variantstring"release"one of research, release
tomaccoctl call ios.emulator.inspect '{"engine":"<engine>","firmware":"<firmware>"}'

ios.emulator.probe#

Examine iOS kernel boot

Run a disposable local iOS diagnostic job. Darwin/SPTM verifies actual restore-shell commands; Inferno captures kernel-start failure. Retains logs, registers and artifacts, cleans temporary resources. No full SpringBoard or graphical app runtime.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; Local QEMU executable implementing Darwin/SPTM or Apple T8030 hardware.
firmwarestringyesnon-empty; Prepared restore-shell directory (Darwin/SPTM) or extracted IPSW components (Inferno), including BuildManifest.plist.
variantstring"release"one of research, release
secondsinteger301 to 120
boot_displayobjectOptional real guest boot framebuffer capture. Requires the patched SPTM engine. This does not provide graphical iOS apps.
boot_display.widthintegeryes64 to 4096
boot_display.heightintegeryes64 to 4096
boot_mediabooleanfalseRegister the existing guest RAM image through Apple DiskImages/IOMedia so disk devices can be inspected and read. Requires the patched SPTM engine. Root remains the restore RAM disk; this does not provide persistent storage or a full iOS boot.
working_imagestringnon-empty; Optional existing owner-only working copy of the source ramdisk (same size, no symlink or hardlinks). Requires boot_media=true and boot-storage support. The guest may modify this copy; it is locked, flushed on graceful exit and retained after the job. Full iOS boot and crash consistency are out of scope.
tomaccoctl call ios.emulator.probe '{"engine":"<engine>","firmware":"<firmware>"}'

ios.emulator.exec#

Run iOS guest command

Boot an isolated Darwin/SPTM iOS restore shell on Linux, execute a UTF-8 shell script, record actual output and exit status, and stop the guest. Requires prepared firmware; no macOS, persistent disk writes, SpringBoard, GUI apps or networking.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; Local QEMU executable implementing Darwin/SPTM or Apple T8030 hardware.
firmwarestringyesnon-empty; Prepared restore-shell directory (Darwin/SPTM) or extracted IPSW components (Inferno), including BuildManifest.plist.
variantstring"release"one of research, release
secondsinteger601 to 300
scriptstringyesnon-empty, up to 32768 characters
boot_displayobjectOptional real guest boot framebuffer capture. Requires the patched SPTM engine. This does not provide graphical iOS apps.
boot_display.widthintegeryes64 to 4096
boot_display.heightintegeryes64 to 4096
boot_mediabooleanfalseRegister the existing guest RAM image through Apple DiskImages/IOMedia so disk devices can be inspected and read. Requires the patched SPTM engine. Root remains the restore RAM disk; this does not provide persistent storage or a full iOS boot.
working_imagestringnon-empty; Optional existing owner-only working copy of the source ramdisk (same size, no symlink or hardlinks). Requires boot_media=true and boot-storage support. The guest may modify this copy; it is locked, flushed on graceful exit and retained after the job. Full iOS boot and crash consistency are out of scope.
tomaccoctl call ios.emulator.exec '{"engine":"<engine>","firmware":"<firmware>","script":"<script>"}'

ios.emulator.restore#

Restore virtual iOS system

Run the original vphone600 restore service through an existing private virtual USB bridge. Writes that guest's attached virtual disks, using local digest-checked firmware and an existing IM4M. No physical devices, downloads or signing requests. Durable, cancellable job; inspect ios.emulator.report for guest status.

Annotations: destructive, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
bridgestringyesnon-empty; Ready private ios_paravirt_usbmuxd.py bridge.json
firmwarestringyesnon-empty; Local directory containing BuildManifest.plist
identityintegeryes0 to 1023; Explicit vphone600 BuildIdentity index
manifeststringyesnon-empty; Existing local DER IM4M matching the running guest's boot manifest
system_imagestringyesnon-empty; Local regular file matching the selected BuildManifest OS digest
userlandstringnon-empty; Completed original iPhone import from ios.emulator.prepare_userland. Selects its OS and three metadata components while preserving the PCC boot identity. system_image must select this import's images/system.dmg. Cryptex installation remains separate.
overridesarray of object[]up to 128 items
overrides[].componentstringyesnon-empty, up to 128 characters
overrides[].pathstringyesnon-empty
secondsinteger6001 to 3600
tomaccoctl call ios.emulator.restore '{"bridge":"<bridge>","firmware":"<firmware>","identity":0,"manifest":"<manifest>","system_image":"<system_image>"}'

ios.emulator.usb_bridge#

Start virtual iOS USB access

Connect a live ios.emulator.paravirtual job with usb=true to a private usbmuxd socket. Waits for the owned guest request and actual USB readiness. Identical active requests reuse the job; stopping its parent cancels the bridge. Inspect ios.emulator.report for readiness, USBMUXD_SOCKET_ADDRESS and service failures. No physical devices, pairing, network listeners, restore or signing requests.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
job_idstringyesmatching ^[a-f0-9]{32}$
secondsinteger1801 to 3600
tomaccoctl call ios.emulator.usb_bridge '{"job_id":"<job_id>"}'

ios.emulator.paravirtual#

Start virtual iOS platform

Run the vphone600/PCC platform directly on Linux as a bounded, exclusive, cancellable job. Requires an explicit local engine, prepared boot inputs and optional private virtual storage. Identical active requests reuse the job. ios.emulator.report separates guest evidence from process completion. An interactive session with guest control saves and stops itself shortly before its seconds limit, with the same flush and record as ios.emulator.control stop; if that is refused it is frozen at the limit.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
guest_control_backendstringnon-empty; Optional local vhost-device-vsock executable. Requires the shared-memory engine patch and owned guest tools built with agent=true; no TCP networking.
enginestringyesnon-empty
kernelstringyesnon-empty
sptmstringyesnon-empty
txmstringyesnon-empty
device_treestringyesnon-empty; Prepared vphone600 device tree; original Apple inputs are retained
ramdiskstringyesnon-empty
trustcachestringyesnon-empty
boot_manifeststringnon-empty
system_volume_root_hashstringnon-empty; Original isys IM4P; hands off its exact APFS auth blob and snapshot name without authenticating Apple signatures
pcc_hostnamestringnon-empty, up to 63 characters; matching ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; Optional local hostname for PCC darwin-init via its NVRAM boot proxy. Requires a normal sealed-system boot. Existing configurations are retained; no security controls or SSH access are changed.
sepstringnon-empty
sep_manifeststringnon-empty
sep_storagestringnon-empty; Explicit writable virtual storage; changes persist. Never a physical device.
auxiliary_diskstringnon-empty
root_diskstringnon-empty
writable_auxiliary_diskstringnon-empty; Explicit writable virtual storage; changes persist. Never a physical device.
writable_root_diskstringnon-empty; Explicit writable virtual storage; changes persist. Never a physical device.
tcg_profilebooleanLoad the TCG profile plugin and write tcg-profile.json (per-block guest profile) into the run directory; windows on demand with tools/ios_tcg_profile.py mark.
tcg_profile_intervalinteger1 to 3600; With tcg_profile: also write a profile window every this many seconds.
secondsinteger601 to 86400; Bounded host session duration, up to 24 hours; the logs get 32 MiB per started six hours. The default is 60 seconds; job.cancel stops the session earlier.
boot_argsstringnon-empty, up to 1023 characters
clock_modestring"wall"one of wall, instruction; Guest time source. instruction uses 1 ns per emulated instruction and real-time idle waits, useful for CPU-bound TCG boots. It changes guest timing, is not a performance speedup, and does not change the host seconds timeout.
host_cpu_policystring"scheduler"one of scheduler, performance; Host CPU placement for wall-clock multithreaded TCG. performance places AP threads on separate allowed physical cores ranked by maximum frequency, when Linux topology and thread affinity are available. The separate SEP thread retains host scheduling. Missing support falls back to the scheduler; inspect the request receipt or control metrics for actual placement. No system CPU settings are changed.
translation_cache_mibinteger1 to 4096; Requested host TCG code-cache size in MiB. Omit to retain the engine default. A larger buffer can reduce repeated translations when the cache fills, at the cost of host memory; requests above 2048 require the actual paused engine to prove range-independent TB chaining and an exact requested mapping before guest execution. The request receipt records the requested size, not a measured performance improvement.
btistringone of keep, off; Branch Target Identification of the emulated CPU: keep (the engine's CPU; also when omitted) or off, a CPU without FEAT_BTI (guest-visible ID register). Fixed at start; off requires an engine with qemu-arm-bti-property.
jump_probestringone of engine, generic, lean, inline; Jump-cache probe at TB exits: engine (the engine's own choice; also when omitted), generic (the out-of-line helper, as upstream), lean or inline. Fixed at start; anything but engine requires an engine with qemu-arm-jump-probe.
wfe_wait_usinteger0 to 100000; Bound of a WFE's wait on its exclusive monitor in microseconds (0: return at once, as upstream). Omit to retain the engine default. Fixed at start; requires an engine with qemu-arm-wfe-monitor.
target_pagestringone of engine, 4k, 16k; Target page size of the engine: engine (the board's, 1 KiB for tomacco-vphone; also when omitted), 4k, or 16k, which also reports only the 16 KiB translation granule (guest-visible ID register; Apple's guest maps only 16 KiB pages). Fixed at start; anything but engine requires an engine with qemu-arm-target-page-16k.
sep_boot_rolestringone of normal, restore
sep_handoffstringone of preloaded, userspace
usbbooleanfalse
windowbooleanfalse
interactivebooleanfalseAttach input devices at boot and enable ios.emulator.control on a private local monitor. Does not imply responsive guest UI.
input_transportstring"virtio"one of virtio, usb-hid; virtio supplies a keyboard and absolute mouse. usb-hid requires interactive=true and supplies a VirtIO keyboard plus USB Home and a single-contact touchscreen. Requires an engine built with the USB HID device.
display_profilestring13 to 29 characters; Fixed physical panel: WIDTHxHEIGHT@HZ:WIDTH_MMxHEIGHT_MM, e.g. 1170x2532@60:65x141. Pixels 64..8192, Hz 1..120, mm 10..2000; physical aspect must match pixels within millimetre rounding. Publishes one native display mode. Requires an engine with reims-display-panel support. This configures a virtual panel, not an iPhone hardware identity or device-specific safe areas. Omit to retain the desktop mode list.
device_modelstringmatching ^[a-z0-9-]{1,32}$; Describe the virtual iOS device as one of the built-in iPhone models that carry a virtual entry (ios.virtual.status lists them). The launcher applies Apple's own gestalt variant from the device tree (the iPhone 16 research device: island, rounded corners, no Home button) and, for other models, that model's display size and corner values, which are derived and not Apple's. The model brings its own panel, so display_profile must be omitted or equal. Changes how iOS lays itself out, not the hardware identity; such a device is unlocked with a swipe up from the bottom edge, not with home.
trace_exceptionsbooleanfalse
trace_storagebooleanfalseDiagnostic only: trace every VirtIO disk request within the bounded log budget. Normal restores use aggregate QMP statistics.
sep_measure_manifestbooleanfalse
guest_networkstring"off"one of off, nat; nat attaches a virtio network device behind passt: outbound only, with no inbound port and without the host's loopback. It is real internet exposure of a guest whose protections are off.
guest_network_backendstringnon-empty; The passt executable of guest_network nat; default: passt on PATH.
guest_memory_importstring"shared"one of shared, dma-buf; Guest RAM for the renderer's GPU import, with guest_control_backend. shared keeps QEMU's shared RAM, which radv/amdgpu refuse to import, so frames are copied on the CPU. dma-buf lays guest RAM on a sealed memfd that the renderer imports through /dev/udmabuf in windows of the kernel's udmabuf.size_limit_mb; it falls back to shared when /dev/udmabuf or memfds are unavailable, and to CPU copies when the import is refused. An imported window stays pinned until the session ends, which on a live boot was nearly all of guest RAM. The request receipt's guest_memory_import says which backing ran.
tomaccoctl call ios.emulator.paravirtual '{"engine":"<engine>","kernel":"<kernel>","sptm":"<sptm>","txm":"<txm>","device_tree":"<device_tree>","ramdisk":"<ramdisk>","trustcache":"<trustcache>"}'

ios.emulator.control#

Operate running virtual iOS

Inspect, capture or send input to a live ios.emulator.paravirtual job started with interactive=true. Actions are serialized per guest: one that arrives while another runs waits up to 2.5 s for its turn before ios_control_busy is returned. ios_control_starting means the control monitor is not published yet; ios_control_ended means the emulator has exited while the job is still finishing (hashing its media), so wait for the job instead of cancelling it. Coordinates are normalized from 0 to 1 across the captured display. With input_transport=virtio, click/drag use an absolute mouse. With usb-hid, they use a single touchscreen contact; home sends the HID Consumer Menu usage with emulator-owned release, and status reports actual USB delivery counters. Touch has no hover or multitouch. A persistent client (tomaccoctl mcp) can stream a live contact instead: touch_down presses at x/y and holds the guest's control lane until touch_up, touch_move moves the contact (the same point again only keeps it alive), and touch_up lifts it, at x/y when given. Other control actions wait for that lane meanwhile; file frames do not. A contact without any event for 5 s is lifted, and the client's end lifts whatever it still holds. A one-shot call refuses touch_down with ios_touch_lane_required; touch_up is accepted anywhere and always sends a release. A drag lifts at its end point at once, which a scroll view reads as a fling; with hold_ms it first rests there that long, so the content stops where the contact did. power_button sends the native timed 100 ms GPIO side-button pulse only on the tomacco-vphone machine; it can wake or lock the screen, so acknowledgement never establishes the resulting screen state. key accepts QEMU key names. Input acknowledgement is not proof of a guest UI response; inspect a subsequent screenshot. Screenshots stay in the owned job directory. frame returns an actual PNG data_url (up to 16 MiB) and deletes its temporary capture on success or failure; it is suitable for bounded live preview polling. metrics reads actual QMP CPU state/topology, cumulative block-device I/O counters and TCG cache statistics without pausing or resetting the guest. Host sampling timestamps bound the sequential observations. Unknown TCG fields stay null, unavailable monitor statistics retain their error, and the original text is preserved. Counters and guest-clock lag are not application FPS or CPU utilization. stop requests QMP exit so the durable job can finalize media hashes. With guest control enabled it reserves the framework lane and requires a confirmed idle native worker; pending or unknown work prevents stopping. An acknowledgement does not establish guest OS shutdown or completed storage verification. Follow ios.emulator.report until the job ends; an unconfirmed stop is not replayed automatically. If the emulator cannot be told to exit, the stop record is rewritten without the flush confirmation (guest_storage_flushed false, quit_acknowledged false); a request the client cancels leaves no record and does not stop the emulator. trust_cache_update stops the guest's CPUs, replaces the boot's trust-cache module in guest RAM with the file at path and lets them go on; only for a machine built with vphone-trust-cache-update, and ios.virtual.deploy is the way to use it. text types on the USB keyboard, into what has the focus; a key sent before the field has it is lost, so look at a frame first. iOS reads the keys with the layout of the guest's language: layout follows it unless named.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
job_idstringyesmatching ^[0-9a-f]{32}$
actionstring"status"one of status, metrics, stop, screenshot, frame, home, power_button, key, move, click, drag, touch_down, touch_move, touch_up, trust_cache_update, text
transportstring"inline"one of inline, file; Only for frame. inline returns a PNG data_url. file writes an uncompressed PPM into the user's private, memory-backed runtime directory and returns its path, size, sequence and changed; only that frame and its predecessor are kept, and a picture identical to the previous one keeps that frame's file and sequence with changed=false. Use file for a viewer that refreshes several times a second. Fails with ios_frame_file_unavailable when no private runtime directory exists. It also fails with ios_frame_file_unavailable, without asking the emulator for a picture, when the display's uncompressed frame would exceed 64 MiB.
keysarray of string1 to 8 items
hold_msinteger1 to 1000
xnumber0 to 1
ynumber0 to 1
to_xnumber0 to 1
to_ynumber0 to 1
duration_msinteger1 to 2000
pathstringnon-empty; trust_cache_update: host path of the complete replacement trust-cache module, of the size the boot's module has.
textstringnon-empty, up to 1024 characters; text: what to type, one key at a time, in characters the layout has.
layoutstringone of us, de; text: the layout iOS reads the keys with; default: the guest's language, and any language but English and German must name one.
tomaccoctl call ios.emulator.control '{"job_id":"<job_id>"}'

ios.emulator.build_gpu_plugin#

Build iOS graphics compiler plugin

Cross-build the pinned open-source vphone GPU compiler plugin on Linux without an Apple SDK. Produces an ad-hoc-signed ARM64e dylib with its exact four-function ABI; retains source, licence, commands and output hash. Requires an ARM64e linker that implements authenticated-pointer relocations. Does not modify the original Apple GPU bundle or install code. Read progress and failures with ios.emulator.report.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
sourcestringyesnon-empty; Local vphone-cli checkout with the pinned GraphicLoader source
outputstringyesnon-empty; New private build directory
clangstring"clang++"non-empty
linkerstringyesnon-empty; ARM64e-capable ld64; stock ld64.lld may not support authenticated-pointer relocations
nmstring"llvm-nm"non-empty
ldidstringyesnon-empty
tomaccoctl call ios.emulator.build_gpu_plugin '{"source":"<source>","output":"<output>","linker":"<linker>","ldid":"<ldid>"}'

ios.emulator.prepare_userland#

Import original iOS system

Prepare the complete original iPhone/iPad OS and Cryptex asset set plus a matching original PCC GPU bundle on Linux. Verifies each component against its selected BuildManifest, retains independent copies and extracts the GPU in a networkless read-only Linux helper. Cancellable, exclusive, offline job; ios.emulator.report exposes progress and failures. Does not install or boot these images, alter authentication policy or generate tickets.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
firmwarestringyesnon-empty; Directory containing the original iPhone/iPad BuildManifest.plist and components
gpu_plugin_buildstringnon-empty; Optional completed ios.emulator.build_gpu_plugin output; its original bytes and licence are retained beside the unmodified Apple driver
pcc_firmwarestringyesnon-empty; Directory containing the original vphone600 PCC BuildManifest.plist
pcc_system_imagestringyesnon-empty; Decrypted original PCC OS image, checked against its manifest
supportstringyesnon-empty; Directory containing busybox/busybox and apfs/apfs.ko
helper_enginestringyesnon-empty; Local qemu-system-x86_64 executable
helper_kernelstringyesnon-empty; Linux kernel matching the APFS helper module
biosstringyesnon-empty; QEMU PC firmware directory
outputstringyesnon-empty; New private directory for verified copies and original GPU driver; existing paths are refused
identityintegeryes0 to 255
pcc_identityintegeryes0 to 255
secondsinteger9001 to 3600
accelstring"kvm"one of kvm, tcg
componentsarray of object[]up to 10 items; Explicit local overrides. OS and both Cryptex OS components must be decrypted original images.
components[].componentstringyesone of OS, Cryptex1,SystemOS, Cryptex1,AppOS, StaticTrustCache, SystemVolume, Ap,SystemVolumeCanonicalMetadata, Cryptex1,SystemTrustCache, Cryptex1,SystemVolume, Cryptex1,AppTrustCache, Cryptex1,AppVolume
components[].pathstringyesnon-empty
tomaccoctl call ios.emulator.prepare_userland '{"firmware":"<firmware>","identity":0,"pcc_firmware":"<pcc_firmware>","pcc_identity":0,"pcc_system_image":"<pcc_system_image>","support":"<support>","helper_engine":"<helper_engine>","helper_kernel":"<helper_kernel>","bios":"<bios>","output":"<output>"}'

ios.emulator.prepare_restore#

Prepare virtual iOS restore runtime

Create a separate PCC 23E5207q restore RAMdisk and trust cache with a bounded disk-format reopen adapter. Optional research_seal explicitly replaces authentication of TWO exact seal-metadata containers with SHA-384 pins; all other inputs use the original Image4 verifier. The default preserves authentication policy. Offline, durable, cancellable job; retains originals and records every modification. Does not sign Apple manifests.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
ramdiskstringyesnon-empty; Original raw APFS restore RAMdisk
trustcachestringyesnon-empty; Original raw version 1 RAMdisk trust cache
supportstringyesnon-empty; Local directory containing ldid/ldid, busybox/busybox and apfs/apfs.ko
helper_enginestringyesnon-empty; Local qemu-system-x86_64 executable
helper_kernelstringyesnon-empty; Linux kernel matching the APFS helper module
biosstringyesnon-empty; QEMU PC firmware directory
outputstringyesnon-empty; New private output directory; existing paths are never overwritten
accelstring"kvm"one of kvm, tcg
research_sealobjectExplicit opt-in to a weakened local research authentication policy for two digest-pinned sealing objects. Apple signatures are NOT verified for these two objects. Requires the exact original 23E5207q BuildManifest; outputs are separate from normal profiles.
research_seal.firmwarestringyesnon-empty
research_seal.identityintegeryes0 to 255
research_seal.manifeststringyesnon-empty; Existing DER IM4M also used by this guest and restore session
research_seal.userlandstringnon-empty; Additional explicit opt-in: use exactly the two original iPhone 24A446 sealing objects from a completed userland import. Requires its exact pinned source BuildManifest and the same PCC boot identity. Cannot be combined with sealing-component overrides.
research_seal.overridesarray of object[]up to 2 items
research_seal.overrides[].componentstringyesone of Ap,SystemVolumeCanonicalMetadata, SystemVolume
research_seal.overrides[].pathstringyesnon-empty
tomaccoctl call ios.emulator.prepare_restore '{"ramdisk":"<ramdisk>","trustcache":"<trustcache>","support":"<support>","helper_engine":"<helper_engine>","helper_kernel":"<helper_kernel>","bios":"<bios>","output":"<output>"}'

ios.emulator.build_guest_tools#

Build native iOS guest tools

Cross-build owned ARM64 inventory, LaunchServices control and optional UIKit diagnostic on Linux without an Apple SDK. Verifies signed code pages and adds explicit local platform grants for exactly these programs. Offline, durable job; inspect ios.emulator.report. Building does not establish guest execution or visible application scenes. With applications, developer app bundles are staged, signed and admitted beside the diagnostic.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
graphics_query_accessbooleanfalseOpt in to the exact AppleParavirtDeviceUserClient entitlement for the owned control agent's native graphics queries; requires agent. Other payload programs receive no graphics exception. Access remains subject to original native checks and does not establish a successful draw.
development_signingbooleanfalseOpt in to an owned-CDHash ad-hoc signing policy for exactly the included UIKit diagnostic. Requires agent and application_probe. Adds a bounded root helper that configures only installd’s next invocation, and a checked native verifier adapter. Does not establish successful installation or an app scene.
agentbooleanfalseRun the local VSOCK service. Requires an explicitly attached host-only virtio-vsock transport.
trustcachestringyesnon-empty; Original version-2 cache. Every existing entry is retained; only owned programs receive explicit local platform trust.
outputstringyesnon-empty; New private build directory.
clangstring"clang"non-empty
linkerstringyesnon-empty; Mach-O linker for ARM64 iOS executables. With development_signing, requires ARM64e authenticated-pointer support; use the ld64 built by tools/build_ios_linker.py. Some ld64.lld versions accept arm64e but emit ARM64 and are rejected by byte verification.
ldidstringyesnon-empty; Local ldid executable for ad-hoc signing.
minimum_iosstring"27.0"non-empty
application_probebooleanfalseInclude the bounded native UIKit lifecycle diagnostic.
quiet_servicesbooleanfalseInclude a root helper that, on every boot, unloads from launchd the original services that can only fail on the emulated processor: duetexpertd, intelligencetasksd, searchpartyd and transparencyd (a libxpc assertion loop), watchdogd (exits because the boot disables the watchdog timer and is restarted) and axassetsd (speech extensions that miss their check-in). chronod stays, so widgets are still drawn. Nothing on disk changes; a service that launchd refuses to unload is left alone. Proactive suggestions, Find My, key transparency and downloadable voices do not work with it; they do not work without it either, and their repeated failures take processor time from the interface.
applicationsarray of objectup to 31 items; Developer app bundles to stage beside the owned diagnostic: each is copied into the payload, signed ad hoc with its allowlisted entitlements (application-identifier, team-identifier, get-task-allow, keychain-access-groups, application-groups; nothing else), its executable's cdhash added to the trust cache and its identity to the signing policy table installd's adapter reads. Requires development_signing. A start installs each from the payload.
applications[].bundlestringyesnon-empty; Host path of an .app directory built for arm64 iOS (SwiftPM/xtool); its main executable must be its only code.
applications[].entitlementsstringEntitlements plist to request; empty: the executable's embedded entitlements, or only application-identifier.
tomaccoctl call ios.emulator.build_guest_tools '{"trustcache":"<trustcache>","output":"<output>","linker":"<linker>","ldid":"<ldid>"}'

ios.emulator.prepare_guest_tools#

Install native iOS guest tools

Revalidate the owned payload, take independent locked snapshots of root/auxiliary/SEP disks and install into unsealed Preboot using a networkless Linux helper. Optional previous_tools permits replacing an exactly matching old payload in the new disk copy, preserving the guest's initialized data. Enables the original monitor virtual ERM setting for launchd SupplementalPaths. Preserves other volumes and the original service cache. Does not activate iOS or boot the guest. Read progress/failures and boot_inputs through ios.emulator.report. development_device optionally configures the non-production virtual device for the original local iOS development activation path; its preparation receipt does not claim activation or scene readiness. previous_graphics explicitly upgrades an existing owned Preboot bundle only after verifying its closed directory, file modes and all previous hashes; the replacement is staged and verified before directory exchange. Unexpected contents and leftover transaction directories are refused.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
diskstringyesnon-empty; Closed restored virtual root disk. Only a new copy is changed.
auxiliary_diskstringyesnon-empty; Matching closed auxiliary storage.
sep_storagestringyesnon-empty; Matching closed SEP storage.
toolsstringyesnon-empty; Completed ios.emulator.build_guest_tools output.
previous_toolsstringnon-empty; Optional completed old guest-tools build. Its signatures, exact file inventory, contents and modes must match the closed disk before replacing only those files in the new copy.
graphicsstringnon-empty; Optional exact prepared Metal driver bundle directory. Verifies or installs the bundle at /private/preboot/Cryptexes/ps.bundle in the new private copy. Pair it with a kernel whose MetalPluginName names that location and a trust cache binding the same driver/compiler/bridge identities. Refuses unexpected existing files. Records graphics_payload.
trustcachestringyesnon-empty; Exact input cache used by the builder.
device_treestringyesnon-empty; Prepared normal-boot virtual vphone600 device tree.
supportstringyesnon-empty; Local preparation support containing BusyBox and the APFS helper module.
helper_enginestringyesnon-empty; Linux QEMU x86_64 helper executable.
helper_kernelstringyesnon-empty; Linux kernel matching the local APFS module.
biosstringyesnon-empty; QEMU BIOS directory.
outputstringyesnon-empty; New private installation directory; source disks are preserved.
accelstring"kvm"one of kvm, tcg
secondsinteger3001 to 3600
development_devicebooleanfalseOpt-in non-production vphone600 VM profile. Sets consistent development-device security properties so the original iOS daemon can select its local activation path. Refuses nonvirtual, retail, recovery or inconsistent identities. No daemon patch, activation response, external request or activation-success claim. Read the actual result with ios.emulator.guest operation=setup.
previous_trustcachestringnon-empty; Original source cache bound to previous_tools when the new graphics cache differs. Requires previous_tools. Each old/new payload is independently revalidated against its own source cache; file inventory/signature checks remain required.
previous_graphicsstringExact previous owned graphics bundle to verify before replacing it in the new private copy; requires graphics.
resumebooleanfalseContinue a complete private copy refused by host resource pressure before the installation helper started. Requires the original inputs and existing output. Revalidates the payload, boot inputs, locks and all source/copy bytes; preserves the previous failure receipt.
root_overlaybooleanfalseMake the new root disk an empty qcow2 overlay (system.qcow2) whose backing file is disk, by its absolute path, instead of copying disk; needs qemu_img. The auxiliary and SEP disks are still copied. A disk that is already an overlay gets another layer, up to 8; beyond that, and without root_overlay, an overlay disk is flattened into a new raw disk with qemu_img. The record's root_disk names the method and why.
qemu_imgstring""qemu-img executable for root_overlay or to flatten an overlay disk. Empty: none.
tomaccoctl call ios.emulator.prepare_guest_tools '{"disk":"<disk>","auxiliary_disk":"<auxiliary_disk>","sep_storage":"<sep_storage>","tools":"<tools>","trustcache":"<trustcache>","device_tree":"<device_tree>","support":"<support>","helper_engine":"<helper_engine>","helper_kernel":"<helper_kernel>","bios":"<bios>","output":"<output>"}'

ios.emulator.guest#

Control native iOS guest programs

Call the owned guest service through the active job's private virtio-vsock channel. Requires build_guest_tools agent=true and paravirtual guest_control_backend. One framework request and one status, service-log or cancel request may run at once; nothing connects from outside. Framework requests return request_id: ios.emulator.guest_result follows the latest receipt after a timeout without replaying the request. A timed-out mutation may still execute in iOS and is never replayed automatically. status reports the current worker and, as *_supported flags, what these guest tools offer. Acceptance never verifies a visible scene, and a process or crash observation never proves a process lifetime. Apps: inspect lists LaunchServices inventory; lookup finds a bundle; install takes a guest .app or .ipa through MobileInstallation (an IPA needs bundle_id) with request-correlated progress; register and open start an app through the original FrontBoard service, keeping native errors and the app's PID; cancel stops the current worker. receive_bundle, policy_add and debug_server are the guest side of ios.virtual.deploy and ios.virtual.debug. Logs: logs reads bounded owned logs by name, among them development (signing setup and verifier), quiet (tomacco-quiet: services unloaded and its locationd guard), battery, debugserver and buffer_texture (the native Metal buffer-texture observer), and an application's log through its container. Setup: setup reads SetupAssistant and MobileActivation state. setup_finish, on an activated virtual device, backs up and writes the three setup values through cfprefsd and reads them back; it returns restart_required. restart_ui restarts the interface. flush flushes storage. effects reduced or original sets Reduce Transparency and Reduce Motion through the system's setters, which turns the glass materials the renderer cannot draw correctly into plain surfaces. watchdog_scale original or a factor 2..30 sets FrontBoard's FBLaunchWatchdogScale, applied after restart_ui or a reboot; the watchdogs stay active. icon_cache clear moves each IconsCache store aside, deleting nothing, and restarts SpringBoard so that every icon is rendered again. time_zone Area/City sets the system zone through tzlink and reads previous_link and link back. auto_lock never or original sets Settings' Auto-Lock through ManagedConfiguration, or SBDontLockEver when profiled does not take it, and reads it back. Setting a zone or Auto-Lock again is harmless, so a lost reply is automatic_retry_safe; a start sends both itself. State: screen_state reads SpringBoard's screen-lock, passcode and backlight state; a failed RPC stays unknown, never unlocked. display_state reads the MobileGestalt scale and display tuple against the chosen display-scale. display_refresh asks the system helper to rebuild its display cache after checking the scale locally, and a new display_state must confirm it before a UI restart. foreground_state reads SpringBoard's primary-layout application. graphics_state reads the Metal default device and a process-name snapshot without submitting GPU work. surface_state reads IOSurface metadata for up to four comma-separated decimal IDs; no pixels are accessed. network_state lists the interfaces' IPv4 addresses and tries one name lookup. hardware_probe &lt;IOKit class&gt; counts that class's services. gestalt &lt;key&gt; reads one MobileGestalt answer. developer_mode status or enable reads or arms Developer Mode, effective at the next boot. keyboard_layout reports the guest's language, by whose layout iOS reads a hardware keyboard. Diagnostics: crash_reports &lt;report name prefix&gt; reads the matching .ips and .crash files of the CrashReporter directories, up to 512 KiB as base64 and with crash_report_identities parsed from the reports; it never removes reports. persistence_probe takes create:&lt;64 hex digits&gt;, read:&lt;same challenge&gt;, classes:&lt;challenge&gt; (one file per protection class A..D) or inode:&lt;digits&gt; (a path and protection class for a boot log's failure). A clean-reboot result needs two separately bound boots with the same challenge.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
timeout_secondsinteger301 to 120; Absolute response deadline, including partial reads. A timed-out install/register/open/setup_finish/display_refresh may still execute in iOS. Use ios.emulator.guest_result with the original request_id, then inspect native state before a new mutation. Missing receipts do not establish failure.
job_idstringyesmatching ^[a-f0-9]{32}$
operationstringyesone of inspect, status, lookup, cancel, install, register, open, logs, setup, setup_finish, restart_ui, flush, effects, screen_state, display_state, foreground_state, display_refresh, graphics_state, surface_state, crash_reports, persistence_probe, watchdog_scale, icon_cache, time_zone, auto_lock, receive_bundle, policy_add, debug_server, network_state, hardware_probe, gestalt, developer_mode, keyboard_layout
argumentstringnon-empty, up to 4096 characters
bundle_idstringnon-empty, up to 255 characters; matching ^[A-Za-z0-9._-]+$; Installation only. Required for guest IPA archives. Optional for .app bundles, where it must match Info.plist. iOS validates the package identity.
tomaccoctl call ios.emulator.guest '{"job_id":"<job_id>","operation":"inspect"}'

ios.emulator.guest_result#

Fetch result of an iOS guest job

Observe a framework request by its request_id, including after the original host timeout: install, register, open, lookup, inspect and application logs. Receipts up to 64 KiB are kept. Reads status and the latest atomic receipt from the live guest within one deadline and returns running, completed (with the actual response) or unknown. ready_for_next_request requires a completed receipt and an idle native worker. An absent or overwritten receipt, or a cancelled or timed-out worker, never establishes that a mutation failed. It does not replay the request, cancel a worker or verify a visible scene. The status read carries request-correlated progress (phase, sequence, optional native_status and percent_complete); progress never overrides a receipt. Recovered crash_reports receipts include crash_report_identities.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
job_idstringyesmatching ^[a-f0-9]{32}$
request_idstringyesmatching ^[a-f0-9]{32}$; The exact ID returned by the original guest framework request or its uncertain-outcome error.
timeout_secondsinteger301 to 120; One absolute deadline covering both read-only guest requests.
tomaccoctl call ios.emulator.guest_result '{"job_id":"<job_id>","request_id":"<request_id>"}'

ios.emulator.prepare_cryptexes#

Install iOS cryptex files

Copy a restored virtual disk and install both original iPhone Cryptex filesystems into Preboot. Rechecks original image digests and every copied file; preserves the sealed System. Optional kernel, boot_manifest and device_tree together prepare original ignition grafts and an explicit, version-pinned research kernel: local exact-byte Cryptex seal pins replace Apple authentication for these two objects only. Optional gpu_trustcache installs the PCC GPU bundle and source-built compiler/bridge with exact hash-pinned executable-mapping exceptions and explicit local platform grants. Optional installer_control permits only the exact original signed installer's otherwise-denied MIG 3404 query through its original kernel handler. Optional development_tools adds one mapping exception for its fully verified owned ARM64e library; requires both preceding options. Offline, durable, cancellable.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
diskstringyesnon-empty; Closed restored virtual GPT disk; a new independent copy is modified.
userlandstringyesnon-empty; Completed ios.emulator.prepare_userland output.
gpu_trustcachestringnon-empty; Optional original vphone600 23E5207q StaticTrustCache IM4P, checked against the imported PCC manifest. Requires all three research inputs and a userland import containing the source-built GPU compiler. Explicitly grants local platform trust and executable mapping to the exact verified graphics CodeDirectories, including the compiler bridge when supplied; does not establish working rendering or Apple authentication.
installer_controlbooleanfalseOpt-in compatibility for the pinned original iOS installer under native service control. Requires kernel, boot_manifest and device_tree. Only KERN_DENIED for MIG 3404 from the exact signed installd CodeDirectory continues into the original kernel handler; other callers and errors are unchanged. Does not establish successful installation or a visible app.
development_toolsstringnon-empty; Optional completed development-signing guest-tools build. Requires gpu_trustcache and installer_control. Verifies the full payload and pins a local executable-mapping exception to its exact signed ARM64e installer library; other code, native signature/resource validation and app-install results retain their original behavior. Install this same payload separately with prepare_guest_tools.
kernelstringnon-empty; Explicit opt-in to the local Cryptex research policy. Exact original vphone600 23E5207q decompressed kernel; a private modified copy is emitted. Requires boot_manifest and device_tree.
boot_manifeststringnon-empty; Existing local DER IM4M used for this restored guest. Its exact bytes are pinned; this does not authenticate it as Apple-signed. Requires kernel and device_tree.
device_treestringnon-empty; Prepared normal-boot vphone600 device tree with the same ECID as the restored guest and SEP storage. Requires kernel and boot_manifest.
supportstringyesnon-empty
helper_enginestringyesnon-empty
helper_kernelstringyesnon-empty
biosstringyesnon-empty
outputstringyesnon-empty; New private directory; existing output is never overwritten.
accelstring"kvm"one of kvm, tcg
secondsinteger9001 to 3600
tomaccoctl call ios.emulator.prepare_cryptexes '{"disk":"<disk>","userland":"<userland>","support":"<support>","helper_engine":"<helper_engine>","helper_kernel":"<helper_kernel>","bios":"<bios>","output":"<output>"}'

ios.emulator.prepare_research#

Prepare iOS test system

Package existing, checksummed full-system research inputs into a new isolated bundle. Runs as a durable job; retains source images. Does not supply display, SEP, native app deployment or a complete iOS simulator.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
firmwarestringyesnon-empty; Prepared restore-shell firmware directory
inputsstringyesnon-empty; Directory from prepare_ios_research_inputs.py
bootstrapstringyesnon-empty; Research bootstrap directory, with manifest.json and image-manifest.json
imagestringyesnon-empty; Assembled APFS image matching the image manifest
outputstringyesnon-empty; New bundle directory; existing output is never replaced
exclavestringnon-empty; Optional matching bundle from prepare_ios_exclave.py; required by exclave-dart inputs
tomaccoctl call ios.emulator.prepare_research '{"firmware":"<firmware>","inputs":"<inputs>","bootstrap":"<bootstrap>","image":"<image>","output":"<output>"}'

ios.emulator.prepare_pmp#

Prepare PMP firmware

Verify the matching T8140 BuildManifest digest, unwrap local PMP firmware and preserve its Mach-O segments in a new ELF bundle. No downloads or instruction changes.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
firmwarestringyesnon-empty; Extracted local firmware tree containing the PMP IM4P
manifeststringyesnon-empty; Matching BuildManifest.plist
outputstringyesnon-empty; New prepared bundle directory
boardstring"d47ap"matching ^[a-z0-9]{1,32}$
tomaccoctl call ios.emulator.prepare_pmp '{"firmware":"<firmware>","manifest":"<manifest>","output":"<output>"}'

ios.emulator.inspect_pmp#

Check PMP execution

Recheck PMP container, payload, executable and source manifest. Inspect isolated ARM64 execution and diagnostic capabilities. CPU family and RAM are explicit laboratory policy; not measured silicon state.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; QEMU executable with the PMP machine and strict bus/system-register fault tracing
firmwarestringyesnon-empty; Verified bundle from ios.emulator.prepare_pmp
controllerstring"none"one of none, asc-v6; Experimental ASC/KIC model with architectural ARM timers and explicit laboratory policies; no independent KIC timer banks, measured silicon behavior or power services.
stop_atstring""matching ^(|0x[0-9a-fA-F]{1,16})$; Optional ARM64 instruction breakpoint inside the verified executable image. Capture and stop the job without changing guest code.
capture_rambooleanfalseSave the isolated 2 MiB RAM after stopping, with exact physical base and SHA-256 in the report.
cpu_midrstring"0x610f0000"matching ^0x[0-9a-fA-F]{1,8}$; Explicit 32-bit laboratory MIDR. Selects firmware CPU setup; does not implement the named silicon or change QEMU max ISA features.
cpu_affinitystring"0x0"matching ^0x[0-9a-fA-F]{1,10}$; MPIDR affinity fields only (mask 0xff00ffffff). A single CPU; topology bits may select a different firmware path. Not a measured PMP identity.
tomaccoctl call ios.emulator.inspect_pmp '{"engine":"<engine>","firmware":"<firmware>"}'

ios.emulator.pmp#

Run PMP firmware

Execute original PMP ARM64 instructions in isolated memory, optionally with the experimental ASC controller. Capture actual bus faults and CPU registers with bounded logs and cleanup. Shares admission with all iOS jobs. Firmware boot, power services and graphical iOS are not provided.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; QEMU executable with the PMP machine and strict bus/system-register fault tracing
firmwarestringyesnon-empty; Verified bundle from ios.emulator.prepare_pmp
secondsinteger101 to 60
controllerstring"none"one of none, asc-v6; Experimental ASC/KIC model with architectural ARM timers and explicit laboratory policies; no independent KIC timer banks, measured silicon behavior or power services.
stop_atstring""matching ^(|0x[0-9a-fA-F]{1,16})$; Optional ARM64 instruction breakpoint inside the verified executable image. Capture and stop the job without changing guest code.
capture_rambooleanfalseSave the isolated 2 MiB RAM after stopping, with exact physical base and SHA-256 in the report.
cpu_midrstring"0x610f0000"matching ^0x[0-9a-fA-F]{1,8}$; Explicit 32-bit laboratory MIDR. Selects firmware CPU setup; does not implement the named silicon or change QEMU max ISA features.
cpu_affinitystring"0x0"matching ^0x[0-9a-fA-F]{1,10}$; MPIDR affinity fields only (mask 0xff00ffffff). A single CPU; topology bits may select a different firmware path. Not a measured PMP identity.
tomaccoctl call ios.emulator.pmp '{"engine":"<engine>","firmware":"<firmware>"}'

ios.emulator.research#

Start iOS test run

Boot a disposable copy of a prepared full-system iOS research image directly on Linux. Capture real platform observations, dedicated serial logs and boot pixels. Identical active requests reuse their job; conflicting iOS jobs are rejected. Completion speaks for those observations only, never SpringBoard or graphical apps.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
enginestringyesnon-empty; Darwin/SPTM QEMU executable
firmwarestringyesnon-empty; Prepared full-system research bundle
variantstring"release"one of release, research
secondsinteger3601 to 900
boot_displayobject{"width":390,"height":844}
boot_display.widthintegeryes64 to 4096
boot_display.heightintegeryes64 to 4096
tomaccoctl call ios.emulator.research '{"engine":"<engine>","firmware":"<firmware>"}'

ios.emulator.report#

Read iOS test run

Read an owned iOS job report and its actual diagnostic/frame artifacts. A missing report while queued is pending, not success. For an ios.emulator.paravirtual job the reply also carries stop: the record written when the guest was stopped through ios.emulator.control (whether the native worker was idle and whether the guest confirmed flushing its storage), or null if it ended any other way. After a stop whose exit request failed, the record carries guest_storage_flushed false and quit_acknowledged false.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
job_idstringyesmatching ^[a-f0-9]{32}$
tomaccoctl call ios.emulator.report '{"job_id":"<job_id>"}'

ios.emulator.memory#

Read PMP memory

Read a bounded physical-memory range from a completed PMP RAM capture. Verify its size and SHA-256 before returning exact hex, ASCII and little-endian words; never reads arbitrary host files or a live guest.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
job_idstringyesmatching ^[a-f0-9]{32}$
addressstringyesmatching ^0x[0-9a-fA-F]{1,16}$; Physical address inside PMP RAM [0x1000000, 0x1200000)
lengthinteger2561 to 4096
tomaccoctl call ios.emulator.memory '{"job_id":"<job_id>","address":"<address>"}'

ios.virtual.status#

Virtual iOS device: status

Read whether a virtual iOS device profile is configured, its current run, phase, guest job and any start or stop that is in progress. A guest job started from another data directory has no guest_state here; that is unknown, not stopped. Experimental research runtime: the boot takes many minutes and the guest runs several times slower than real time. Also reports model (for the next start), running_model (of the current run), running_panel (the current run's panel as WIDTHxHEIGHT@HZ:WIDTH_MMxHEIGHT_MM, as its guest job's request gave it to the engine, so a later edit of the profile does not change it; null when that boot named no panel. Without a guest job: the model's own panel, or the profile's display profile for the Home-button phone; null for an unknown recorded model), models (the choice: id, name, panel, and source apple or derived) and guest_ended when the guest has ended on its own since the last start, with guest_saved telling whether it flushed its storage and closed cleanly first (as a session does at its time limit). application_process reports only the matching active job and application; it proves no process lifetime or visible scene. applications lists the developer apps the profile names (bundle path, bundle_id); the start installs them from the guest-tools build that carries them. phase_started and phase_elapsed_seconds tell when the current phase began and how long it has run, and phases lists each phase of the current start or stop with its started time and seconds (empty for a state written before phases were logged). start_timing and stop_timing break the last start and stop down into steps with their seconds (start: checks, preparation, disk_copy with its method, guest_tools, boot_request, input_hashing with its bytes, launcher, boot, app_install, guest_settings, unlock_and_launch, display_check; stop: guest_exit, output_hashing, finalize), with total_seconds, slowest and run; a step whose times are unknown is folded into the next one, which lists it under includes; null before the first start that recorded them. copy_progress, only in the preparing phase, compares the bytes of the new disk copy with those of its base (bytes_done, bytes_total, percent) and names the preparation's step and disk; a reflinked copy is complete at once. start_disk says whether the next start's disk copy fits in the profile's installs: needed_bytes, free_bytes, reclaimable_bytes (older copies the start deletes first), reflink and enough, the check ios.virtual.start refuses with; the status writes nothing to find out, so on a filesystem that might clone files and lacks room for a full copy enough and reflink are null until a start probes it. start_disk.method is how that start makes its root disk (copy, overlay or flatten), and start_disks the profile's choice (copy or overlay). Null when the base's disks or the filesystem cannot be read. last_run says in plain words how the last finished run ended: verified, the launcher's outcome, a summary, each reason it was not verified (no exit code 0, incomplete storage hashes, no confirmed flush, the graphics memory guard) and a remedy; null before the first run that recorded it. next names what to call when a start or stop ended halfway, with action (resume, unlock_then_resume, stop, record_end, start_again or wait), call, arguments, resumes (true when the call goes on with the running guest without a new disk copy) and why; null when nothing is left halfway. host reports on every call the graphics card's memory (gpu: source guard, the running guest's last guard check, or sysfs, the card with the most memory; used_bytes, total_bytes, free_bytes, guard_limit_free_bytes, the eighth the graphics memory guard keeps, level ok, low under a quarter free or short under the guard's limit, and short_checks and guard_stopping from the guard; null when no card reports memory) and the host's pressure (pressure: Linux PSI full avg10 and avg60 for io and memory against the start's admission limits, blocked naming those over them, observed false without PSI). liveness, while the guest runs, says whether it still shows signs of life: state live, quiet (no new frame, console output or guest answer for stall_seconds; normal for a still screen), stalled (the guest service did not answer a probe and neither it nor the screen showed life for stall_seconds) or booting (not judged); last_seen with the newest frame's number, the console log's size and the guest's last answer, each with when it last changed; and for a stalled phone recovery, the typed calls to make in order: ios.emulator.guest restart_ui, then ios.virtual.stop. The status never acts on the phone itself; it remembers the signs in liveness.json in the guest job's run directory.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
probebooleanAlso ask the running guest service for its status, bounded to 10 seconds, so that liveness can tell a still screen from a guest that no longer answers. Changes nothing in the guest.
tomaccoctl call ios.virtual.status

ios.virtual.host_check#

Check this machine for the virtual iOS device

Check whether this machine can run the virtual iOS device, before any setup step or start: architecture (x86_64), CPU (logical processors, VT-x or AMD-V, AVX2), /dev/kvm for the disk-preparing helper, memory (fail under 12 GiB, warn under 16 GiB), free disk on the filesystem of directory (by default the configured profile's installs, needing 32 GiB for one start's disk copy, else the data directory, needing 100 GiB for the setup chain), Vulkan drivers and the card with the most graphics memory (fail under 8 GiB, warn under 16 GiB or when under an eighth is free), the helper support's APFS module against the running kernel, and passt for the guest network. Each entry in checks has check, status (pass, warn or fail), found, message and a remedy unless it passed; ready is false when any check fails. host has logical_cpus, physical_cores (one per package and core among the CPUs this process may use) and memory_total_bytes, and recommend compares the configured profile's boot settings with them: each entry names a setting (boot.host_cpu_policy: performance with at least two physical cores more than application processors, scheduler with fewer cores than processors; boot.translation_cache_mib: 1024 under 16 GiB of memory, 2048 from 32 GiB), its current and suggested value and the reason; empty when they suit the host or no profile is configured. Nothing is changed. Reads /proc, /sys, PATH and the filesystem only; starts and writes nothing. A pass means the machine meets what the device was measured with, not that it was qualified on it.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
directorystringnon-empty; Absolute existing directory whose filesystem will hold the setup chain's outputs; checked against the 100 GiB the chain needs
supportstringnon-empty; Absolute helper support directory (prepare_ios_emulator.py output); by default the profile's, else the checkout's .tooling/ios-sptm/support
tomaccoctl call ios.virtual.host_check

ios.virtual.doctor#

Check the virtual iOS device

One pass, warn or fail report on whether the configured virtual iOS device can start and how its last run ended, each item with a remedy: profile (profile.json and state.json readable), profile_inputs (every input profile.json names, checked as tools/ios_virtual_profile.py checks a new profile: engine, guest-control backend, helper, boot files, trust cache, device tree, boot manifest, graphics payload, guest tools, kernel and the verified base), guest_tools_sources (the guest tools built from the guest sources this checkout has), engine_patches (the engine built from this checkout's engine patches, from its build-manifest.json), saved_states (each saved state and the verified base still have their disks), last_run (the verdict ios.virtual.status shows as last_run), start_disk (whether the next start's disk copy fits, as the start refuses it), every ios.virtual.host_check item, gpu_now (the graphics card's free memory against the graphics memory guard's limit) and pressure_now (host I/O and memory pressure against the start's admission limits). ready is false when any item fails; status is the worst. Without a profile, profile fails and the machine's items still run. Reads files only, which takes some seconds for the kernel and payloads; starts and writes nothing.

Annotations: read-only, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.doctor

ios.virtual.setup#

Set up the virtual iOS device

Walk the virtual iOS device's setup chain (docs/virtual-ios/setup.md) as one guided sequence. step plan (the default) reads which of the 17 stages are done from their outputs under work and the checkout's .tooling and writes nothing but work/inputs.json: stages lists each with stage, what, state (done, pending, partial when an earlier run stopped halfway, unknown when only the person can tell), detail, needs (arguments it still lacks), ask_first, why_ask, manual and the commands it would run; next is the first stage not done; inputs is tools/check_ios_inputs.py's check of the person's own files and host is ios.virtual.host_check. step run starts the next stage, or the one named by stage, as a durable job (or the operation it is, such as ios.emulator.prepare_userland) and returns it; a done stage is skipped without running anything. run refuses with ios_setup_out_of_order before an earlier stage is done, ios_setup_needs_input when the person's files are not given, ios_setup_inputs_invalid when they are the wrong ones, ios_setup_partial for a half-finished output (move it aside; nothing is deleted), ios_setup_confirm for stages that build for long or need the person's template until confirm is true, ios_setup_manual for the restore, which boots a guest and is run by hand from its commands, and ios_setup_needs_checkout for the engine without a source checkout. Nothing of Apple's is fetched: the restore images, decrypted, the public template and the IM4M stay the person's to supply.

Annotations: writes, idempotent, open world.

ArgumentTypeRequiredDefaultDetails
stepstring"plan"one of plan, run; plan reports; run starts a stage
workstringyesnon-empty; Absolute directory for the chain's outputs (boot, trees, storage, userland, cryptex, kernel, guest tools, profile); created by the first stage that writes there
stagestringone of engine, support, linker, gpu_plugin, boot, trees, manifest, userland, restore_runtime, restore, cryptex_first, guest_tools, cryptex, kernel, first_install, profile, configure; With run: the stage to start; by default the first that is not done
confirmbooleanfalseWith run: the person agreed to a stage that asks first
restoredbooleanfalseThe restore finished with restore_completed:true, so the stages after it may run
iphone_firmwarestringnon-empty; Absolute unpacked iPhone17,3 24A446 restore image
pcc_firmwarestringnon-empty; Absolute unpacked PCC 23E5207q restore image
iphone_os_imagestringnon-empty; Absolute decrypted iPhone OS image; by default found under decrypted/ in iphone_firmware
iphone_systemos_imagestringnon-empty; Absolute decrypted iPhone SystemOS Cryptex image; by default found under decrypted/ in iphone_firmware
pcc_system_imagestringnon-empty; Absolute decrypted PCC system image; by default found under decrypted/ in pcc_firmware
templatestringnon-empty; Absolute public ticket.shsh2 research template, for the manifest stage
manifeststringnon-empty; Absolute IM4M; by default work/your.im4m, which the manifest stage makes
vphone_clistringnon-empty; Absolute vphone-cli checkout at the pinned revision, for the GPU plugin
swift_libstringnon-empty; Absolute Linux Swift toolchain's usr/lib/swift, for the linker
clangstringnon-empty; Absolute clang 22, for the guest tools
helper_kernelstringnon-empty; Absolute kernel for the disk-preparing helper; by default the support's apfs/vmlinuz, else the running kernel's
jobsinteger81 to 64; Parallel build jobs for the engine and linker
cpu_optimizationstring"portable"one of portable, native; Engine build: portable, or native for this CPU only (what was measured)
tomaccoctl call ios.virtual.setup '{"work":"<work>"}'

ios.virtual.configure#

Set up virtual iOS device

Select the prepared profile directory used by ios.virtual.start and ios.virtual.stop. The directory must contain a format 1 profile.json (prepare and boot inputs, graphics bundle, engine, application, installs) and a state.json naming the last closed run. Both are validated; nothing is created or repaired. With model alone, the configured profile is kept and only the device model for the next start changes. add_application, remove_application and application change the profile's own app list and the app the start opens, in profile.json; clang names the clang 22 the start rebuilds guest tools with. With add_application and deploy, the app also goes onto the running phone at once (ios.virtual.deploy, in deploy_job). Choosing another directory is refused with ios_virtual_busy while a start or stop runs and with ios_virtual_running while the current profile's phone runs; each directory configured is remembered for ios.virtual.profiles.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
directorystringnon-empty; Existing profile directory. Becomes the profile of the next start; refused while the current profile's phone runs or a start or stop is in progress.
modelstringmatching ^[a-z0-9-]{0,32}$; Device model the next start describes the phone as (an id from ios.virtual.status models). Empty: the profile's own description, a phone with a Home button. Takes effect at the next start; the disks stay the same.
add_applicationstringnon-empty; Host path of an .app directory built for arm64 iOS to add to the profile's applications. It is inspected first (Info.plist, arm64 executable, only allowlisted entitlements) and refused with the reason otherwise. The next start installs it; when the profile's guest tools do not carry it as it is now, the start rebuilds them with the profile's apps and pins the new signing adapter in a derived kernel itself (clang 22 needed: clang, or a clang-22 on PATH).
add_entitlementsstringEntitlements plist to request for add_application; empty: the executable's embedded entitlements, or only application-identifier.
remove_applicationstringnon-empty; Host path or bundle identifier of a listed app to remove from the profile. If it was the app the start opens, the owned diagnostic is opened again.
applicationstringmatching ^[A-Za-z0-9][A-Za-z0-9.-]{0,254}$; Bundle identifier of the app the start opens after boot: the owned diagnostic (io.github.marselnenaj.tomacco.research-app) or one of the listed applications.
clangstringHost path of the clang 22 executable the start rebuilds guest tools with (profile.json toolchain.clang); empty removes it, so a clang-22 on PATH or the build's recorded toolchain is used.
deploybooleanfalseWith add_application: install it on the running phone now, without a reboot. Refused with ios_virtual_not_running when the phone is off.
deploy_open_appbooleantrueWith deploy: open the app afterwards.
start_disksstringone of copy, overlay; How the next start makes its root disk (profile.json start_disks): overlay makes an empty qcow2 overlay on the base run's root disk with qemu-img (beside the profile's helper engine or on PATH) and copies only the small auxiliary and SEP disks; copy copies the whole 32 GiB root disk. A profile that names neither lays overlays while a qemu-img is found and copies without one. Without qemu-img, or after 8 overlays on one base, the start copies or flattens instead and says why. Experimental: run on a live phone, overlays layered three times and flattened once.
stall_secondsinteger30 to 86400; How long a running phone may show no new frame, console output or guest answer before ios.virtual.status calls it quiet, and, after a probe the guest service did not answer, stalled (profile.json stall_seconds; default 300).
tomaccoctl call ios.virtual.configure

ios.virtual.profiles#

Profiles of virtual iOS device

List the virtual iOS device profile directories configured so far (up to 32, the newest first), each with current, valid (with error code and message when its profile.json or state.json cannot be used), run, phase, closed, running (its guest job is alive), application, installs, start_disks and saved_states (the number of saved states). current is the directory the next start uses; switch with ios.virtual.configure directory. can_switch is false while a start or stop runs (busy) or the current profile's phone runs. Reads files only.

Annotations: read-only, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.profiles

ios.virtual.apps#

Apps of virtual iOS device

List the apps of the configured virtual iOS device in one place: the owned diagnostic, every app profile.json lists (bundle path, bundle_id, name, display_name, entitlements) with version, build and minimum_os read from its bundle's Info.plist on the host now (bundle_readable false with bundle_error when the bundle is gone; null when it could not be read at all; bundle_id_changed when the bundle now names another identifier), and any app the current run has installed that the profile no longer lists (listed false). installed and installed_cdhash are the current run's record of what a start or ios.virtual.deploy installed; they do not say whether the bundle changed since, which the next start checks itself. opens_at_start marks the app the start opens. Reads files only; no guest is asked.

Annotations: read-only, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.apps

ios.virtual.start#

Start virtual iOS device

Start the configured virtual iOS device as one durable job: prepare a new private disk copy from the last cleanly closed run (reinstalling guest tools and restoring the Preboot Metal driver the guest removes), boot it, attempt passcode-free unlock with Home presses and request the configured app once the native screen-state query confirms unlock. Launch acceptance does not establish a visible application scene. A guest that is already running is adopted. Disks of a run that did not close with verified storage are never reused. Follow progress with ios.virtual.status and job.logs. Experimental research runtime: the boot takes many minutes and the guest runs several times slower than real time. A late screen-query receipt is followed by its original request ID. application_process reports only the matching active job and application; it proves no process lifetime or visible scene. A start that would make a disk copy is refused with ios_virtual_disk_space, before anything is deleted or copied, when the filesystem of the profile's installs lacks the base's disks plus 1 GiB, counting the older copies the start deletes first; a filesystem that clones files (reflink) needs only the 1 GiB. A start that boots a guest returns warnings, a list of {check, message}: check gpu when under a quarter of the graphics card is free (the graphics memory guard stops a session under an eighth), check pressure when the host's I/O or memory pressure is over the admission limits the start waits for; neither refuses the start. Refused with ios_virtual_host_too_small on a host that is not x86_64 or has less than 12 GiB of memory.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
open_appbooleantrueFalse boots and waits for the guest service only.
tomaccoctl call ios.virtual.start

ios.virtual.stop#

Stop virtual iOS device

Flush guest storage, stop the running virtual iOS device, wait for its final report and record whether its disks closed with exit 0 and complete storage hashes. Only such a run is used as the base of the next start.

Annotations: writes, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.stop

ios.virtual.unlock#

Unlock virtual iOS device

Leave the running virtual iOS device's lock screen the way a start does, as one short job: observe the native screen state, then wake the display and press Home (or swipe up on a model without a Home button) with the start's pacing until a fresh observation says it is unlocked. The job's screen is unlocked, or not-checked when the guest tools cannot report the screen (nothing is pressed then). A passcode is never entered: a lock screen with one, or one that stays locked for 10 minutes, fails the job. ios.virtual.deploy (before it opens the app) and testing.run on the virtual-iphone device unlock the same way first; testing.run fails with ios_virtual_locked when it cannot. Refused with ios_virtual_not_running when the phone is off and ios_virtual_busy during a start or stop.

Annotations: writes, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.unlock

ios.virtual.cleanup#

Clean up virtual iOS device disk copies

Delete older disk copies of the configured virtual iOS device from the profile's installs directory, or with dry_run (the default) only report what would go. Only copies the start recorded as its own (state.json created) that lie directly in installs and are named guest-install* are candidates; the verified base, the current run and a start's attempt are always kept (kept, with the reason). Each copy's files are locked before it is deleted, so a copy a running guest holds is refused with the reason. A guest-install* directory the state does not record is listed under unrecorded and never deleted. copies lists each candidate with path, bytes (allocated on disk; blocks a reflinked copy shares with its base are counted in full) and removed; reclaimable_bytes is their sum and freed_bytes what was deleted. A real cleanup is refused with ios_virtual_busy while a start or stop runs.

Annotations: destructive, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
dry_runbooleantrueOnly report; delete nothing. false deletes the candidates.
tomaccoctl call ios.virtual.cleanup

ios.virtual.states#

Saved states of virtual iOS device

List the saved phone states of the configured virtual iOS device (saved-states.json in the profile directory): each with name, run, install, saved (time), model (the model its run was last started as), exists (whether its root disk is still there), bytes (allocated in its install directory, blocks shared with other copies counted in full) and base (whether the next start begins from it). base is the next start's base run, and base_saved_as the names that keep it. Reads files only.

Annotations: read-only, idempotent, closed world.

Takes no arguments.

tomaccoctl call ios.virtual.states

ios.virtual.state_save#

Save state of virtual iOS device

Keep the last verified run of the configured virtual iOS device under a name, so a later start can begin from it with ios.virtual.state_use. Nothing is copied: a verified run's disks never change after its stop, and the start's pruning and ios.virtual.cleanup keep every saved run's disk copy and the copies its overlay reads. Refused with ios_virtual_no_verified_run when no run has been stopped cleanly, ios_virtual_state_exists for a name in use unless replace, and ios_virtual_busy while a start or stop runs. A running phone is fine: the saved run is the last verified one, not the running one.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
namestringyesmatching ^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$; Name of the saved state: 1 to 64 letters, digits, spaces, '.', '_' or '-', starting with a letter or digit.
replacebooleanfalsePoint an existing name at the current verified run.
tomaccoctl call ios.virtual.state_save '{"name":"<name>"}'

ios.virtual.state_delete#

Delete saved state of virtual iOS device

Remove a saved state's name and delete the disk copy it kept, when that copy is one the start made in the profile's installs and nothing else keeps it: not the verified base, the current run, a start's attempt, another saved state or a copy an overlay among those reads. kept_because says why a copy stayed; removed and freed_bytes what went. A copy a running guest holds is refused, not deleted. Refused with ios_virtual_state_unknown for an unknown name and ios_virtual_busy while a start or stop runs.

Annotations: destructive, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
namestringyesmatching ^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$; Name of the saved state: 1 to 64 letters, digits, spaces, '.', '_' or '-', starting with a letter or digit.
tomaccoctl call ios.virtual.state_delete '{"name":"<name>"}'

ios.virtual.state_use#

Start virtual iOS device from saved state

Make a saved state the base of the next ios.virtual.start of the configured virtual iOS device: state.json then names its run as the verified run, and the start copies from it (or lays an overlay on it). The phone must be stopped (ios_virtual_running otherwise). When the current verified run has no saved name, a later start's pruning may delete its disks, so this is refused with ios_virtual_state_unsaved unless discard_current. Refused with ios_virtual_state_missing when the state's disks are gone and ios_virtual_busy while a start or stop runs. previous is the base it replaced.

Annotations: writes, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
namestringyesmatching ^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$; Name of the saved state: 1 to 64 letters, digits, spaces, '.', '_' or '-', starting with a letter or digit.
discard_currentbooleanfalseGive up the current verified run even though no saved state keeps it.
tomaccoctl call ios.virtual.state_use '{"name":"<name>"}'

ios.virtual.deploy#

Install app on running virtual iOS device

One step from a built .app directory to the app on the running phone, without a reboot: inspect the bundle, sign a copy ad hoc, exchange spare entries of this boot's trust cache for its hashes while the guest is stopped for a moment, send it over the guest channel, name it to the signing adapter, install it through installd and open it. A durable job: follow job.logs, whose deploy events carry each step's seconds. When this run installed the same signed app before (the cdhash of the ad hoc signed copy, which seals every file of the bundle), nothing is trusted, sent or installed: the deploy event unchanged follows signed and the app is only opened; force installs it again. The profile lists the app afterwards, so the next start stages it. Refused with ios_virtual_not_running when the phone is off: use ios.virtual.configure add_application and start it. A boot has 512 spare entries and the adapter's table of the boot takes 32 apps, frameworks and extensions.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
bundlestringyesnon-empty; Host path of an .app directory built for arm64 iOS.
entitlementsstringEntitlements plist to request; empty: the executable's embedded entitlements, or only application-identifier.
open_appbooleantrueFalse installs without opening.
forcebooleanfalseInstall even when this run has the same signed app installed already.
tomaccoctl call ios.virtual.deploy '{"bundle":"<bundle>"}'

ios.virtual.debug#

Debug process on virtual iOS device

Join lldb to one process of the running phone: a durable job holds a TCP port on the host's loopback and, once lldb connects, copies its bytes to /usr/libexec/debugserver in the guest, attached to the target. The first line of job.logs is an event with the port and the lldb command, process connect connect://127.0.0.1:&lt;port&gt;. The app needs get-task-allow (ios.virtual.deploy with such entitlements). One session per job; it ends when lldb disconnects. Anything that reaches this port controls that process, so it listens on loopback only.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
targetstringyesmatching ^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}$; The process's decimal PID or its name.
portinteger00 to 65535; TCP port on 127.0.0.1; 0 picks a free one.
secondsinteger360010 to 86400; Longest the session lasts.
tomaccoctl call ios.virtual.debug '{"target":"<target>"}'

ios.virtual.crash_reports#

Save crash reports of virtual iOS device

Read the running virtual iPhone's crash reports whose file names start with process (an app's executable name, or any report name prefix) through the guest service, as ios.emulator.guest crash_reports does, and keep each complete one as a file on the host at directory/&lt;owner&gt;/&lt;name&gt; (owner mobile or root). reports lists every match: owner, name, path, bytes, sha256, saved, and the pid, process, capture_time and incident_id parsed from its bytes; a report the guest could not read whole, or with a name or owner that is not a plain report name, has saved false, no path and the reason. Pass a saved path to ios.crash_symbolicate. The guest returns at most three reports per directory and 512 KiB of content in all, by descending file name, which is not crash time; collection_complete false means some were left out, and an empty list never proves that nothing crashed. Reports are never removed from the guest. Without directory, a new directory in Tomacco's data directory is used. Refused with ios_virtual_not_running when the phone is off.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
processstringyesnon-empty, up to 255 characters; Report name prefix, usually the app's executable name
directorystring""Absolute host directory for the reports; created if missing. Empty: a new directory in Tomacco's data directory.
tomaccoctl call ios.virtual.crash_reports '{"process":"<process>"}'

ios.virtual.syslog#

Log of virtual iOS device

Read the configured virtual iPhone's log as structured lines: the guest's serial console (uart.log of its guest job, running or last), where the kernel prints and launchd forwards log records. Each line has offset (its byte offset), time (as the line gives it, or null), process (for a launchd record the service's last label part, for a UIKit application its executable-like last identifier part; kernel for a line of no known form), pid, service, sender (the forwarding subsystem or the library in name(library)[pid]), level (emergency, alert, critical, error, fault, warning, notice, info, debug, default, or null) and message (cut at 2000 characters). bundle_id keeps one app's lines, across relaunches, and the reply names its executable under app; refused with ios_virtual_application_unknown for an app the profile does not list. Without offset the last 256 KiB are read; pass next_offset back to follow the log. Only complete lines are returned; an unfinished one comes in a later read. more says a read stopped at max_lines or 1 MiB before the end; reset says an offset past the end (another run's log) started over at 0. job_id names the guest job read and running whether it still runs. Reads a host file; the guest is not asked. Refused with ios_virtual_not_running when no run has a guest job and ios_virtual_log_unavailable before the console log exists.

Annotations: read-only, idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
processstring""up to 128 characters; Only lines whose process or service contains this, ignoring case. Empty: all.
bundle_idstring""up to 255 characters; Only this app's lines: those of the process named after its executable (read from the bundle the profile lists) and launchd's lines about its service. By name, so a relaunched app's lines match too. Empty: all.
levelsarray of stringitems one of emergency, alert, critical, error, fault, warning, notice, info, debug, default, none; up to 11 items; Only lines with one of these levels; none matches lines without a level, such as the kernel's. Empty or absent: all.
containsstring""up to 256 characters; Only lines whose message contains this text.
offsetintegerat least 0; Byte offset to read on from: a previous reply's next_offset. Absent: the last 256 KiB.
max_linesinteger5001 to 5000; The most lines returned in one reply.
tomaccoctl call ios.virtual.syslog

ios.virtual.screenshot#

Screenshot of virtual iOS device

Save the running virtual iPhone's current screen as a PNG at path, from the same frame ios.emulator.control frame with transport file gives a viewer. frame none (default) keeps the picture as it is; rounded cuts the screen's corners round with the radius of the model the phone runs as (square for the Home-button phone) and makes outside them transparent; device also puts a plain black bezel around it, a drawing with rounded outer corners and no buttons, not a picture of the phone. The corner radius is the circular arc of the device catalogue's value, not Apple's continuous curve. MCP returns a PNG of up to 4 MiB inline. Refused with ios_virtual_not_running when the phone is off.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the PNG to write, ending in .png; an existing file is replaced.
framestring"none"one of none, rounded, device; none: the screen as it is; rounded: round, transparent corners; device: rounded screen inside a plain bezel.
tomaccoctl call ios.virtual.screenshot '{"path":"<path>"}'

ios.virtual.record#

Record virtual iOS device screen

Record the running virtual iPhone's screen for seconds into an H.264 MP4 at path, as a durable job: the frames ios.emulator.control frame with transport file gives a viewer, polled fps times a second and encoded by ffmpeg as they come (ffmpeg must be installed). A frame that has not changed, or a poll slower than one frame, repeats the last picture, so the video lasts as long as the recording; the guest itself draws about 10 changed frames a second on this host. The job's result names the file and counts frames_written, frames_captured and frames_distinct; a phone that stops ends the recording early with what it has (ended_early). Follow it with job.wait and job.logs. job.send stop ends the recording early and keeps the video recorded so far (stopped_early). One recording at a time. Refused with ios_virtual_not_running when the phone is off.

Annotations: writes, not idempotent, closed world.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute path of the MP4 to write, ending in .mp4; an existing file is replaced when the recording succeeds.
secondsinteger101 to 600; How long to record.
fpsinteger101 to 30; Frames per second of the video, and how often a frame is asked for.
tomaccoctl call ios.virtual.record '{"path":"<path>"}'

View this page as Markdown on GitHub