Skip to content

feat(image): mount personalized developer disk images#34

Merged
mozharovsky merged 10 commits into
mainfrom
codex/personalized-ddi-auto
Jun 29, 2026
Merged

feat(image): mount personalized developer disk images#34
mozharovsky merged 10 commits into
mainfrom
codex/personalized-ddi-auto

Conversation

@mozharovsky

@mozharovsky mozharovsky commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

  • implement the iOS 17+ personalized Developer Disk Image mount protocol, including device-side manifest reuse and Apple TSS fallback
  • add a provider-neutral HTTPS archive source and digest-keyed cache
  • use the pinned rorkai/swift-zip-archive fork for bounded streaming extraction across native and WASI builds
  • expose local and automatic mounting through DeviceSession and the rorkdevice image mount / image auto commands
  • report whether callers must recreate an existing CoreDevice tunnel after a new mount

Why

iOS 17+ exposes developer services such as CoreDevice AppService only after a personalized Developer Disk Image is mounted. InstallationProxy remains sufficient for basic installed-app queries, but richer CoreDevice operations need this mount flow.

The library intentionally does not hardcode or endorse a DDI archive provider. Callers supply an HTTPS URL and the exact expected SHA-256, allowing applications and distributions to make their own legal and trust decisions.

Flow

flowchart TD
    A["HTTPS archive + pinned SHA-256"] --> B["Download or reuse digest-keyed cache"]
    B --> C["Validate and extract Restore directory"]
    C --> D["Check iOS 17+, Developer Mode, and ECID"]
    D --> E["Look up mounted personalized image"]
    E -->|Already mounted| K["Return alreadyMounted"]
    E -->|Not mounted| F["Query hardware identifiers and select BuildIdentity"]
    F --> G["Query device-side personalization manifest"]
    G -->|Manifest available| I["Upload DMG and mount with trust cache"]
    G -->|Manifest unavailable| H["Reopen service, query nonce, request Apple TSS ticket"]
    H --> I
    I --> J["Return mounted and require a fresh CoreDevice tunnel"]
Loading

Security

  • retain platform-default TLS certificate validation for archive downloads and Apple TSS
  • authenticate archives before extraction with a caller-pinned SHA-256
  • bound archive download size, entry count, and total expanded size
  • reject symlinks, unsafe or conflicting paths, destination escapes, digest mismatches, and malformed manifests
  • validate extracted ZIP data and the selected DMG/trust cache against their BuildManifest digests
  • omit any built-in third-party DDI host

Compatibility

  • preserve the package's native and WASI target builds through platform-neutral HTTP boundaries
  • retain the existing pinned SwiftNIO, TLS, certificates, crypto, atomics, and system dependency revisions
  • introduce no public RorkDevice API break relative to main

Validation

  • swift test: 403 tests passed, 1 physical-device smoke test skipped, 0 failures
  • swift test --disable-automatic-resolution --filter DeveloperDiskImageSessionTests: 5 tests passed with the checked-in dependency pins
  • swift build -c release --product rorkdevice
  • swift build --swift-sdk swift-6.3.2-RELEASE_wasm --target RorkDeviceWeb
  • swiftly run swift build +6.0.3 --product rorkdevice with the matching macOS 15.4 SDK
  • swift package diagnose-api-breaking-changes origin/main --products RorkDevice: no breaking changes
  • verified image mount against a connected iPhone with an already-mounted personalized image
  • verified image auto downloads, authenticates, extracts, caches, and reaches mounted-image detection against the same iPhone

The connected phone already had a personalized image mounted, and unmounting was rejected while it was locked, so the live check did not repeat the destructive upload/mount branch.

Summary by CodeRabbit

  • New Features
    • Added iOS 17+ personalized Developer Disk Image mounting via new DeviceSession APIs and a CLI image mount/image auto workflow.
    • Added an archive download/verification pipeline with SHA-256 integrity checks, safe extraction, and digest-keyed caching.
    • Introduced DeveloperDiskImageMountResult plus machine-readable newline-delimited JSON output (--json) with ticket-source and tunnel-restart guidance.
  • Bug Fixes
    • Added stricter Apple TSS ticket request/response validation and improved error reporting.
    • Hardened restore handling: rejects unsafe ZIP contents (e.g., symlinks/traversal), enforces HTTPS-only redirects, and tightens restoration directory validation.
  • Chores
    • Pinned the Swift ZIP dependency and updated third-party notices.

@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f3a6c66-7002-4870-a609-bb7c4614f562

📥 Commits

Reviewing files that changed from the base of the PR and between 4957426 and 41e954e.

📒 Files selected for processing (4)
  • Package.resolved
  • Package.swift
  • Sources/RorkDevice/Lockdown/LockdownPairingMaterial.swift
  • Sources/RorkDevice/Public/RorkDevice.swift
