### Worked example: List installed apps with InstallationProxyService Source: https://doronz88.github.io/pymobiledevice3/guides/python-api Lists installed apps and prints each bundle ID with its short version string. Requires an existing 'lockdown' service provider. ```python from pymobiledevice3.services.installation_proxy import InstallationProxyService apps = await InstallationProxyService(lockdown=lockdown).get_apps() for bundle_id, info in apps.items(): print(bundle_id, info.get("CFBundleShortVersionString")) ``` -------------------------------- ### Verify connectivity with example commands Source: https://doronz88.github.io/pymobiledevice3/installation Verify connectivity with a device by listing usbmux connections, streaming syslog, and listing installed apps. ```bash pymobiledevice3 usbmux list pymobiledevice3 syslog live pymobiledevice3 apps list ``` -------------------------------- ### Development Setup: Install editable package, pre-commit, and hooks Source: https://doronz88.github.io/pymobiledevice3/project/contributing Run these commands to install the package in editable mode with test dependencies, install pre-commit, and set up the git hooks. The editable install should run inside your .venv so the pyright hook can resolve imports from it. ```bash python3 -m pip install -U -e ".[test]" python3 -m pip install -U pre-commit pre-commit install ``` -------------------------------- ### install(package_path, developer, options, handler) Source: https://doronz88.github.io/pymobiledevice3/api/services Streams a local app to the device and installs it. Accepts an .ipa file or an unpackaged .app directory. Optionally installs as a developer package, provides extra installation options, and accepts a progress callback. ```APIDOC ## install(package_path: Path, developer: bool = False, options: Optional[dict[str, Any]] = None, handler: Optional[Callable[[int], Any]] = None) -> None ### Description Stream a local app to the device and install it. The method accepts an .ipa file or an unpackaged .app directory. It can install as a developer package, merge extra installation options, and invoke a progress callback with the completion percentage. ### Method async ### Signature install(package_path: Path, developer: bool = False, options: Optional[dict[str, Any]] = None, handler: Optional[Callable[[int], Any]] = None) -> None ### Parameters - **package_path** (Path) - Required - An .ipa file or an unpackaged .app directory. - **developer** (bool) - Optional - Install as a developer package (PackageType Developer) rather than a customer one. Defaults to False. - **options** (Optional[dict[str, Any]]) - Optional - Extra installation options, merged over the defaults. - **handler** (Optional[Callable[[int], Any]]) - Optional - Progress callback, invoked with the completion percentage. ### Returns None ### Raises - **AppInstallError** - If the device fails to install the app. - **StreamingZipConduitError** - If the device refuses or fails the transfer. ### Example ```python from pathlib import Path from pymobiledevice3.services.streaming_zip_conduit import StreamingZipConduitService service = StreamingZipConduitService(lockdown) await service.install(Path("/path/to/app.ipa"), developer=True) ``` ``` -------------------------------- ### install(package_path, developer=False, options=None, handler=None) Source: https://doronz88.github.io/pymobiledevice3/api/services Streams a local app to the device and installs it. Accepts an .ipa file or an unpackaged .app directory, with options for developer installation and a progress callback. ```APIDOC ## install(package_path, developer=False, options=None, handler=None) ### Description Stream a local app to the device and install it. ### Method async method ### Signature ```python async def install( self, package_path: Path, developer: bool = False, options: Optional[dict[str, Any]] = None, handler: Optional[Callable[[int], Any]] = None, ) -> None: ``` ### Parameters - **package_path** (Path) - Required - An `.ipa` file or an unpackaged `.app` directory. - **developer** (bool) - Optional - Install as a developer package (`PackageType` `Developer`) rather than a customer one. Default: `False`. - **options** (Optional[dict[str, Any]]) - Optional - Extra installation options, merged over the defaults. Default: `None`. - **handler** (Optional[Callable[[int], Any]]) - Optional - Progress callback, invoked with the completion percentage. Default: `None`. ### Raises - **AppInstallError** - If the device fails to install the app. - **StreamingZipConduitError** - If the device refuses or fails the transfer. ### Returns None ``` -------------------------------- ### pymobiledevice3 profile: list and install commands Source: https://doronz88.github.io/pymobiledevice3/cli/profile The profile subcommand manages installed profiles and installs SSL certificates. Listing profiles and installing profiles use separate command syntaxes: list takes no arguments, while install requires one or more profile files. A keybag may be supplied for silent installation. ```text pymobiledevice3 profile list [OPTIONS] ``` ```text pymobiledevice3 profile install [OPTIONS] {profiles}... ``` -------------------------------- ### install provisioning profile (async) Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Sends an Install request with the raw provisioning profile data. Raises PyMobileDevice3Exception if the device reports a non-zero status. ```python async def install(self, plist: IO[bytes]) -> dict[str, Any]: """ Install a provisioning profile on the device. :param plist: stream whose contents are the raw provisioning profile to install; read in full and sent to the device. :returns: the device's response plist. :raises PyMobileDevice3Exception: if the device reports a non-zero status. """ response = await self.service.send_recv_plist({ "MessageType": "Install", "Profile": plist.read(), "ProfileType": "Provisioning", }) if response["Status"]: raise PyMobileDevice3Exception(f"invalid status: {response}") return response ``` -------------------------------- ### install_http_proxy implementation Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Full implementation of the async install_http_proxy method. Installs a managed profile with a payload for a global manual HTTP proxy. When keybag_file is provided, installs silently using that supervision identity; otherwise installs with a user prompt. ```python async def install_http_proxy(self, server: str, server_port: int, keybag_file: Optional[Path] = None) -> None: """ Build and install a global manual HTTP-proxy profile. The profile uses the fixed `GLOBAL_HTTP_PROXY_UUID` so it can later be removed by `remove_http_proxy`. :param server: proxy server host. :param server_port: proxy server port. :param keybag_file: when provided, install silently using this supervision identity; otherwise install with a user prompt. """ payload_uuid = str(uuid4()) await self.install_managed_profile( f"HTTP Proxy for {server}:{server_port}", { "PayloadDescription": "Global HTTP Proxy", "PayloadDisplayName": "Global HTTP Proxy", "PayloadIdentifier": f"com.apple.proxy.http.global.{payload_uuid}", "PayloadType": "com.apple.proxy.http.global", "PayloadUUID": payload_uuid, "PayloadVersion": 1, "ProxyCaptiveLoginAllowed": False, "ProxyServer": server, "ProxyServerPort": server_port, "ProxyType": "Manual", }, payload_uuid=GLOBAL_HTTP_PROXY_UUID, keybag_file=keybag_file, ) ``` -------------------------------- ### query_nonce (async) Source: https://doronz88.github.io/pymobiledevice3/api/services Installs a cryptex by sending an install request with the given argv and file transfers to the cryptexd service, then unwraps the response. ```APIDOC ## query_nonce (async) ### Description Installs a cryptex by sending an install request with the given argv and file transfers to the cryptexd service, then unwraps the response. ### Method async ### Parameters - **argv** - The arguments to pass to the install routine. - **transfers** - Iterable of file transfers to send. ### Request Example ```python connection = self.rsd.start_remote_service(self.SERVICE_NAME) await connection.connect() await connection.send_request({ ``` ```APIDOC ## [ASYNC] query_nonce ### Description Queries a nonce from the cryptexd service by sending an install request with the provided argv and optional file transfers. ### Method async ### Parameters - **argv** - The arguments to pass to the install routine. - **transfers** - Iterable of file transfers to send before receiving the response. ### Request Example ```json { "routine": "install", "argv": "" } ``` ### Response #### Success Response - **response** - The response received from the cryptexd service. #### Error Handling - **ConnectionTerminatedError** - Raised if cryptexd closes the connection without replying, indicating the request was rejected or crashed. ### Notes - The connection is always closed in a ``finally`` block. - The response is unwrapped with ``self._unwrap( ``` -------------------------------- ### install_managed_profile method Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Wraps a single payload in a Configuration profile and installs it. If keybag_file is provided, installs silently using the supervision identity; otherwise installs with a user prompt. ```python async def install_managed_profile( self, display_name: str, payload_content: dict[str, Any], payload_uuid: str = str(uuid4()), keybag_file: Optional[Path] = None, ) -> None: """ Wrap a single payload in a ``Configuration`` profile and install it. :param display_name: profile display name. :param payload_content: the inner payload dictionary to wrap. :param payload_uuid: UUID used as both the profile identifier and UUID. :param keybag_file: when provided, install silently using this supervision identity; otherwise install with a user prompt. """ profile_data = plistlib.dumps({ "PayloadContent": [payload_content], "PayloadDisplayName": display_name, "PayloadIdentifier": payload_uuid, "PayloadRemovalDisallowed": False, "PayloadType": "Configuration", "PayloadUUID": payload_uuid, "PayloadVersion": 1, }) if keybag_file is not None: await self.install_profile_silent(keybag_file, profile_data) else: await self.install_profile(profile_data) ``` -------------------------------- ### profile install Source: https://doronz88.github.io/pymobiledevice3/cli/profile Installs the specified profiles, optionally using a keybag for silent installation. ```APIDOC ## `install` ### Description Installs the given profiles. If a keybag is provided, the profile is installed silently. ### Method CLI ### Command `pymobiledevice3 profile install [OPTIONS] {profiles}...` ### Arguments - `profiles...` (required) - The profiles to install. ### Options - `--native` - macOS only: reach the iOS 17+ tunnel by piggybacking Apple's own `remoted` tunnel. [env var: PYMOBILEDEVICE3_NATIVE] - `--keybag ` - Use the given keybag for silent installation. - Accepts the connection options: `--rsd`, `--tunnel`, `--userspace`, `--mobdev2`, `--usbmux`, `--udid`. ``` -------------------------------- ### install_from_bytes async method signature Source: https://doronz88.github.io/pymobiledevice3/api/services Installs an app from bytes. The signature shows parameters: package_bytes, cmd (default 'Install'), options, handler, and *args. ```python install_from_bytes(package_bytes: bytes, cmd: str = 'Install', options: Optional[dict[str, Any]] = None, handler: Optional[Callable[..., Any]] = None, *args: Any) -> None ``` -------------------------------- ### Install pymobiledevice3 from source Source: https://doronz88.github.io/pymobiledevice3/installation Clone the repository and install the package in editable mode for development or testing the latest source. Requires Python 3.9+ and Git. ```bash git clone git@github.com:doronz88/pymobiledevice3.git cd pymobiledevice3 python3 -m pip install -U -e . ``` -------------------------------- ### install Source: https://doronz88.github.io/pymobiledevice3/api/services Installs a cryptex by transferring five payloads (image, trustcache, im4m, info, volumehash) to the cryptexd daemon. The request shape mirrors what Xcode sends during a devicectl DDI install, and the daemon replies only after consuming all payloads. ```APIDOC ## install ### Description Installs a cryptex by transferring five payloads (image, trustcache, im4m, info, volumehash) to the daemon. The cryptex is identified solely by the `info` payload; there is no identifier argument. The `im4m` must be a Cryptex1 ticket bound to the current nonce of the identity's Cryptex1,NonceDomain. ### Method async ### Parameters #### Request Body - **image** (bytes) - Required - The cryptex disk image, i.e. the build manifest's ``Cryptex1,GenericDmg``. - **trustcache** (bytes) - Required - ``Cryptex1,GenericTrustCache``. - **im4m** (bytes) - Required - The Cryptex1 personalization ticket. - **info** (bytes) - Required - ``Cryptex1,CryptexInfoPlist``; names and versions the cryptex. - **volumehash** (bytes) - Required - ``Cryptex1,GenericVolume`` root hash; without it the daemon reports "AuthAPFS will not be supported". - **cryptex1_properties** (dict[str, Any]) - Required - The ``Cryptex1,*`` parameters from the build identity. - **image_type_index** (int) - Optional - The image's `CryptexAssetType`. Defaults to `CryptexAssetType.GDMG`. - **persistence** (int) - Optional - Cryptex persistence mode. Defaults to `DDI_PERSISTENCE`. - **nonce_persistence** (int) - Optional - Nonce persistence mode. Defaults to `DDI_NONCE_PERSISTENCE`. - **auth** (int) - Optional - Authentication mode. Defaults to 0. ### Request Example ```json { "image": "", "trustcache": "", "im4m": "", "info": "", "volumehash": "", "cryptex1_properties": {}, "image_type_index": 0, "persistence": 0, "nonce_persistence": 0, "auth": 0 } ``` -------------------------------- ### Install shell completions Source: https://doronz88.github.io/pymobiledevice3/installation Install shell completions for pymobiledevice3 commands in the current shell. ```bash pymobiledevice3 --install-completion ``` -------------------------------- ### install method signature Source: https://doronz88.github.io/pymobiledevice3/api/services Shows the async signature of the install method, which takes a package path, optional developer flag, options dictionary, and progress handler. ```python install(package_path: Path, developer: bool = False, options: Optional[dict[str, Any]] = None, handler: Optional[Callable[[int], Any]] = None) -> None ``` -------------------------------- ### List and Query Installed Apps Source: https://doronz88.github.io/pymobiledevice3/guides/cli-recipes List installed apps and query specific bundle IDs. ```shell pymobiledevice3 apps list ``` ```shell pymobiledevice3 apps query BUNDLE_ID1 BUNDLE_ID2 ``` -------------------------------- ### install_http_proxy Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Installs a global HTTP proxy configuration profile on the device. Requires a proxy server host and port, and optionally a keybag file for silent installation. ```APIDOC ## install_http_proxy ### Description Installs a global HTTP proxy configuration profile on the device. Requires a proxy server host and port, and optionally a keybag file for silent installation. ### Method install_http_proxy ### Parameters - **server** (string) - Required - Proxy server host. - **server_port** (integer) - Required - Proxy server port. - **keybag_file** (Path) - Optional - When provided, install silently using this supervision identity; otherwise install with a user prompt. ### Request Example ```python await service.install_http_proxy(server="proxy.example.com", server_port=8080, keybag_file=None) ``` ### Response No response body is returned. ``` -------------------------------- ### Pair and start tunneld Source: https://doronz88.github.io/pymobiledevice3/guides/ios17-tunnels Pair the device if required, then start the tunneld service on the local machine. Run from a privileged shell on Windows. ```bash # If the device supports remote pairing (for example, Corellium/Apple TV/Apple Vision Pro), pair first. # Apple TV and Apple Vision Pro display a PIN to type in. Standard iOS devices usually do not need this step. python3 -m pymobiledevice3 remote pair # On Windows, run from a privileged shell. sudo python3 -m pymobiledevice3 remote tunneld ``` -------------------------------- ### async start_lockdown_service: Start lockdownd service and connect Source: https://doronz88.github.io/pymobiledevice3/api/connection Starts a lockdownd service and opens a connection, upgrading to SSL if required. Raises NotPairedError, PasswordRequiredError, or StartServiceError as appropriate. ```python start_lockdown_service(name: str, include_escrow_bag: bool = False) -> ServiceConnection ``` ```python async def start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection: """Start a named lockdownd service and open a connection to it. Asks lockdownd to start the service (see `get_service_connection_attributes`), opens a new connection to the reported port, and upgrades it to SSL when the service requires it. :param name: The lockdownd service name to start (e.g. ``"com.apple.afc"``). :param include_escrow_bag: When True, include the pair record's escrow bag in the start request (required by some services to operate while the device is locked). :returns: A connected `ServiceConnection` for the started service. :raises NotPairedError: The client is not paired with the device. :raises PasswordRequiredError: The device is passcode-protected and must be unlocked first. :raises StartServiceError: lockdownd refused to start the service. """ attr = await self.get_service_connection_attributes(name, include_escrow_bag=include_escrow_bag) service_connection = await self.create_service_connection(attr["Port"]) if attr.get("EnableServiceSSL", False): with self.ssl_file() as f: await service_connection.ssl_start(f) return service_connection ``` -------------------------------- ### Start a lockdown service from Python Source: https://doronz88.github.io/pymobiledevice3/internals/idevice-protocol-layers Creates a LockdownClient, starts a service by name, and sends/receives raw data over the service connection. Replace SERVICE_NAME with the desired service identifier. ```python from pymobiledevice3.lockdown import create_using_usbmux async def main() -> None: # Create the LockdownClient instance lockdown = await create_using_usbmux() # Get a handle to the service service = await lockdown.start_lockdown_service(SERVICE_NAME) # Attempt to send and receive messages from it await service.sendall(b'hello') response = await service.recvall(20) ``` -------------------------------- ### Install restrictions profile payload example Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Shows a partial example of the payload dictionary passed to install_restrictions_profile, with many boolean restriction keys and rating settings. The snippet is truncated at the start and end, showing only the dictionary contents and the closing of the function call. ```python "allowAssistantWhileLocked": True, "allowAutoCorrection": True, "allowAutoUnlock": True, "allowAutomaticAppDownloads": True, "allowBluetoothModification": True, "allowBookstore": True, "allowBookstoreErotica": True, "allowCamera": True, "allowCellularPlanModification": True, "allowChat": True, "allowCloudBackup": True, "allowCloudDocumentSync": True, "allowCloudPhotoLibrary": True, "allowContinuousPathKeyboard": True, "allowDefinitionLookup": True, "allowDeviceNameModification": True, "allowDeviceSleep": True, "allowDictation": True, "allowESIMModification": True, "allowEnablingRestrictions": True, "allowEnterpriseAppTrust": True, "allowEnterpriseBookBackup": True, "allowEnterpriseBookMetadataSync": True, "allowEraseContentAndSettings": True, "allowExplicitContent": True, "allowFilesNetworkDriveAccess": True, "allowFilesUSBDriveAccess": True, "allowFindMyDevice": True, "allowFindMyFriends": True, "allowFingerprintForUnlock": True, "allowFingerprintModification": True, "allowGameCenter": True, "allowGlobalBackgroundFetchWhenRoaming": True, "allowInAppPurchases": True, "allowKeyboardShortcuts": True, "allowManagedAppsCloudSync": True, "allowMultiplayerGaming": True, "allowMusicService": True, "allowNews": True, "allowNotificationsModification": True, "allowOpenFromManagedToUnmanaged": True, "allowOpenFromUnmanagedToManaged": True, "allowPairedWatch": True, "allowPassbookWhileLocked": True, "allowPasscodeModification": True, "allowPasswordAutoFill": True, "allowPasswordProximityRequests": True, "allowPasswordSharing": True, "allowPersonalHotspotModification": True, "allowPhotoStream": True, "allowPredictiveKeyboard": True, "allowProximitySetupToNewDevice": True, "allowRadioService": True, "allowRemoteAppPairing": True, "allowRemoteScreenObservation": True, "allowSafari": True, "allowScreenShot": True, "allowSharedStream": True, "allowSpellCheck": True, "allowSpotlightInternetResults": True, "allowSystemAppRemoval": True, "allowUIAppInstallation": True, "allowUIConfigurationProfileInstallation": True, "allowUSBRestrictedMode": True, "allowUnpairedExternalBootToRecovery": False, "allowUntrustedTLSPrompt": True, "allowVPNCreation": True, "allowVideoConferencing": True, "allowVoiceDialing": True, "allowWallpaperModification": True, "allowiTunes": True, "enforcedSoftwareUpdateDelay": enforced_software_update_delay, "forceAirDropUnmanaged": False, "forceAirPrintTrustedTLSRequirement": False, "forceAssistantProfanityFilter": False, "forceAuthenticationBeforeAutoFill": False, "forceAutomaticDateAndTime": False, "forceClassroomAutomaticallyJoinClasses": False, "forceClassroomRequestPermissionToLeaveClasses": False, "forceClassroomUnpromptedAppAndDeviceLock": False, "forceClassroomUnpromptedScreenObservation": False, "forceDelayedSoftwareUpdates": True, "forceEncryptedBackup": False, "forceITunesStorePasswordEntry": False, "forceLimitAdTracking": False, "forceWatchWristDetection": False, "forceWiFiPowerOn": False, "forceWiFiWhitelisting": False, "ratingApps": 1000, "ratingMovies": 1000, "ratingRegion": "us", "ratingTVShows": 1000, "safariAcceptCookies": 2.0, "safariAllowAutoFill": True, "safariAllowJavaScript": True, "safariAllowPopups": True, "safariForceFraudWarning": False, }, payload_uuid=payload_uuid, keybag_file=keybag_file, ) ``` -------------------------------- ### install(plist: IO[bytes]) -> dict[str, Any] Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Install a provisioning profile on the device. Takes a stream containing the raw provisioning profile and sends it to the device, returning the device's response plist. ```APIDOC ## install(plist: IO[bytes]) -> dict[str, Any] ### Description Install a provisioning profile on the device. ### Method async method call on the service object ### Endpoint Not applicable (Python SDK method) ### Parameters - **plist** (IO[bytes]) - Required - stream whose contents are the raw provisioning profile to install; read in full and sent to the device. ### Returns - **dict[str, Any]** - the device's response plist. ### Raises - **PyMobileDevice3Exception** - if the device reports a non-zero status. ### Source Code Reference Located in `pymobiledevice3/services/misagent.py` (lines 45-62), part of the MisagentService. ``` -------------------------------- ### Get app install record with pymobiledevice3 apps install-record Source: https://doronz88.github.io/pymobiledevice3/guides/cli-recipes Retrieves the LaunchServices install record for a given bundle ID, showing the database UUID/sequence, install path, and persistent identifier. The --userspace flag targets the user-space install record. ```bash # LaunchServices install record (DB UUID/sequence, install path, persistent identifier) pymobiledevice3 apps install-record com.apple.Preferences --userspace ``` -------------------------------- ### CLI examples for implemented services Source: https://doronz88.github.io/pymobiledevice3/internals/idevice-protocol-layers Shows how to use pymobiledevice3 to access already-implemented services: view device syslog and reboot the device. ```bash # View device syslog pymobiledevice3 syslog live # Reboot the device pymobiledevice3 diagnostics restart # And the list goes on and on... ``` -------------------------------- ### Start pymobiledevice3 tunnel (native, remote, lockdown) with example output Source: https://doronz88.github.io/pymobiledevice3/guides/ios17-tunnels Starts a tunnel manually, covering native, remote, and lockdown variants. The classic tunnel requires sudo to create a TUN/TAP interface; the native path on macOS needs no privileges. The second block shows expected output with the RSD address and port. ```shell # macOS, no sudo: publishes Apple's own remoted tunnel (kernel-routable, so # other tools can use the printed --rsd too). It competes with remoted for the # device; see "Native remoted tunnel" in the network stacks guide. python3 -m pymobiledevice3 remote start-tunnel --native # Optional for remote-pairing devices. python3 -m pymobiledevice3 remote pair # iOS 17.4+ (faster lockdown tunnel) sudo python3 -m pymobiledevice3 lockdown start-tunnel # Optional: allow Wi-Fi connections over lockdown python3 -m pymobiledevice3 lockdown wifi-connections on # iOS 17.0-17.3.1 fallback, and the default without --native. # Add `-t wifi` to force Wi-Fi transport. sudo python3 -m pymobiledevice3 remote start-tunnel ``` ```text Interface: utun6 RSD Address: fd7b:e5b:6f53::1 RSD Port: 64337 Use the following connection option: --rsd fd7b:e5b:6f53::1 64337 ``` -------------------------------- ### install_from_bytes(package_bytes, cmd='Install', options=None, handler=None, *args) -> None Source: https://doronz88.github.io/pymobiledevice3/api/services Installs an app from raw bytes. The method signature is provided, but no detailed description or parameters are given in the source. ```APIDOC ## install_from_bytes(package_bytes, cmd='Install', options=None, handler=None, *args) -> None ### Description Installs an app from raw bytes. (No detailed description provided in source.) ### Method Async method ### Signature `install_from_bytes(package_bytes: bytes, cmd: str = 'Install', options: Optional[dict[str, Any]] = None, handler: Optional[Callable[..., Any]] = None, *args: Any) -> None` ### Parameters - **package_bytes** (bytes) - Required - The app package as bytes. - **cmd** (str) - Optional - Command to send, defaults to `'Install'`. - **options** (Optional[dict[str, Any]]) - Optional - Command options. - **handler** (Optional[Callable[..., Any]]) - Optional - Progress callback. - **args** (Any) - Optional - Extra arguments for handler. ### Returns - **None** - None. ### Source Code ```python install_from_bytes(package_bytes: bytes, cmd: str = 'Install', options: Optional[dict[str, Any]] = None, handler: Optional[Callable[..., Any]] = None, *args: Any) -> None ``` ``` -------------------------------- ### Get tunneld devices and stream syslog in Python Source: https://doronz88.github.io/pymobiledevice3/internals/idevice-protocol-layers Python async example that retrieves a list of RemoteServiceDiscoveryService objects from tunneld and uses the first one to stream syslog entries. The returned RSDs can be used like any LockdownClient. ```python from pymobiledevice3.tunneld.api import get_tunneld_devices from pymobiledevice3.services.os_trace import OsTraceService async def main() -> None: rsds = await get_tunneld_devices() # We can now simply use the returned list of RSDs as any other LockdownClients async for entry in OsTraceService(rsds[0]).syslog(): print(entry) ``` -------------------------------- ### pymobiledevice3.services.debugserver_applist.DebugServerAppList service class Source: https://doronz88.github.io/pymobiledevice3/api/capture Defines the DebugServerAppList class, a LockdownService that retrieves installed applications via the com.apple.debugserver.DVTSecureSocketProxy.applist service. The class sets SERVICE_NAME, initializes with a LockdownServiceProvider, and its get method reads a plist response until the closing tag then parses it with plistlib.loads. ```python class DebugServerAppList(LockdownService): """ Retrieve the list of installed applications as seen by debugserver, via the ``com.apple.debugserver.DVTSecureSocketProxy.applist`` service. """ SERVICE_NAME = "com.apple.debugserver.DVTSecureSocketProxy.applist" def __init__(self, lockdown: LockdownServiceProvider): super().__init__(lockdown, self.SERVICE_NAME) async def get(self) -> dict[str, Any]: """ Fetch the application list. Reads the full plist response from the service and parses it. :returns: The parsed application list, keyed by bundle identifier. """ buf = b"" while b"" not in buf: buf += await self.service.recv_any(CHUNK_SIZE) return plistlib.loads(buf) ``` -------------------------------- ### get_service_connection_attributes: Start service and get connection attributes Source: https://doronz88.github.io/pymobiledevice3/api/connection Asynchronously asks lockdownd to start a named service via a StartService request serious note that this snippet is incomplete in the source. A StartService request is sent, and the returned dict includes the Port to connect on and whether SSL must be enabled (EnableServiceSSL). The parameter include_escrow_bag is a note that this snippet appears incomplete: the source text is cut off after the docstring, so the function body is not present on the page. The description only covers the visible portion. This snippet should be flagged as incomplete. ```python async def get_service_connection_attributes(self, name: str, include_escrow_bag: bool = False) -> dict[str, Any]: """Ask lockdownd to start a named service and return its connection attributes. Sends a ``StartService`` request for the given service. The returned dict includes the ``Port`` to connect on and whether SSL must be enabled (``EnableServiceSSL``). Use `start_lockdown_service` to also open the connection. ``` -------------------------------- ### invoke (async) Source: https://doronz88.github.io/pymobiledevice3/api/services Installs a cryptex by providing the disk image, trust cache, personalization ticket, info plist, volume hash, and properties. The method raises errors if the daemon rejects the request or the device lacks the CryptexInstall capability. ```APIDOC ## invoke (async) ### Description Installs a cryptex. The five payloads are announced in the request as XPC file transfers and then pushed on their own streams; the daemon only replies once it has consumed all of them. ### Method async ### Parameters #### Required Parameters - **image** (`bytes`) - Required - The cryptex disk image, i.e. the build manifest's `Cryptex1,GenericDmg`. - **trustcache** (`bytes`) - Required - `Cryptex1,GenericTrustCache`. - **im4m** (`bytes`) - Required - The Cryptex1 personalization ticket. - **info** (`bytes`) - Required - `Cryptex1,CryptexInfoPlist`; names and versions the cryptex. - **volumehash** (`bytes`) - Required - `Cryptex1,GenericVolume` root hash; without it the daemon reports "AuthAPFS will not be supported". - **cryptex1_properties** (`dict[str, Any]`) - Required - The `Cryptex1,*` parameters from the build identity. #### Optional Parameters - **image_type_index** (`int`) - Default: `GDMG` - The image's `CryptexAssetType`. - **persistence** (`int`) - Default: `DDI_PERSISTENCE` - Cryptex persistence mode. - **nonce_persistence** (`int`) - Default: `DDI_NONCE_PERSISTENCE` - Nonce persistence mode. - **auth** (`int`) - Default: `0` - Authentication mode. ### Parameters #### Required Parameters - **image** (`bytes`) - The cryptex disk image, i.e. the build manifest's `Cryptex1,GenericDmg`. - **trustcache** (`bytes`) - `Cryptex1,GenericTrustCache`. - **im4m** (`bytes`) - The Cryptex1 personalization ticket. - **info** (`bytes`) - `Cryptex1,CryptexInfoPlist`; names and versions the cryptex. - **volumehash** (`bytes`) - `Cryptex1,GenericVolume` root hash; without it the daemon reports "AuthAPFS will not be supported". - **cryptex1_properties** (`dict[str, Any]`) - The `Cryptex1,*` parameters from the build identity. #### Optional Parameters - **image_type_index** (`int`) - Default: `GDMG` - The image's `CryptexAssetType`. - **persistence** (`int`) - Default: `DDI_PERSISTENCE` - Cryptex persistence mode. - **nonce_persistence** (`int`) - Default: `DDI_NONCE_PERSISTENCE` - Nonce persistence mode. - **auth** (`int`) - Default: `0` - Authentication mode. ### Errors - **CryptexdError** - if the daemon rejected the request, the install failed, or the daemon closed the connection without replying. - **DeviceFeatureNotSupportedError** - if the device does not advertise the `CryptexInstall` capability. ### Source Source code in `pymobiledevice3/services/cryptexd.py` ``` -------------------------------- ### StreamingZipConduitService class implementation Source: https://doronz88.github.io/pymobiledevice3/api/services Defines the StreamingZipConduitService class, which extends LockdownService. It selects the appropriate service name based on the provider type (RSD vs lockdown) and provides an async install method that streams an .ipa or .app directory to the device. The _transfer method sends the setup plist, streams file entries, and handles progress and error responses. ```python class StreamingZipConduitService(LockdownService): """ Stream an app to the device and install it, the way Xcode does. The device extracts the archive while it arrives, instead of receiving an ``.ipa`` over AFC and unpacking it afterwards as `InstallationProxyService` does, so nothing is written twice. The stream is a zip without compression and without a central directory, opened by a metadata record. Each instance performs a single transfer. This is a lockdown service; the RSD/tunnel variant is chosen automatically for `RemoteServiceDiscoveryService` providers. """ SERVICE_NAME = "com.apple.streaming_zip_conduit" RSD_SERVICE_NAME = "com.apple.streaming_zip_conduit.shim.remote" def __init__(self, lockdown: LockdownServiceProvider) -> None: """ :param lockdown: service provider used to start the service and reach the device. """ if isinstance(lockdown, RemoteServiceDiscoveryService): super().__init__(lockdown, self.RSD_SERVICE_NAME) else: super().__init__(lockdown, self.SERVICE_NAME) async def install( self, package_path: Path, developer: bool = False, options: Optional[dict[str, Any]] = None, handler: Optional[Callable[[int], Any]] = None, ) -> None: """ Stream a local app to the device and install it. :param package_path: an ``.ipa`` file or an unpackaged ``.app`` directory. :param developer: install as a developer package (``PackageType`` ``Developer``) rather than a customer one. :param options: extra installation options, merged over the defaults. :param handler: progress callback, invoked with the completion percentage. :raises AppInstallError: if the device fails to install the app. :raises StreamingZipConduitError: if the device refuses or fails the transfer. """ install_options: dict[str, Any] = {"PackageType": "Developer" if developer else "Customer"} install_options.update(options or {}) setup: dict[str, Any] = { "InstallOptionsDictionary": install_options, "InstallTransferredDirectory": True, "MediaSubdir": f"{STAGING_DIRECTORY}/pymobiledevice3-{uuid.uuid4()}.ipa", "UserInitiatedTransfer": False, } if package_path.is_dir(): await self._transfer(setup, _app_directory_entries(package_path), handler) else: with zipfile.ZipFile(package_path) as archive: await self._transfer(setup, _ipa_entries(archive), handler) async def _transfer( self, setup: dict[str, Any], entries: list[_Entry], handler: Optional[Callable[[int], Any]] ) -> None: await self.service.send_plist(setup) for entry in _with_metadata(entries): await self.service.sendall(entry.header()) for chunk in entry.chunks(): await self.service.sendall(chunk) await self.service.sendall(_CENTRAL_DIRECTORY_SIGNATURE) while True: response = await self.service.recv_plist() if "Error" in response: raise StreamingZipConduitError(str(response["Error"])) if response.get("Status") == "DataComplete": return progress = cast(dict[str, Any], response.get("InstallProgressDict") or {}) if "Error" in progress: raise AppInstallError(f"{progress['Error']}: {progress.get('ErrorDescription')}") if handler is not None and "PercentComplete" in progress: handler(progress["PercentComplete"]) ``` -------------------------------- ### lookup async method in installation_proxy.py Source: https://doronz88.github.io/pymobiledevice3/api/services Sends the "Lookup" command to the installation proxy service and returns the LookupResult value from the response. The options dict (e.g. BundleIDs, ApplicationType, ReturnAttributes) determines which apps and attributes are returned. If options is None, it is sent as an empty dict. Returns None if LookupResult is absent. ```python async def lookup(self, options: Optional[dict[str, Any]] = None) -> Optional[dict[str, Any]]: """ Look up installed apps via the ``"Lookup"`` command. Sends the command and returns the single ``LookupResult`` value from the response. The options dict (e.g. ``BundleIDs``, ``ApplicationType``, ``ReturnAttributes``) determines which apps and attributes are returned. :param options: Command options, sent as ``ClientOptions``. ``None`` is sent as an empty dict. :returns: The ``LookupResult`` mapping bundle identifiers to per-app info dictionaries, or ``None`` if absent. """ if options is None: options = {} cmd: dict[str, Any] = {"Command": "Lookup", "ClientOptions": options} await self.service.send_plist(cmd) return cast(Optional[dict[str, Any]], (await self.service.recv_plist()).get("LookupResult")) ``` -------------------------------- ### install_http_proxy(server, server_port, keybag_file) Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Builds and installs a global manual HTTP-proxy profile. The profile uses a fixed UUID so it can later be removed by remove_http_proxy. ```APIDOC ## install_http_proxy(server, server_port, keybag_file) ### Description Build and install a global manual HTTP-proxy profile. The profile uses the fixed `GLOBAL_HTTP_PROXY_UUID` so it can later be removed by `remove_http_proxy`. ### Method Async method ### Signature `async def install_http_proxy(self, server: str, server_port: int, keybag_file: Optional[Path] = None) -> None` ### Parameters - **server** (str) - Required - The proxy server address. - **server_port** (int) - Required - The proxy server port. - **keybag_file** (Optional[Path]) - Optional - When provided, install silently using this supervision identity; otherwise install with a user prompt. ### Returns None ``` -------------------------------- ### MisagentService async context manager example Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Shows how to use MisagentService as an async context manager to copy all provisioning profiles from the device. ```python async with MisagentService(lockdown) as misagent: profiles = await misagent.copy_all() ``` -------------------------------- ### remove_profile async method source code Source: https://doronz88.github.io/pymobiledevice3/api/apps-files Removes an installed configuration profile by its identifier. Looks up the profile's metadata in the installed profile list and, if present, sends a signed-free removal request. Does nothing if no profiles are installed or the identifier is not currently installed. The identifier parameter is the payload identifier of the profile to remove. ```python async def remove_profile(self, identifier: str) -> None: """ Remove an installed configuration profile by its identifier. Looks up the profile's metadata in the installed profile list and, if present, sends a signed-free removal request. Does nothing if no profiles are installed or the identifier is not currently installed. :param identifier: payload identifier of the profile to remove. """ profiles = await self.get_profile_list() if not profiles: return if identifier not in profiles["ProfileMetadata"]: self.logger.info(f"Trying to remove not installed profile: {identifier}") return meta = profiles["ProfileMetadata"][identifier] data = plistlib.dumps({ "PayloadType": "Configuration", "PayloadIdentifier": identifier, "PayloadUUID": meta["PayloadUUID"], "PayloadVersion": meta["PayloadVersion"], }) await self._send_recv({"RequestType": "RemoveProfile", "ProfileIdentifier": data}) ``` -------------------------------- ### auto_install_ddi implementation Source: https://doronz88.github.io/pymobiledevice3/api/services Full implementation of auto_install_ddi. It checks for existing installations, loads cryptex assets, requests a TSS ticket, installs the cryptex, and returns the installed cryptex. Raises AlreadyMountedError, CryptexdError, TSSError, or DeviceFeatureNotSupportedError. ```python async def auto_install_ddi(self, restore_dir: Optional[Path] = None) -> InstalledCryptex: """ Personalize and install the DeveloperDiskImage cryptex entirely through ``cryptexd``. This is the cryptex equivalent of the image mounter's ``auto-mount``: it assembles the payloads, reads the device's personalization identifiers and cryptex nonce, has Apple sign a Cryptex1 ticket for them, and installs the result — all over ``cryptexd``, without the image mounter. :param restore_dir: an unpacked DDI ``Restore`` directory; defaults to the cached download from the DeveloperDiskImage repository, so no Xcode installation is required. :returns: the installed cryptex, as reported back by `copy_installed`. :raises AlreadyMountedError: if the DDI cryptex is already installed, or the image mounter already has a Personalized image mounted. :raises CryptexdError: if the daemon rejected the image. :raises TSSError: if Apple refused to sign the personalization request. :raises DeviceFeatureNotSupportedError: below `DDI_CRYPTEX_MIN_VERSION`. """ # Before asking Apple to sign anything the device cannot mount require_ddi_cryptex_support(self.rsd.product_version, self.rsd.udid) already_installed = await self._installed_ddi() if already_installed is not None: raise AlreadyMountedError( f"{already_installed.identifier} {already_installed.version} is already installed" ) if await self._ddi_mounted_by_image_mounter(): raise AlreadyMountedError("a Personalized image is already mounted by the image mounter") assets = load_cryptex1_assets(restore_dir) request = TSSRequest() request.add_cryptex1_tags( assets.build_identity, await self.read_personalization_identifiers(), await self.cryptex_nonce(assets.nonce_domain), ) response = await request.send_receive() im4m = response.get("Cryptex1,Ticket") or response["ApImg4Ticket"] await self.install( assets.image, assets.trustcache, im4m, assets.info, assets.volumehash, assets.cryptex1_properties, ) ddi = await self._installed_ddi() if ddi is None: raise CryptexdError(f"install reported success but {DDI_CRYPTEX_IDENTIFIER} is not installed") return ddi ```