From 203d75dffc25b799b58fb7ba63c50ffe8687529f Mon Sep 17 00:00:00 2001 From: root-daemon Date: Sun, 25 Oct 2026 02:32:01 +0530 Subject: [PATCH 1/2] fix(web): Tailscale HTTPS no longer says "Start Tailscale" while it runs When the tailscale CLI cannot be run (on macOS it is often missing from the app's PATH), the desktop still finds the Tailscale IP from the network adapter but gets no MagicDNS name, and the HTTPS row told users to start Tailscale. With a Tailscale IP present, it now says Tailscale is running and its MagicDNS name could not be read. Fixes #15919 --- .../ConnectionsSettings.logic.test.ts | 26 +++++++++++++++++++ .../settings/ConnectionsSettings.logic.ts | 15 +++++++++-- .../settings/ConnectionsSettings.tsx | 5 +++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts b/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts index a97eb6abac676..d1cd9d891ff4f 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts +++ b/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts @@ -1,7 +1,9 @@ import type { DesktopWslState } from "@t3tools/contracts"; +import { createAdvertisedEndpoint } from "@t3tools/shared/advertisedEndpoint"; import { describe, expect, it, vi } from "vite-plus/test"; import { applyWslEnableSelection, + missingTailscaleEndpointDescription, selectQrEndpointOption, togglePairingScopeSelection, } from "./ConnectionsSettings.logic"; @@ -123,3 +125,27 @@ describe("selectQrEndpointOption", () => { ); }); }); + +describe("missingTailscaleEndpointDescription", () => { + const networkAccessible = { mode: "network-accessible", tailscaleServeEnabled: false } as const; + const tailscaleIp = createAdvertisedEndpoint({ + id: "tailscale-ip:http://100.84.12.7:4780", + label: "Tailscale IP", + provider: { id: "tailscale", label: "Tailscale", kind: "private-network", isAddon: true }, + httpBaseUrl: "http://100.84.12.7:4780", + reachability: "private-network", + source: "desktop-addon", + }); + + it("does not ask to start Tailscale when its adapter is up but MagicDNS could not be read", () => { + expect(missingTailscaleEndpointDescription(networkAccessible, [tailscaleIp])).toBe( + "Tailscale is running, but its MagicDNS name could not be read. Make sure the tailscale command is installed and MagicDNS is on.", + ); + }); + + it("asks to start Tailscale when no Tailscale address is present", () => { + expect(missingTailscaleEndpointDescription(networkAccessible, [])).toBe( + "Start Tailscale to set up HTTPS access through MagicDNS.", + ); + }); +}); diff --git a/apps/web/src/components/settings/ConnectionsSettings.logic.ts b/apps/web/src/components/settings/ConnectionsSettings.logic.ts index db5ba42ae7c6b..b224701bc8667 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.logic.ts +++ b/apps/web/src/components/settings/ConnectionsSettings.logic.ts @@ -73,9 +73,16 @@ export function isQrShareableEndpoint(endpoint: AdvertisedEndpoint): boolean { */ export function missingTailscaleEndpointDescription( state: Pick | null, + endpoints: ReadonlyArray, ): string { - return state && state.mode !== "network-accessible" && !state.tailscaleServeEnabled - ? "Turn on Network access to detect Tailscale." + if (state && state.mode !== "network-accessible" && !state.tailscaleServeEnabled) { + return "Turn on Network access to detect Tailscale."; + } + // The Tailscale IP comes from the network adapter, but the MagicDNS name + // needs `tailscale status`. An IP alone means Tailscale is running and the + // CLI could not be used: on macOS it is often missing from the app's PATH. + return endpoints.some(isTailscaleIpEndpoint) + ? "Tailscale is running, but its MagicDNS name could not be read. Make sure the tailscale command is installed and MagicDNS is on." : "Start Tailscale to set up HTTPS access through MagicDNS."; } @@ -377,6 +384,10 @@ export function isTailscaleHttpsEndpoint(endpoint: AdvertisedEndpoint): boolean return endpoint.id.startsWith("tailscale-magicdns:"); } +function isTailscaleIpEndpoint(endpoint: AdvertisedEndpoint): boolean { + return endpoint.id.startsWith("tailscale-ip:"); +} + export function endpointDefaultPreferenceKey(endpoint: AdvertisedEndpoint): string { if (endpoint.id.startsWith("desktop-loopback:")) { return "desktop-core:loopback:http"; diff --git a/apps/web/src/components/settings/ConnectionsSettings.tsx b/apps/web/src/components/settings/ConnectionsSettings.tsx index fea873dc4e529..1353edaec59b9 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.tsx +++ b/apps/web/src/components/settings/ConnectionsSettings.tsx @@ -916,7 +916,10 @@ export function ConnectionsSettings() { ? tailscaleHttpsEndpoint.status === "available" ? tailscaleHttpsEndpoint.httpBaseUrl : "Use Tailscale Serve to expose this backend through a MagicDNS HTTPS URL." - : missingTailscaleEndpointDescription(desktopServerExposureState) + : missingTailscaleEndpointDescription( + desktopServerExposureState, + desktopAdvertisedEndpoints, + ) } control={ tailscaleHttpsEndpoint ? ( From 2ba046fe104e13088816767facdcc65beef39e6c Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Mon, 26 Oct 2026 20:18:01 +0000 Subject: [PATCH 2/2] fix(web): simplify Tailscale detection guidance and cover disabled access --- .../settings/ConnectionsSettings.logic.test.ts | 8 ++++++++ .../src/components/settings/ConnectionsSettings.logic.ts | 9 +-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts b/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts index d1cd9d891ff4f..07bc8cc9e8610 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts +++ b/apps/web/src/components/settings/ConnectionsSettings.logic.test.ts @@ -143,6 +143,14 @@ describe("missingTailscaleEndpointDescription", () => { ); }); + it("keeps detection-off guidance when Network access is off, even with a cached IP", () => { + expect( + missingTailscaleEndpointDescription({ mode: "local-only", tailscaleServeEnabled: false }, [ + tailscaleIp, + ]), + ).toBe("Turn on Network access to detect Tailscale."); + }); + it("asks to start Tailscale when no Tailscale address is present", () => { expect(missingTailscaleEndpointDescription(networkAccessible, [])).toBe( "Start Tailscale to set up HTTPS access through MagicDNS.", diff --git a/apps/web/src/components/settings/ConnectionsSettings.logic.ts b/apps/web/src/components/settings/ConnectionsSettings.logic.ts index aeb7c44da8406..1733d93c0a57b 100644 --- a/apps/web/src/components/settings/ConnectionsSettings.logic.ts +++ b/apps/web/src/components/settings/ConnectionsSettings.logic.ts @@ -66,10 +66,7 @@ export function missingTailscaleEndpointDescription( if (state && state.mode !== "network-accessible" && !state.tailscaleServeEnabled) { return "Turn on Network access to detect Tailscale."; } - // The Tailscale IP comes from the network adapter, but the MagicDNS name - // needs `tailscale status`. An IP alone means Tailscale is running and the - // CLI could not be used: on macOS it is often missing from the app's PATH. - return endpoints.some(isTailscaleIpEndpoint) + return endpoints.some((endpoint) => endpoint.id.startsWith("tailscale-ip:")) ? "Tailscale is running, but its MagicDNS name could not be read. Make sure the tailscale command is installed and MagicDNS is on." : "Start Tailscale to set up HTTPS access through MagicDNS."; } @@ -363,10 +360,6 @@ export function isTailscaleHttpsEndpoint(endpoint: AdvertisedEndpoint): boolean return endpoint.id.startsWith("tailscale-magicdns:"); } -function isTailscaleIpEndpoint(endpoint: AdvertisedEndpoint): boolean { - return endpoint.id.startsWith("tailscale-ip:"); -} - export function endpointDefaultPreferenceKey(endpoint: AdvertisedEndpoint): string { if (endpoint.id.startsWith("desktop-loopback:")) { return "desktop-core:loopback:http";