Skip to content

Phase D.6.1-B — hypothesis H1 confirmed: zero UUID was the session-progression gate

Status: verified — completed 2026-04-19

Breakthrough. With the D.6.1-A UUID patching enabled (IOSMUX_HANDSHAKE_UUID=random, commit ffe7c29), CDS accepts our handshake and holds the connection indefinitely — no more ~30 s timeout. Session held ESTABLISHED for 130 s (the orchestrator's kill window), only TCP keepalives every ~15 s as activity. Previous iter-6 "CDS disconnects at 23-36 s" behaviour was entirely caused by the 16-byte zero placeholder in the top-level UUID field of #8 big Handshake. H1 confirmed, H2/H3/H4 no longer need testing. Full writeup: findings.md.

Next step D.6.2: figure out what CDS expects from us now that the handshake is accepted (CDS is silent at the HTTP/2 layer post-handshake — not a timeout, actual idle).

What D.6.1-B proved

The 16 zero bytes at offset 0x371C in our iter-01 fixture — where pymobiledevice3's XpcWrapper.parse reads the top-level UUID field — were redacted at source in iphone_replay_bytes.py for privacy. Real iPhones emit session-bound random UUIDs there. CDS uses this field as a session-validity gate: zero → reject after ~30 s of silence, valid random → accept.

This is the most impactful single-line change in the Phase C/D arc: it unblocks the entire post-handshake layer with no wire format changes, no new frames, no new services. Just 16 bytes of entropy per session.

Files

  • findings.md — full per-session timeline, third-state analysis, three ranked candidate explanations for the new "accepted but silent" behaviour, D.6.2 plan.
  • verbose-session.log — 40 lines from the single session with the patched UUID line visible.
  • iosmux-d7.pcap — 20 KB loopback capture. TCP state transitions + keepalive ACKs. No iPhone UDID in cleartext.

Result vs iter-06

metric iter-06 (zero UUID) iter-07 (random v4)
Sessions in window 3 consecutive 1 long-lived
Handshake bytes emitted 14,313 (× 3) 14,313 (× 1)
CDS-initiated TCP close yes, at 23-36 s each no, within 130 s
GOAWAY none none
RST_STREAM none none
New post-handshake HTTP/2 frames none none
TCP state at orchestrator kill N/A (CDS already closed) ESTABLISHED

Open questions for D.6.2

With the handshake-gate passed:

  • Does CDS expect server-initiated traffic (heartbeats, service announcements) that would "invite" it to respond? (H1a)
  • Is CDS trying to connect back to us on one of the 62 services we advertise in Handshake, and failing silently? (H1b)
  • Does a different trigger (devicectl manage pair vs device info) produce actual RemoteXPC traffic? (H1c — cheapest)

D.6.2 will test H1c first (no code changes, just a different trigger command + same long-wait observation).

Correction notes in prior findings

D.6.0-B's clarification callouts in iter-04/findings.md and iter-05-go-backend/findings.md remain correct for the zero-UUID case. They need a second- layer update pointing to iter-07: the byte-level work in iter-4 and D.5 was correct; the handshake was rejected solely due to the UUID field, not any framing issue. That update lives in the top-level ../index.md.