Developer Mode via AMFI XPC

How iOS developer mode is enabled programmatically, based on TrollStore's implementation.

XPC Service

Mach service: com.apple.amfi.xpc

AMFI (Apple Mobile File Integrity) daemon exposes an XPC endpoint for developer mode control on iOS 16+.

Required Entitlement

com.apple.private.amfi.developer-mode-control = true

Without this entitlement the XPC connection to amfid is rejected.

Message Protocol

Messages are serialized using private CoreFoundation-XPC bridge functions:

extern xpc_object_t _CFXPCCreateXPCMessageWithCFObject(CFTypeRef obj);
extern CFTypeRef _CFXPCCreateCFObjectFromXPCMessage(xpc_object_t obj);

Request

NSDictionary with a single key:

@{@"action": @(action)}

Actions

ActionValueBehavior
kAMFIActionArm0Arm developer mode — takes effect on next reboot, user must select "Turn On"
kAMFIActionDisable1Disable developer mode immediately
kAMFIActionStatus2Query current state

Response

XPC reply dict contains a "cfreply" key holding the CF-serialized response:

xpc_object_t cfReply = xpc_dictionary_get_value(reply, "cfreply");
NSDictionary *dict = _CFXPCCreateCFObjectFromXPCMessage(cfReply);

Response fields:

KeyTypeDescription
successBOOLWhether the XPC call succeeded
statusBOOLCurrent developer mode state (for Status action)
armedBOOLWhether armed for reboot (for Arm action)
errorNSStringError description if success is false

Arming Flow

  1. Query status (kAMFIActionStatus)
  2. If already enabled, done
  3. Send arm (kAMFIActionArm)
  4. Device must reboot; user selects "Turn On" in the prompt
  5. Developer mode is now active

Arming does not enable developer mode immediately. It sets a flag that triggers the enable prompt on the next reboot. Disabling (kAMFIActionDisable) takes effect immediately.

TrollStore Reference

Source: references/TrollStore/RootHelper/devmode.m

TrollStore separates privileges: the main app has no AMFI entitlement; all privileged operations go through RootHelper which has com.apple.private.amfi.developer-mode-control.

Key functions:

  • checkDeveloperMode() — returns current state, YES on iOS <16 (devmode doesn't exist)
  • armDeveloperMode(BOOL *alreadyEnabled) — check + arm in one call
  • startConnection() — creates and resumes XPC connection to com.apple.amfi.xpc
  • sendXPCRequest() — CF dict → XPC message → sync reply → CF dict

vphoned Implementation

Added as devmode capability in vphoned guest agent:

Protocol Messages

Status query:

{"t": "devmode", "action": "status"}
→ {"t": "ok", "enabled": true}

Enable (arm):

{"t": "devmode", "action": "enable"}
→ {"t": "ok", "already_enabled": false, "msg": "developer mode armed, reboot to activate"}

Entitlements

Added to Scripts/VPhoned/entitlements.plist:

<key>com.apple.private.amfi.developer-mode-control</key>
<true/>

Host-Side API (historical design)

The calls below record the earlier XPC design. The current guest API exposes the status query as VPhoneGuestControl.isDeveloperModeEnabled(); there is no host-side enable action in the current menu.

let status = try await control.sendDevModeStatus()    // -> DevModeStatus (enabled: Bool)
let result = try await control.sendDevModeEnable()     // -> DevModeEnableResult (alreadyEnabled: Bool, message: String)

Both methods use sendRequest() with pending request tracking — the response is returned via async throws, not logged to console. Callers (e.g. VPhoneMenuConnect) display results as NSAlert sheets on the VM window.