Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate. Related but different: Desktop T3 Connect toggle for a remote environment always fails with "Invalid managed endpoint origin" #32422 and [Bug]: iOS Live Activities toggle always fails with "Invalid managed endpoint origin" (link-proof called over LAN/tunnel, host accepts loopback only) #27412 (link-proof requests over LAN or a tunnel), [Bug]: Tailscale Serve proxies the wrong address when the server binds a specific interface #34733 (the Tailscale Serve bug fix(server): Tailscale Serve forwards to the address a specific --host binds #34741 fixed).
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Run a headless server bound to a specific non-loopback address, for example its tailnet IP:
t3 serve --host 100.x.y.z --port 3773 --no-browser(as a systemd service). - Link it to T3 Connect (managed tunnel), so the desired link is stored.
- Restart the server.
- Run
t3 connect status.
Expected behavior
The server reconciles its desired T3 Connect link on startup and the managed tunnel connects, as it did before #34741.
Actual behavior
Every startup logs Failed to reconcile T3 Connect desired link on startup with CloudLinkOriginInvalidError (origin: 'endpoint'), no tunnel starts, and t3 connect status reports T3 Connect is NOT reachable from other devices · The server has not set up a T3 Connect tunnel.
Cause: #34741 changed loopbackUrlHostForAddress (apps/server/src/startupAccess.ts) to return the bound address for a specific --host. Besides Tailscale Serve, two T3 Connect callers build the managed tunnel's local origin from it: the startup reconcile in server.ts (const localOrigin = …) and applyRelayConfig in cloud/CloudLink.ts. With a non-loopback bind the origin becomes http://100.x.y.z:3773, and isAllowedEndpointOrigin (cloud/linkChecks.ts) rejects any origin whose localHttpHost is not loopback, so the link proof is refused before it reaches the relay.
Before #34741 the origin was 127.0.0.1, so the link reconciled and the tunnel targeted loopback. That only reached the server when something listened there (we run a small loopback-to-tailnet forwarder for exactly this), so a non-loopback bind without one could not serve T3 Connect either way. It may be worth deciding which of the two the managed tunnel should target for a specific bind, and making the link checks agree with it.
Impact
Major degradation or frequent failure
Version or commit
Upstream main since 8ea4a88097 (#34741); still the same code at 580b526758.
Environment
Headless server on Ubuntu 24.04 x86_64, Node 26.12.1, run as a systemd service bound to its tailnet address. Two servers set up this way both lost T3 Connect on their first restart after updating past #34741.
Logs or stack traces
WARN: Failed to reconcile T3 Connect desired link on startup
{ cause: { _tag: 'CloudLinkOriginInvalidError', origin: 'endpoint' } }
✕ T3 Connect is NOT reachable from other devices
The server has not set up a T3 Connect tunnel.
Screenshots, recordings, or supporting files
No response
Workaround
Keep the T3 Connect origin on loopback for a non-loopback bind and run a loopback listener that forwards to the server. In our fork, the two T3 Connect callers use 127.0.0.1 unless the bind address is loopback or a wildcard, while Tailscale Serve keeps #34741's behaviour; after that, both servers reconnected (✓ Other devices can reach this machine · Tunnel connected · 4 connections · QUIC).
#34741's test covers loopbackUrlHostForAddress alone; no test checks that the managed tunnel origin for a specific bind still passes isAllowedEndpointOrigin, which would have caught this.
Activity
docs: section 32 reported upstream as pingdotgg#36500