Skip to content

ADR-0004 — Stage 2 uses Option δ (CDS → local shim), not α/β/γ

Status

accepted

Context

Stage 2 of the project is "make the Pair button in Xcode actually work for a device surfaced by our CoreDeviceService inject". Four options were on the table at the start of Stage 2 planning:

  • Option α — let CoreDeviceService open its own nw_connection directly to the tunnel RSD endpoint that pymobiledevice3 tunneld advertises, and rely on the real pair flow completing naturally.
  • Option β — intercept at the Xcode UI level (_shadowUseAssertion / hasConnection) so the Pair button never asks for a pair to begin with.
  • Option γ — intercept at the AMDevice layer, translating individual AMDevice pair calls into pymobiledevice3 equivalents.
  • Option δ — intercept CDS's outgoing nw_connection to the tunnel RSD endpoint and route it through a local shim backend that speaks the protocol CDS expects, using pymobiledevice3 on the back side to do the real work.

Stage S2.A removed four DeviceInfo force-writes that had been bypassing the pair flow in Stage 1 ("lies about device state"). With the inject honest for the first time, Stage S2.B ran a Pair-button smoke test and produced a course-correcting finding:

  • CDS opens nw_connection directly to the tunnel RSD endpoint.
  • The connection receives an immediate TCP RST.
  • The tunnel itself is fully functional — pymobiledevice3 Python clients (remote rsd-info, mounter list, DVT filesystem listing) all succeed against the same tunnel in parallel.
  • The impedance mismatch is therefore inside the VM, between Apple's native CDS client and pymobiledevice3's tunneld transport.

This falsified Option α empirically: letting CDS speak to the tunnel naturally is not going to work because the two sides do not speak the same transport. Options β and γ were re-evaluated in light of S2.B and both turned out to be degenerate versions of the real problem (β hides the symptom without solving pair; γ duplicates logic that pymobiledevice3 already has). Option δ — stand up a local shim that CDS trusts and that pymobiledevice3 drives — became the only option that actually solves the root cause.

Decision

Stage 2 implements Option δ: CoreDeviceService's outgoing pair-flow connection is interposed, and redirected to a local backend that speaks the exact wire protocol CDS expects. The backend drives pymobiledevice3 internally to translate between CDS's RemoteXPC requests and the pymobiledevice3 tunnel transport.

Options α, β, and γ are explicitly rejected.

Consequences

Wanted:

  • We own the CDS-facing side of the protocol completely, so we can make the exact bytes CDS wants appear on the wire.
  • pymobiledevice3 stays in its working configuration and does not need to be forced to speak a different transport.
  • The same interpose pattern already used by our MDRemoteServiceSupport property-query hooks generalizes naturally to the pair flow.

Accepted as cost:

  • We have to characterize the full CDS ↔ iPhone RemoteXPC dialog empirically before the shim can be implemented. This is what Phase C of Stage 2 exists to do.
  • Every protocol change Apple ships in a future CDS update is potentially our problem. We accept this because the alternative (Option α) is already broken today.
  • The shim is a new production component. Its language and implementation constraints are set by ADR-0005.

Evidence

References

  • ADR-0005 — the shim's implementation language.
  • ADR-0006 — the discipline that makes Phase C's byte-level characterization trustworthy.