Repository navigation
[Bug]: Proton VPN address is advertised as Tailscale IP and breaks phone pairing #15750
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
- Run T3 Code Desktop on Linux with Proton VPN's kill-switch interface and Tailscale present. On the affected host,
pvpnksintrf1has100.85.0.1andtailscale0has100.74.126.34. - Open Settings → Connections and enable Network access.
- Select the advertised Tailscale IP option using
100.85.0.1, create a pairing link, and open it on a phone connected to the same tailnet from another network. - Compare the advertised address with
tailscale ip -4on the host.
The failure also appears with a freshly computed endpoint list: the current provider emits the Proton address before the actual Tailscale address.
Expected behavior
The Tailscale pairing option should advertise the host's actual Tailscale address. An unrelated VPN interface should not be labeled as reachable through the tailnet.
Actual behavior
T3 advertises http://100.85.0.1:3773/ as Tailscale IP and uses it in the pairing link. The phone's request to /.well-known/t3-environment times out after 10 seconds. Replacing only the host in the link with 100.74.126.34 makes pairing succeed.
Impact
Major degradation or frequent failure: the generated remote-pairing link is unusable unless the address is manually corrected.
Version or commit
Observed on desktop 0.0.45. Source reproduction confirmed on main at 4ee6bfd50ef4a089440d5c3662db2298da9cc50e.
Environment
Ubuntu 24.04.4 LTS; Linux x86_64 AppImage; Proton VPN; Tailscale 1.102.4; iPhone connected to the same tailnet.
Relevant evidence
Local interfaces:
pvpnksintrf1 100.85.0.1
tailscale0 100.74.126.34
tailscale ip -4:
100.74.126.34
Current endpoint provider:
http://100.85.0.1:3773/
http://100.74.126.34:3773/
The provider scans every interface and accepts anything matching isTailscaleIpv4Address, which only tests the shared 100.64.0.0/10 range. That range alone does not establish ownership by Tailscale.
The saved Tailscale default is keyed by endpoint type, so the first incorrectly admitted candidate can remain selected. Filtering out the unrelated candidate lets the existing selection logic resolve to the real address.
Related but different: #9882 separates LAN and Tailscale entries; #11920 proposed broader discovery changes for macOS permission prompts and was closed without merging during the V2 transition.
Workaround
Use tailscale ip -4 and replace the address in the generated pairing URL while preserving the port, path, and token. This worked on the affected phone. Reloading alone does not change the range-based classification.
Activity
juliusmarminge commentedon Oct 4, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the clear report and the working workaround, @arthurkatcher!
What I found
Confirmed on
mainat4ee6bfd. This is separate from #15751, where the Connections list stays cached after the network changes.Why the wrong address gets picked:
isTailscaleIpv4Address(packages/tailscale/src/tailscale.ts) accepts any address in100.64.0.0/10. That's shared CGNAT space, not Tailscale's alone.pvpnksintrf0/pvpnksintrf1) with100.85.0.1/24, which falls in that range.resolveTailscaleIpAdvertisedEndpoints(apps/desktop/src/backend/tailscaleEndpointProvider.ts) scansObject.values(networkInterfaces), so interface names are dropped and listing order decides which address comes first.tailscale-ip:endpoint shares the saved preference keytailscale:ip:http(endpointDefaultPreferenceKeyinConnectionsSettings.tsx), and pairing takes the first match. So the Proton address stays selected, and the phone's/.well-known/t3-environmentrequest times out.The same range check also affects:
isUsableLanIpv4Address).resolveRuntimeStateinapps/desktop/src/backend/DesktopServerExposure.ts).Related, not duplicates:
pvpnksintrf*by name alone wouldn't cover it.Likely fix area
Some options:
readTailscaleStatusalready returnsSelf.TailscaleIPsastailnetIpv4Addresses, but the desktop path only keeps the MagicDNS name. Its cached result could be used to filter the interface candidates. Spawningtailscale statuson every settings read should be avoided, since that brings back the macOS "Other apps" prompt.tailscale*/utun*-style interfaces when several CGNAT addresses exist.tailscale:ip:httppreference key, so a stale first candidate doesn't stay selected.Until then, your workaround works: keep the port, path, and token, and replace only the host with the address from
tailscale ip -4.A maintainer will decide on the fix direction.
xixixao commentedon Oct 9, 2026
Ran into this with Cloudflare WARP as well.