✅ Files skipped from review due to trivial changes (1)
  • Package.resolved
🚧 Files skipped from review as they are similar to previous changes (1)
  • Package.swift

📝 Walkthrough

Walkthrough

This PR adds personalized Developer Disk Image mounting support, including ZIP archive preparation, manifest parsing, Apple TSS ticket requests, mobile image mounter orchestration, public session APIs, CLI commands, and tests.

Changes

Personalized DDI mount flow

Layer / File(s) Summary
Dependency wiring
Package.swift, Package.resolved, THIRD_PARTY_NOTICES.md
Pins swift-zip-archive, adds ZipArchive to RorkDevice and tests, makes Crypto unconditional, and updates the third-party notices list.
Archive source and restore cache
Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift, Sources/RorkDevice/DeveloperDiskImage/HTTPSOnlyURLSessionDelegate.swift, Tests/RorkDeviceTests/DeveloperDiskImageStoreTests.swift
Validates HTTPS archive sources, downloads and caches restore directories with byte, digest, and archive checks, filters redirects to HTTPS, and covers the rejection paths in tests.
Manifest payload parsing
Sources/RorkDevice/DeveloperDiskImage/PersonalizedDeveloperDiskImage.swift, Tests/RorkDeviceTests/PersonalizedDeveloperDiskImageTests.swift
Parses BuildManifest.plist, matches personalized build identities, resolves and validates image and trust-cache paths, computes SHA-384 digests, and tests matching and failure cases.
Apple TSS ticket flow
Sources/RorkDevice/DeveloperDiskImage/AppleTSSClient.swift, Tests/RorkDeviceTests/AppleTSSClientTests.swift
Builds Apple TSS XML requests from manifest data, parses the key-value response into ApImg4Ticket, applies restore-rule ticket shaping, and tests request generation and transport behavior.
Mobile image mounter orchestration
Sources/RorkDevice/DeveloperDiskImage/MobileImageMounterClient.swift, Tests/RorkDeviceTests/MobileImageMounterClientTests.swift, Tests/RorkDeviceTests/FakeConnection.swift
Implements image lookup, personalization, upload, mount, hangup, and Apple TSS fallback over a device connection, with tests for reuse, retry, and failure paths.
DeviceSession mount APIs
Sources/RorkDevice/Public/DeveloperDiskImageMountResult.swift, Sources/RorkDevice/Public/DeviceSession.swift, Tests/RorkDeviceTests/DeveloperDiskImageSessionTests.swift
Adds mount-result modeling plus public personalized-image mount overloads that validate device state and ECID before opening the mounter service.
Image CLI commands
Sources/RorkDeviceCLI/RorkDeviceCommand.swift, Tests/RorkDeviceCLITests/RorkDeviceCLITests.swift
Adds image mount and image auto commands, JSON output, and parse/output tests.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

  • rorkai/rork-device#13: This PR uses DeviceSession.isDeveloperModeEnabled() and device-state validation that connect directly to the developer-mode status flow added there.

Poem

I hopped through zips on moonlit toes,
with tickets tucked beneath my nose.
The image mounted, tunnel bright,
and caches sparkled in the night.
🐰 Hop, hop, hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding support for mounting personalized developer disk images.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/personalized-ddi-auto

Comment @coderabbitai help to get the list of available commands.

