Store both types of Apple's DeveloperDiskImage files:
DeveloperDiskImage.dmg&DeveloperDiskImage.dmg.signature- Used for each iOS version < 17.0
- The new Personalized images, but splitted to:
- APFS
BuildManifest.plist- Trustcache
- The Cryptex1 assets, for installing the same DDI through
cryptexdinstead of the image mounter:- APFS
BuildManifest.plist- Trustcache
- Cryptex info plist
- Volume root hash
The split of the new format is requires for OS* other than macOS that will have trouble extracting the original APFS
image inside: ~/Library/Developer/DeveloperDiskImages.
| Directory | Consumed by |
|---|---|
DeveloperDiskImages/<version>/ |
iOS < 17.0 |
PersonalizedImages/Xcode_iOS_DDI_Personalized/ |
the image mounter (mounter auto-mount on iOS 17.0 - 17.3.1 without an RSD tunnel, and released pymobiledevice3 versions) |
PersonalizedImages/Xcode_iOS_DDI_Cryptex/ |
cryptexd (mounter auto-mount from iOS 17.4 or over an RSD tunnel, and cryptex auto-install) |
Newer devices, such as the iPhone 18 series, can only use the Cryptex variant. Every
PersonalizedDMG build identity is tied to an ApChipID/ApBoardID pair, so it only covers the
devices listed in the DDI's build manifest -- even Xcode 27.1's DDI stops at iPhone18,5. A device
outside that list has no identity to request a ticket for (pymobiledevice3 reports
NoSuchBuildIdentityError). The Cryptex1 identity names no device at all
(Cryptex1,UseProductClass, no chip or board), so the manifest doesn't limit which devices it can
be personalized for; Apple's signing server decides, as it does for Xcode.
This is why pymobiledevice3's mounter auto-mount installs the Cryptex variant through cryptexd
from iOS 17.4, setting up the RSD tunnel it needs by itself. The Personalized variant is kept for
backward compatibility: released pymobiledevice3 versions fetch exactly its paths, and iOS 17.0 -
17.3.1 without a tunnel still mounts it.
Every payload is published under a fixed file name, so download URLs stay predictable across releases:
| File | Personalized | Cryptex |
|---|---|---|
BuildManifest.plist |
yes | yes |
Image.dmg |
PersonalizedDMG |
Cryptex1,GenericDmg |
Image.dmg.trustcache |
LoadableTrustCache |
Cryptex1,GenericTrustCache |
Image.dmg.cryptex_info |
-- | Cryptex1,CryptexInfoPlist |
Image.dmg.root_hash |
-- | Cryptex1,GenericVolume |
Apple's own paths embed a build number (022-22107-072.dmg), so the published
BuildManifest.plist is rewritten to declare the fixed names instead. Only Info.Path is
adjusted -- the digests personalization depends on are untouched -- which keeps a downloaded
cryptex directory usable as a drop-in Restore directory.
update_ddi.py refreshes every variant from a local Xcode installation. macOS only, no
dependencies, run it with uv:
uv run --script update_ddi.py # refresh every variant of the iOS DDI
uv run --script update_ddi.py --dry-run # report what would change, write nothing
uv run --script update_ddi.py --platform tvOS --variant cryptexPass --script so uv runs it as a standalone script rather than treating this repository as a uv
project (which would leave a stray uv.lock and .venv behind).
It reads Xcode's bundle from /Library/Developer (falling back to attaching
CoreDevice/CandidateDDIs/<platform>_DDI.dmg when no expanded copy exists), verifies every payload
against the SHA-384 digests in the build manifest before publishing it, and removes stale files
left over from a previous build. Remember to bump LATEST_DDI_BUILD_ID in pymobiledevice3 to match
whatever build it reports.
Additionally, this repo provides a python API for accessing this repository. You can install it as follows:
python3 -m pip install -U developer_disk_imagePayloads are fetched from raw.githubusercontent.com, which is not subject to the REST API's
60-requests-per-hour anonymous quota, so no GitHub token is needed. create() accepts a ref
(branch, tag or commit) to read a revision other than main.
from developer_disk_image.repo import DeveloperDiskImageRepository
repo = DeveloperDiskImageRepository.create()
# will get both the APFS and the signature file
developer_disk_image = repo.get_developer_disk_image('16.4')
# will get all necessary files for mount
personalized_disk_image = repo.get_personalized_disk_image()
# will get all necessary files for installing the DDI as a cryptex
cryptex_disk_image = repo.get_cryptex_disk_image()Writing the cryptex assets back out under their published names produces a directory that
cryptexd clients accept as-is:
from pathlib import Path
from developer_disk_image.repo import CRYPTEX_IMAGE_PAYLOADS
restore = Path('Xcode_iOS_DDI_Cryptex')
restore.mkdir(parents=True, exist_ok=True)
(restore / 'BuildManifest.plist').write_bytes(cryptex_disk_image.build_manifest)
for field, name in CRYPTEX_IMAGE_PAYLOADS.items():
(restore / name).write_bytes(getattr(cryptex_disk_image, field))This repository is not affiliated with or endorsed by Apple Inc. Apple, iOS and Xcode are trademarks of Apple Inc.
The disk images and their accompanying files are Apple's property, as shipped with Xcode. They are mirrored here solely for interoperability, mainly for Linux and Windows, where Xcode is not available: so developers there can use the developer services of their own devices.
- The files are unmodified, except for the
Info.Pathkey of the publishedBuildManifest.plist, rewritten to match this repository's fixed file names. - Nothing here bypasses Apple's protections: on iOS 17 and later, every image still requires a per-device signature issued by Apple's signing server before a device will mount it.
- No fee is charged and nothing here is sold.
Rights holders: to request removal, please open an issue or contact the maintainer. Valid requests will be honored promptly.