fix(wda): reach WDA via CoreDevice tunnel IP on iOS 17+ devices - #171
Merged
Conversation
bootstrap-device built, installed, and launched WDA fine on a physical
iOS 26 device, but the /status smoke (and every later tool call) failed
with "Connection refused": WDA announces the first on-device IP it finds
via ServerURLHere — often `localhost` (no routable Wi-Fi IP) or a Wi-Fi IP
on a network the Mac can't reach (AP isolation / different subnet) — and
simdrive smoke-tested that host directly. On iOS 17+ a USB-attached device
is reachable from the host only via the RemoteServiceTunnel, whose address
devicectl reports as connectionProperties.tunnelIPAddress (an IPv6 ULA).
Add choose_reachable_host(): probe the tunnel IP, the announced host, then
localhost, and use the first that answers GET /status. WDA binds 0.0.0.0
on-device, so the tunnel IP reaches it. The resolved host is persisted to
the registry, so session_start / wda/client.py connect to a reachable
address (a bracketed IPv6 flows through f"http://{host}:{port}" unchanged).
Wired into both bootstrap_device() and the wda-up relaunch path.
Validated end-to-end on Moes Max (iPhone 17 Pro Max, iOS 26.5.1): WDA
announced 192.168.1.10 (unreachable), fell back to [fd1a:431c:b5a0::1],
smoke passed ready=True, device session started + drove the app.
Tests: tests/test_a14_wda_tunnel_host.py (5) cover tunnel fallback for
localhost + unreachable Wi-Fi IP, no-tunnel fallback, and the
nothing-reachable error path. Full wda/bootstrap suite stays green.
**Not done:** does not set up a persistent host→device port-forward (uses
the tunnel IP directly, which suffices because WDA binds 0.0.0.0); does not
touch the simulator path or the MCP version-drift auto-restart (fix #1 was
already resolved in b12's _MCP_SERVER_MODE gate); not yet pushed/PR'd.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
bootstrap-devicebuilds, installs, and launches WDA fine on a physical iOS 26 device, but the/statussmoke (and every later tool call) fails with Connection refused. WDA announces the first on-device IP it finds viaServerURLHere->http://<ip>:<port>— oftenlocalhost(no routable Wi-Fi IP) or a Wi-Fi IP on a network the Mac can't reach (AP isolation / different subnet) — and simdrive smoke-tested that host directly.On iOS 17+ a USB-attached device is reachable from the host only via the RemoteServiceTunnel, whose address devicectl reports as
connectionProperties.tunnelIPAddress(an IPv6 ULA, e.g.fd1a:431c:b5a0::1).Fix
Add
choose_reachable_host(): probe the tunnel IP → announced host →localhost, and use the first that answersGET /status. WDA binds0.0.0.0:<port>on-device, so the tunnel IP reaches it. The resolved host is persisted to the registry, sosession_start/wda/client.pyconnect to a reachable address (a bracketed IPv6 flows throughf"http://{host}:{port}"unchanged). Wired into bothbootstrap_device()and the wda-up relaunch path.Validation
End-to-end on Moes Max (iPhone 17 Pro Max, iOS 26.5.1): WDA announced
192.168.1.10(unreachable from host) → fell back to[fd1a:431c:b5a0::1]→ smoke passedready=True→ device session started and drove the app (observe / swipe / tap) in Airplane Mode over USB.tests/test_a14_wda_tunnel_host.py(5) — tunnel fallback forlocalhost+ unreachable Wi-Fi IP, no-tunnel fallback, nothing-reachable error path.Note on the MCP auto-restart issue
Separately observed: a stale (pre-
b12) MCP server auto-restarts on version drift and wedges the stdio transport (-32602on every call). That is already fixed inb12by the_MCP_SERVER_MODEgate inserver.py(suppressesos.execvwhile serving MCP) — no change needed here.🤖 Generated with Claude Code