Repository navigation
Keep Tailscale routes behind authenticated Iroh - #8122
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds Tailscale-derived private Iroh path hints, integrates them into reconnect and route selection, tracks active Tailscale network profiles, and expands tests for synthesis, route pinning, runtime authorization, and dial fallback behavior. ChangesIroh private path routing
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant RouteSelection
participant CmxAttachRoute
participant TailscaleStatus
participant MobileIrohNetworkPathState
RouteSelection->>CmxAttachRoute: addingIrohPrivatePaths(routes, observedAt)
CmxAttachRoute-->>RouteSelection: decorated Iroh routes
MobileIrohNetworkPathState->>TailscaleStatus: evaluate current interface addresses
TailscaleStatus-->>MobileIrohNetworkPathState: active Tailscale status
MobileIrohNetworkPathState-->>RouteSelection: active network profile snapshot
🚥 Pre-merge checks | ✅ 24 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (24 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 |
Greptile SummaryThis PR routes Tailscale connectivity through authenticated Iroh by synthesizing short-lived fallback-only
Confidence Score: 5/5Safe to merge — the synthesizer is a pure count-preserving transformation with no identity side-effects; the Tailscale profile is always computed live and fail-closed; EndpointID pinning and the no-downgrade invariant are unchanged. All three call sites (reconnect, ticket, broker merge) apply the transformation before filtering, so hint collection sees the full route set. The per-route injection logic correctly handles de-dup, refresh, and capacity limits in every traced edge case. The precondition in merged and the static let + preconditionFailure for activeTailscaleTunnel address the two invariant-enforcement concerns from the previous review cycle. Test coverage spans hint metadata, IPv6 bracketing, MagicDNS rejection, expired-hint slot reclamation, Tailscale profile gating, and dial-plan authorization. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant App
participant PathState as MobileIrohNetworkPathState
participant Interfaces as NetworkInterfaceAddressProviding
participant Synthesizer as addingIrohPrivatePaths
participant Routes as [CmxAttachRoute]
App->>PathState: snapshot()
PathState->>Interfaces: currentInterfaceAddresses()
Interfaces-->>PathState: [NetworkInterfaceAddress]
Note over PathState: TailscaleStatus == .active?
PathState-->>App: CmxIrohNetworkPathSnapshot (activeNetworkProfiles incl. activeTailscaleTunnel)
App->>Synthesizer: addingIrohPrivatePaths(to: routes, observedAt: Date())
Note over Synthesizer: Phase 1 - collect candidates: scan .tailscale .hostPort routes, reject LAN/public/MagicDNS, de-dup by (kind,value,source,networkProfile)
Note over Synthesizer: Phase 2 - inject per Iroh route: filter usableHints, refresh same-key stale hints, respect maximumIrohPathHintCount
Synthesizer-->>Routes: "Iroh routes with fallback-only hints (privacyScope=.privateNetwork, use=.fallbackOnly, networkProfile=.activeTailscaleTunnel)"
App->>Routes: irohDialPlan(at:managedRelayURLs:activeNetworkProfiles:)
Note over Routes: hint.networkProfile in activeNetworkProfiles? privateFallbackPaths includes Tailscale hint
Routes-->>App: IrohDialPlan (Tailscale carries Iroh, no raw Stack bearer granted)
%%{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 App
participant PathState as MobileIrohNetworkPathState
participant Interfaces as NetworkInterfaceAddressProviding
participant Synthesizer as addingIrohPrivatePaths
participant Routes as [CmxAttachRoute]
App->>PathState: snapshot()
PathState->>Interfaces: currentInterfaceAddresses()
Interfaces-->>PathState: [NetworkInterfaceAddress]
Note over PathState: TailscaleStatus == .active?
PathState-->>App: CmxIrohNetworkPathSnapshot (activeNetworkProfiles incl. activeTailscaleTunnel)
App->>Synthesizer: addingIrohPrivatePaths(to: routes, observedAt: Date())
Note over Synthesizer: Phase 1 - collect candidates: scan .tailscale .hostPort routes, reject LAN/public/MagicDNS, de-dup by (kind,value,source,networkProfile)
Note over Synthesizer: Phase 2 - inject per Iroh route: filter usableHints, refresh same-key stale hints, respect maximumIrohPathHintCount
Synthesizer-->>Routes: "Iroh routes with fallback-only hints (privacyScope=.privateNetwork, use=.fallbackOnly, networkProfile=.activeTailscaleTunnel)"
App->>Routes: irohDialPlan(at:managedRelayURLs:activeNetworkProfiles:)
Note over Routes: hint.networkProfile in activeNetworkProfiles? privateFallbackPaths includes Tailscale hint
Routes-->>App: IrohDialPlan (Tailscale carries Iroh, no raw Stack bearer granted)
Reviews (3): Last reviewed commit: "fix(ios): reclaim stale private path cap..." | Re-trigger Greptile |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ios/cmuxPackage/Sources/cmuxFeature/MobileIrohRouteCatalog.swift (1)
216-228: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick winUse the decorated
teamroutes instead of discarding them.The logic applies
addingIrohPrivatePathstopersonal + team, which correctly generatesdecoratedRoutes. However, the loop then iterates over the originalteamarray, causing any Tailscale path hints added to the team routes to be silently discarded.To preserve the private-path decorations on team routes, use the suffix of
decoratedRoutes.🐛 Proposed fix
- var routeIDs = Set(personal.map(\.id)) - var peerIdentities = Set<CmxIrohPeerIdentity>(personal.compactMap { route in + let teamWithPrivatePaths = decoratedRoutes.dropFirst(personal.count) + var routeIDs = Set(personalWithPrivatePaths.map(\.id)) + var peerIdentities = Set<CmxIrohPeerIdentity>(personalWithPrivatePaths.compactMap { route in guard case let .peer(identity, _) = route.endpoint else { return nil } return identity }) - for route in team { + for route in teamWithPrivatePaths { guard routeIDs.insert(route.id).inserted else { continue } if case let .peer(identity, _) = route.endpoint, !peerIdentities.insert(identity).inserted { continue } merged.append(route) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ios/cmuxPackage/Sources/cmuxFeature/MobileIrohRouteCatalog.swift` around lines 216 - 228, Update the merge loop in the route-catalog construction to iterate over the team-route suffix of decoratedRoutes rather than the original team array, preserving Tailscale path hints added by addingIrohPrivatePaths. Keep the existing route ID and peer identity deduplication logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CmxIrohPrivatePathSynthesizer.swift`:
- Around line 36-58: Update the route transformation in
CmxIrohPrivatePathSynthesizer to remove existing hints that are not usable at
observedAt before enforcing CmxAttachEndpoint.maximumIrohPathHintCount. Replace
the nested tailscaleHints × existingHints scans with a single deduplicated hint
index and a bounded candidate set, then merge candidates into each eligible
.iroh peer route without repeated full scans, preserving route order and
existing deduplication semantics.
---
Outside diff comments:
In `@ios/cmuxPackage/Sources/cmuxFeature/MobileIrohRouteCatalog.swift`:
- Around line 216-228: Update the merge loop in the route-catalog construction
to iterate over the team-route suffix of decoratedRoutes rather than the
original team array, preserving Tailscale path hints added by
addingIrohPrivatePaths. Keep the existing route ID and peer identity
deduplication logic unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1b12f22d-bfd5-4cbc-854f-0d052c23f1ad
📒 Files selected for processing (8)
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CmxIrohPrivatePathSynthesizer.swiftPackages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/CmxIrohPrivatePathSynthesizerTests.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+ReconnectRoutes.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/IrohReconnectRouteSelectionTests.swiftios/cmuxPackage/Sources/cmuxFeature/MobileIrohNetworkPathState.swiftios/cmuxPackage/Sources/cmuxFeature/MobileIrohRouteCatalog.swiftios/cmuxPackage/Tests/cmuxFeatureTests/MobileIrohRuntimeCompositionTests.swift
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Summary
Security model
Tailscale supplies an optional network path. Iroh still authenticates the Mac by EndpointID before cmux sends its session token. A stale address or wrong tailnet cannot substitute another endpoint, and raw-only legacy routes remain fail-closed until authenticated discovery supplies an Iroh identity.
Verification
The full CmuxMobileShell suite still has its existing foreground-resume timing failure on the unchanged non-Tailscale path; focused route and security coverage passes.
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Route Tailscale connectivity through authenticated Iroh by attaching short‑lived private
.directAddresshints to Iroh routes and pinning connections to Iroh. This prevents raw Tailscale downgrades while still letting Tailscale carry Iroh when a tunnel is active.New Features
activeTailscaleTunnel; rejects LAN, public, MagicDNS, and generic hosts..irohand drop raw host/port fallbacks once Iroh exists; EndpointID pinning is preserved.Hardening
CmxIrohNetworkProfileKey.activeTailscaleTunnel; stale or wrong tailnets cannot bypass Iroh; tests added acrossCMUXMobileCore,CmuxMobileShell, andcmuxFeature.Written for commit 879c4ee. Summary will update on new commits.
Summary by CodeRabbit