How Device Pairing Information Is Generated in iLoader: A Deep Dive into Lockdown and RPPairing

iLoader generates device pairing information by constructing a combined plist containing traditional lockdown session data for Wi-Fi debugging and, on iOS 17.4+, ephemeral RPPairing data obtained through CoreDeviceProxy tunnels.

iLoader is an open-source iOS sideloading tool written in Rust that establishes cryptographic trust between a host machine and an iOS device. Understanding how device pairing information is generated in iloader requires examining the orchestration between legacy lockdown services and modern Remote Pairing (RPPairing) frameworks as implemented in src-tauri/src/pairing.rs.

The Two-Component Pairing Architecture

iLoader produces a single XML-encoded plist payload that combines two distinct trust mechanisms:

  • Lockdown pairing – Classic lockdown session data enabling Wi-Fi debugging, generated from the device’s existing pairing record.
  • RPPairing data – A modern Remote Pairing payload introduced in iOS 17.4, created dynamically by communicating with the device’s CoreDeviceProxy over an untrusted tunnel.

The pairing_file function in src-tauri/src/pairing.rs orchestrates the generation pipeline, returning a Vec<u8> containing the final XML bytes ready for transmission to the device.

Step-by-Step Generation Process

Establishing the USBMuxd Connection

The process begins by acquiring a mutable UsbmuxdConnection and resolving the target device’s provider. The pairing_file function receives the application state, DeviceInfo, and a cancellation token, then calls get_provider to obtain an IdeviceProvider for the specific UDID.

// src-tauri/src/pairing.rs (lines 6-13)
pub async fn pairing_file(
    app: &AppHandle,
    device_info: &DeviceInfo,
    usbmuxd: &mut UsbmuxdConnection,
    token: CancellationToken,
) -> Result<Vec<u8>, IdeviceError> {
    let provider = device_info.get_provider(usbmuxd).await?;
    // ...
}

Generating Lockdown Pairing Data

The generate_lockdown_plist function (lines 65-100) retrieves the existing pair record via usbmuxd.get_pair_record, injects the device UDID, initiates a lockdown session, and explicitly enables Wi-Fi debugging. It returns the plist representation as a Value type.

This step is universal—it executes for all iOS versions regardless of whether RPPairing is required.

Conditional RPPairing Generation

For devices running iOS 17.4 or later, iLoader must supplement lockdown data with RPPairing information. The system first checks for cached data in InMemoryStorage or the system key-ring under the key rppairing_file_<udid> (lines 34-40).

If no valid cache exists, generate_rppairing_plist invokes generate_rppairing (lines 21-65), which:

  1. Connects to CoreDeviceProxy on the device
  2. Opens a software tunnel to the Remote Service Daemon (RSD)
  3. Performs an RSD handshake to locate the untrusted tunnel service
  4. Establishes an XPC channel via RemoteXpcClient
  5. Creates an RpPairingFile and executes the pairing protocol twice—once to push the credential into the device keychain, once to confirm
// Simplified RPPairing generation flow
let (plist_value, raw_bytes) = generate_rppairing(&provider, token).await?;

Caching and Storage Strategy

Freshly generated RPPairing data is immediately persisted to avoid expensive regeneration. The raw bytes are stored via the SideloadingStorage abstraction (implemented in src-tauri/src/secure_storage.rs) using a key derived from the device UDID (lines 81-86).

Subsequent pairing attempts for the same device will retrieve this cached blob, skipping the network-intensive CoreDeviceProxy negotiation.

Merging and Serialization

The final phase combines both data sources. The pairing_file function merges the lockdown plist and RPPairing plist into a single dictionary using the plist! macro, then serializes the result to XML bytes (lines 94-99).

// Combining both pairing components
let combined = plist!({
    "Lockdown": lockdown_plist,
    "RPPairing": rppairing_plist,
});
let xml_bytes = plist::to_xml_bytes(&combined)?;

Cancellation and Async Handling

All long-running operations—including generate_lockdown_plist and generate_rppairing_plist—are wrapped in tokio::select! blocks (lines 14-19 and 49-54). This allows immediate abortion if the user triggers the cancellation token, preventing hanging connections during the RSD handshake or XPC negotiation.

Code Examples

Frontend Invocation (TypeScript/React)

Frontend components trigger pairing generation through Tauri’s command bridge:

// src/Device.tsx
await invoke('set_selected_device', { device: selectedDevice });
// Backend executes pairing_file(), storing result in DeviceInfoWithPairing.pairing

Direct Rust Usage

For custom integrations, invoke pairing_file directly with the required dependencies:

use tauri::State;
use iloader::pairing::pairing_file;

let pairing_blob = pairing_file(
    &app_handle,
    &device_info,
    &mut usbmuxd_connection,
    cancellation_token
).await?;

// pairing_blob contains XML plist bytes ready for device transmission

Cache Invalidation

To force regeneration of RPPairing data (for example, after device resets):

invoke('delete_stored_rppairing', { deviceState, app });

Summary

  • iLoader generates device pairing information in src-tauri/src/pairing.rs by combining legacy lockdown data with modern RPPairing payloads.
  • Lockdown pairing enables Wi-Fi debugging through modified lockdown session records.
  • RPPairing requires iOS 17.4+ and involves CoreDeviceProxy tunnels, RSD handshakes, and XPC channels via RemoteXpcClient.
  • Caching occurs in SideloadingStorage under keys formatted as rppairing_file_<udid> to avoid redundant network operations.
  • Cancellation is supported throughout via tokio::select! and CancellationToken.

Frequently Asked Questions

What is the difference between Lockdown pairing and RPPairing in iLoader?

Lockdown pairing is the traditional trust mechanism used for Wi-Fi debugging, generated from existing lockdown records and modified to enable network-based debugging. RPPairing is a newer protocol introduced in iOS 17.4 that uses CoreDeviceProxy and untrusted tunnels to establish trust without requiring a previous USB pairing, making it essential for modern wireless workflows.

Where does iLoader store generated pairing data?

iLoader stores RPPairing blobs in an abstraction called SideloadingStorage, implemented in src-tauri/src/secure_storage.rs. This storage uses either InMemoryStorage or the system key-ring, with keys formatted as rppairing_file_<udid>. Lockdown pairing data is never cached; it is generated fresh for each session from the device’s existing lockdown records.

Why does iLoader need to generate pairing information twice for iOS 17.4+ devices?

According to the source code in src-tauri/src/pairing.rs, the generate_rppairing function runs the pairing protocol twice: first to push the RpPairingFile into the device’s keychain, and second to confirm the credential persistence. This double-handshake ensures the device retains the trust relationship after the initial session closes.

How can I cancel an ongoing pairing operation in iLoader?

Pass a tokio_util::sync::CancellationToken to the pairing_file function. The implementation uses tokio::select! to monitor this token during generate_lockdown_plist and generate_rppairing_plist operations. Triggering the token aborts the connection to UsbmuxdConnection or CoreDeviceProxy immediately, preventing hang states during network timeouts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →