Summary
On-device interop testing shows that the Android ↔ iOS peer pair cannot establish a direct QUIC path and falls back to the relay, even though both peers now advertise all their routable direct addresses (#348). The request still completes (relay returns HTTP 200), so this is a connectivity/hole-punching robustness gap rather than a functional outage. Every other pair in the matrix (desktop↔iOS, desktop↔Android, and — in run-3 — Android→desktop) reaches transport=direct.
Evidence
From two consecutive on-device passes (both phones + desktop node on one LAN), the direct-dial-fetch case (tests/interop/suite.mjs, which passes all advertised addresses via directAddrs so iroh races every candidate):
- run-2
android→ios: HTTP 200 via 10.12.222.17:64939 — transport=relay (reached over RELAY, not direct ✗)
- run-3
android→ios: HTTP 200 via 10.12.222.17:54534 — transport=relay (reached over RELAY, not direct ✗)
Note 10.12.222.17 is iOS's VPN/CG-NAT interface; iOS also advertises a real LAN 192.168.x address (post-#348), yet the pair still lands on relay. run-3 also showed android→desktop reaching direct, so the failure is specific to the Android↔iOS pair, not Android-as-client in general.
Impact
- Android↔iOS traffic always traverses the relay: higher latency, relay bandwidth dependency, and it defeats the core P2P-direct value proposition for that pair.
- Not a hard failure — relay fallback delivers correct responses (200, uncorrupted body).
- Likely environmental (interface enumeration / NAT / candidate exchange between the two mobile stacks), so it needs investigation before we know whether it is fixable in our layer or is an iroh/network characteristic.
Remediation
- Capture iroh path/candidate diagnostics on both peers during an Android→iOS dial (which candidate pairs are tried, which time out) via
peerStats() / connection-path events.
- Confirm both peers actually exchange the LAN candidate (TXT carries it; verify the dialer imports it and iroh attempts it) vs. it being gathered-but-not-punched.
- Determine whether the block is a firewall/AP client-isolation issue on the test LAN, an iOS/Android interface-binding limitation, or a real gap in our address plumbing.
- If environmental, document the finding and downgrade; if a plumbing gap, fix and add a device-verified regression note.
Acceptance criteria
- Root cause identified: environmental vs. product (address gather/import/punch).
- If fixable in our layer, Android→iOS and iOS→Android
direct-dial-fetch report transport=direct on a clean LAN, device-verified.
- If environmental, the constraint is documented (e.g. in the device-pass runbook / protocol notes) and the case's expectation is adjusted so it no longer reads as an unexplained regression.
Summary
On-device interop testing shows that the Android ↔ iOS peer pair cannot establish a direct QUIC path and falls back to the relay, even though both peers now advertise all their routable direct addresses (#348). The request still completes (relay returns HTTP 200), so this is a connectivity/hole-punching robustness gap rather than a functional outage. Every other pair in the matrix (desktop↔iOS, desktop↔Android, and — in run-3 — Android→desktop) reaches
transport=direct.Evidence
From two consecutive on-device passes (both phones + desktop node on one LAN), the
direct-dial-fetchcase (tests/interop/suite.mjs, which passes all advertised addresses viadirectAddrsso iroh races every candidate):android→ios:HTTP 200 via 10.12.222.17:64939 — transport=relay (reached over RELAY, not direct ✗)android→ios:HTTP 200 via 10.12.222.17:54534 — transport=relay (reached over RELAY, not direct ✗)Note
10.12.222.17is iOS's VPN/CG-NAT interface; iOS also advertises a real LAN192.168.xaddress (post-#348), yet the pair still lands on relay. run-3 also showedandroid→desktopreaching direct, so the failure is specific to the Android↔iOS pair, not Android-as-client in general.Impact
Remediation
peerStats()/ connection-path events.Acceptance criteria
direct-dial-fetchreporttransport=directon a clean LAN, device-verified.