Summary
Since 0.0.34 the server answers the CORS preflight correctly but omits Access-Control-Allow-Origin from the actual response. Any browser-context client whose origin differs from the server's therefore discards the response, and the desktop app never gets past Failed to connect. Reconnecting....
0.0.33 is the last version that sends the header. Rolling the server back to 0.0.33 — changing nothing else — restored the desktop client immediately.
Environment
- Server: headless
t3 serve --host <tailnet-ip> --port 3010, Linux x64, Bun runtime - Reached from the desktop over a separate HTTPS origin via Tailscale Serve
(https://<host>.ts.net:8445 → http://<tailnet-ip>:3010), valid Let's Encrypt cert - Desktop app: macOS, tested at 0.0.33, 0.0.35, 0.0.36, 0.0.37 — all fail against a 0.0.34+ server
Reproduction
t3 serve --host 127.0.0.1 --port 3099 --base-dir /tmp/t3probe
curl -sD- -o/dev/null -H 'Origin: https://app.t3.codes' \
http://127.0.0.1:3099/.well-known/t3/environment | grep -i access-control
| Server version | Released | Access-Control-Allow-Origin on GET |
|---|
| 0.0.33 | 2026-08-10 | * |
| 0.0.34 | 2026-08-26 | absent |
| 0.0.35 | 2026-08-27 | absent |
| 0.0.36 | 2026-08-29 | absent |
| 0.0.37 | 2026-08-31 | absent |
| 0.0.38-nightly.20260831 | 2026-08-31 | absent |
The preflight still looks right on 0.0.37:
$ curl -sD- -o/dev/null -X OPTIONS -H 'Origin: https://app.t3.codes' \
-H 'Access-Control-Request-Method: GET' .../.well-known/t3/environment
HTTP/2 204
access-control-allow-origin: *
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-headers: authorization,b3,traceparent,content-type,dpop
access-control-max-age: 600
The same GET that lacks the header returns 200 with the expected body, so this is purely a response-header regression. Reproduced on /.well-known/t3/environment, /api/auth/session and /health, and with every Origin value tried (https://app.t3.codes, file://, app://-, null, http://localhost:5173, the server's own origin).
Symptom in the desktop app
Provider settings are unavailable
Failed to connect. Reconnecting... Reason: Failed to fetch remote environment endpoint
https://<host>.ts.net:8445/.well-known/t3/environment
(HttpClientError: Transport error (GET https://<host>.ts.net:8445/.well-known/t3/environment))
What rules out a network or TLS problem:
curl -v from the same Mac, at the same moment, completes the handshake and returns HTTP/2 200 with the descriptor JSON; cert verifies, ALPN negotiates h2.- The iOS app connected to the same 0.0.35 server successfully while the desktop could not. A native client does not enforce CORS; curl does not either. Only the browser-context client fails.
- Server-side
/api/auth/clients showed no connection attempt from the desktop after the upgrade, while the stored bearer token was still valid for another two weeks.
Why it may have gone unnoticed
The common setup is a desktop app talking to a server on the same machine over loopback, where the request is not cross-origin. It only bites when the environment is remote and published on a different origin — the documented Tailscale Serve setup in docs/user/remote-access.md.
Expected
Responses carry Access-Control-Allow-Origin (as 0.0.33 did), so a remote environment on its own HTTPS origin stays reachable from browser-context clients.
Failing that, a documented way to configure allowed origins would be enough — there is no server setting for it today.
Summary
Since 0.0.34 the server answers the CORS preflight correctly but omits
Access-Control-Allow-Originfrom the actual response. Any browser-context client whose origin differs from the server's therefore discards the response, and the desktop app never gets pastFailed to connect. Reconnecting....0.0.33 is the last version that sends the header. Rolling the server back to 0.0.33 — changing nothing else — restored the desktop client immediately.
Environment
t3 serve --host <tailnet-ip> --port 3010, Linux x64, Bun runtime(
https://<host>.ts.net:8445→http://<tailnet-ip>:3010), valid Let's Encrypt certReproduction
Access-Control-Allow-OriginonGET*The preflight still looks right on 0.0.37:
The same GET that lacks the header returns
200with the expected body, so this is purely a response-header regression. Reproduced on/.well-known/t3/environment,/api/auth/sessionand/health, and with everyOriginvalue tried (https://app.t3.codes,file://,app://-,null,http://localhost:5173, the server's own origin).Symptom in the desktop app
What rules out a network or TLS problem:
curl -vfrom the same Mac, at the same moment, completes the handshake and returnsHTTP/2 200with the descriptor JSON; cert verifies, ALPN negotiates h2./api/auth/clientsshowed no connection attempt from the desktop after the upgrade, while the stored bearer token was still valid for another two weeks.Why it may have gone unnoticed
The common setup is a desktop app talking to a server on the same machine over loopback, where the request is not cross-origin. It only bites when the environment is remote and published on a different origin — the documented Tailscale Serve setup in
docs/user/remote-access.md.Expected
Responses carry
Access-Control-Allow-Origin(as 0.0.33 did), so a remote environment on its own HTTPS origin stays reachable from browser-context clients.Failing that, a documented way to configure allowed origins would be enough — there is no server setting for it today.