fix(ios): rank sync routes by health, not interface presence#813
Conversation
iOS remote-connect ordered routes by interface presence, not route health: relay was appended dead-last whenever a tailnet interface was present, and a dead tailnet host expanded into a 213-port sweep (8787-8999) before relay was ever dialed. Reconnect re-preferred tailnet on a 20s timer with no memory of the last-good route. Port desktop's proven model to iOS: - rank candidates by reachability, then kind (lan<tailnet<relay), then last-succeeded-at within kind. Relay is first-class (never presence- demoted) and a live LAN always beats a sticky relay, so returning home can no longer lock the phone onto the cloud relay. - bound port fallback to 9 ports (was 213) - persist per-endpoint lastSucceededAt, stamped at the hello health gate - add a subtle "Connected in 0.3s / tailnet" pill so the win is felt - remove the 20s tailnet-preference heuristic and now-dead plumbing Health-gated connect (hello RPC), DPoP, and pairing are unchanged. 70 SyncService tests pass; iOS Simulator build is green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe iOS connection stack now persists endpoint success history, ranks direct and relay routes, records connection timing and route metadata, simplifies reconnect APIs, and displays the latest connection telemetry in Settings. ChangesConnection health and telemetry
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Phase 1 — Accounts / Machine-Directory / Connection-Reliability initiative
Fixes a diagnosed iOS remote-connect reliability bug. Standalone and independently shippable — no account/identity dependency.
The bug
iOS ordered connection routes by interface presence, not route health:
8787–8999) before relay was ever dialed;Net effect: slow, flaky reconnects away from home — the exact case the relay exists to serve.
The fix (ports desktop's proven model to iOS)
lan < tailnet < relay) → last-succeeded-at within kind. Relay is first-class (never presence-demoted); a live LAN always beats a sticky relay, so returning to a home network can no longer lock the phone onto the cloud relay.lastSucceededAt), stamped at the hello health gate.Connected in 0.3s · tailnetpill so the improvement is felt.syncRelayAwareEndpointAttemptswrapper,preferTailnetboolean).Invariants preserved
Health-gated connect (
.connectedonly after a validhelloRPC), DPoP, and pairing are unchanged.Testing
SyncServiceXCTest cases pass, including new regression tests: relay reached before the fallback sweep regardless of interface; live LAN ahead of a sticky relay + last-good within kind; winning-route-only success stamping; socket-open-without-hellodoes not connect.xcodebuild buildgreen.Reviewed via a two-track independent review (correctness/security + maintainability) that caught and fixed a relay-lock-in regression before this PR was opened.
🤖 Generated with Claude Code
Greptile Summary
This PR updates iOS sync reconnect routing to choose routes by health instead of interface presence. The main changes are:
hellohealth gate succeeds.Confidence Score: 5/5
Safe to merge with low risk.
The routing changes are localized, keep the existing
hello-gated connection contract, and store new profile state as optionalCodablefields for compatibility.No files require special attention.
What T-Rex did
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant UI as Settings UI participant Sync as SyncService participant Rank as syncRankedEndpointAttempts participant Route as LAN/Tailnet/Relay route participant Profile as HostConnectionProfile UI->>Sync: reconnectIfPossible() Sync->>Sync: collect live + saved direct addresses Sync->>Sync: collect saved relay candidates Sync->>Rank: direct attempts, relay routes, endpointStates, live routes Rank-->>Sync: ranked primary attempts, then bounded fallback ports loop until hello succeeds Sync->>Route: open websocket attempt Route-->>Sync: socket open or failure Sync->>Route: hello health gate Route-->>Sync: hello_ok or failure end Sync->>Profile: stamp winning endpoint lastSucceededAt Sync->>UI: publish route kind + connect duration%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant UI as Settings UI participant Sync as SyncService participant Rank as syncRankedEndpointAttempts participant Route as LAN/Tailnet/Relay route participant Profile as HostConnectionProfile UI->>Sync: reconnectIfPossible() Sync->>Sync: collect live + saved direct addresses Sync->>Sync: collect saved relay candidates Sync->>Rank: direct attempts, relay routes, endpointStates, live routes Rank-->>Sync: ranked primary attempts, then bounded fallback ports loop until hello succeeds Sync->>Route: open websocket attempt Route-->>Sync: socket open or failure Sync->>Route: hello health gate Route-->>Sync: hello_ok or failure end Sync->>Profile: stamp winning endpoint lastSucceededAt Sync->>UI: publish route kind + connect durationReviews (1): Last reviewed commit: "fix(ios): rank sync routes by health, no..." | Re-trigger Greptile
Summary by CodeRabbit
New Features
Improvements