@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mozharovsky mozharovsky self-assigned this Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift`:
- Around line 454-459: Normalize ZIP entry paths before checking duplicates in
DeveloperDiskImageStore so aliases like repeated separators or "./" resolve to
the same key. Update the guard around entry.path to validate canonical path
components and use the normalized archive path when calling paths.insert,
ensuring extraction dedupes equivalent entries consistently.
- Around line 299-309: The downloader in DeveloperDiskImageStore uses a
URLSession without redirect handling, so non-HTTPS redirects are currently
allowed. Update the DeveloperDiskImageDownloadLimitDelegate used by URLSession
to implement
urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:) and
reject any redirect whose newRequest URL scheme is not https by cancelling it;
keep the allow path for valid HTTPS redirects.

In `@Sources/RorkDevice/DeveloperDiskImage/MobileImageMounterClient.swift`:
- Around line 82-98: The `personalizationManifest(for:)` recovery path is too
broad and converts transport failures, explicit device `Error`/`DetailedError`
replies, and malformed responses into `PersonalizationManifestUnavailable`.
Narrow the `do/catch` and response validation so only the specific “no cached
manifest / service closed” case maps to that sentinel, and rethrow or surface
the other failures instead. Keep the fallback in `mount(...)` limited to the
true reusable-manifest miss, and add a regression test covering an explicit
device error response to protect this boundary.

In `@Sources/RorkDeviceCLI/RorkDeviceCommand.swift`:
- Around line 803-817: Reject empty or all-whitespace cache directory overrides
in the image auto command by mirroring the existing --path validation before
`DeveloperDiskImageStore` is constructed. Update `validate()` or the
`cacheDirectory` handling in `RorkDeviceCommand` so `--cache-directory ""` and
whitespace-only values fail early instead of reaching `run()`, where the current
`URL(fileURLWithPath:)` fallback can create the store in an unintended location.
Use the same style of check already applied to `source()`/`--path`, and keep the
default `DeveloperDiskImageStore()` behavior when no valid override is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29df5b96-da6e-476e-9001-5e3c55f909bc

📥 Commits

Reviewing files that changed from the base of the PR and between c03c610 and 391b4ae.

📒 Files selected for processing (17)
  • Package.resolved
  • Package.swift
  • Sources/RorkDevice/DeveloperDiskImage/AppleTSSClient.swift
  • Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift
  • Sources/RorkDevice/DeveloperDiskImage/MobileImageMounterClient.swift
  • Sources/RorkDevice/DeveloperDiskImage/PersonalizedDeveloperDiskImage.swift
  • Sources/RorkDevice/Public/DeveloperDiskImageMountResult.swift
  • Sources/RorkDevice/Public/DeviceSession.swift
  • Sources/RorkDeviceCLI/RorkDeviceCommand.swift
  • THIRD_PARTY_NOTICES.md
  • Tests/RorkDeviceCLITests/RorkDeviceCLITests.swift
  • Tests/RorkDeviceTests/AppleTSSClientTests.swift
  • Tests/RorkDeviceTests/DeveloperDiskImageSessionTests.swift
  • Tests/RorkDeviceTests/DeveloperDiskImageStoreTests.swift
  • Tests/RorkDeviceTests/MobileImageMounterClientTests.swift
  • Tests/RorkDeviceTests/PersonalizedDeveloperDiskImageTests.swift
  • ThirdPartyLicenses/ZIPFoundation-LICENSE.txt

Comment thread Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift Outdated
Comment thread Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift Outdated
Comment thread Sources/RorkDeviceCLI/RorkDeviceCommand.swift Outdated
@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Package.swift`:
- Line 214: The RorkDeviceTests target is unconditionally depending on
ZipArchive, unlike RorkDevice which is gated by zipArchivePlatforms, so the test
target can fail on unsupported platforms. Update the test target in
Package.swift to apply the same platform gate for the ZipArchive product, and in
the ZipArchive-based test file guard the import ZipArchive plus any archive
fixture tests with `#if` canImport(ZipArchive) so the tests only build where the
library is available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8b61639-6d00-40f5-9fe7-9688cf928cb2

📥 Commits

Reviewing files that changed from the base of the PR and between 07709b4 and 63ce591.

📒 Files selected for processing (9)
  • Package.resolved
  • Package.swift
  • Sources/RorkDevice/DeveloperDiskImage/AppleTSSClient.swift
  • Sources/RorkDevice/DeveloperDiskImage/DeveloperDiskImageStore.swift
  • Sources/RorkDevice/DeveloperDiskImage/HTTPSOnlyURLSessionDelegate.swift
  • Sources/RorkDevice/Public/DeviceSession.swift
  • THIRD_PARTY_NOTICES.md
  • Tests/RorkDeviceTests/AppleTSSClientTests.swift
  • Tests/RorkDeviceTests/DeveloperDiskImageStoreTests.swift
✅ Files skipped from review due to trivial changes (2)
  • THIRD_PARTY_NOTICES.md
  • Package.resolved
🚧 Files skipped from review as they are similar to previous changes (3)
  • Sources/RorkDevice/DeveloperDiskImage/HTTPSOnlyURLSessionDelegate.swift
  • Tests/RorkDeviceTests/AppleTSSClientTests.swift
  • Sources/RorkDevice/DeveloperDiskImage/AppleTSSClient.swift

Comment thread Package.swift
@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mozharovsky

mozharovsky commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

Follow-up commit 421d3e4 removes the temporary zipArchivePlatforms gate now that rorkai/swift-zip-archive#3 adds OpenBSD support. ZipArchive is now a required dependency for production and tests, .driverKit is removed because this package does not provide a DriverKit target, and the resolved fork revision is pinned. Validation: 9 focused DDI archive tests, the full 403-test Swift suite, and the Swift.org 6.3.2 WASI RorkDeviceWeb build all passed.

@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mozharovsky

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mozharovsky mozharovsky merged commit 8ccb909 into main Jun 29, 2026
1 check passed
@mozharovsky mozharovsky deleted the codex/personalized-ddi-auto branch June 29, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant