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) |
PersonalizedImages/Xcode_iOS_DDI_Cryptex/ | cryptexd (cryptex auto-install) |
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.
fromdeveloper_disk_image.repoimportDeveloperDiskImageRepositoryrepo=DeveloperDiskImageRepository.create()
# will get both the APFS and the signature filedeveloper_disk_image=repo.get_developer_disk_image('16.4')
# will get all necessary files for mountpersonalized_disk_image=repo.get_personalized_disk_image()
# will get all necessary files for installing the DDI as a cryptexcryptex_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:
frompathlibimportPathfromdeveloper_disk_image.repoimportCRYPTEX_IMAGE_PAYLOADSrestore=Path('Xcode_iOS_DDI_Cryptex')
restore.mkdir(parents=True, exist_ok=True)
(restore/'BuildManifest.plist').write_bytes(cryptex_disk_image.build_manifest)
forfield, nameinCRYPTEX_IMAGE_PAYLOADS.items():
(restore/name).write_bytes(getattr(cryptex_disk_image, field))