Repository navigation
Description
What happened
In the Mac desktop app, linking an existing SSH-connected Linux environment through Settings → Linked environments → Link fails. T3 automatically tries the machine’s LAN and numeric Tailscale addresses, then rejects both as unsafe plain HTTP.
The Linux environment remains connected through SSH over Tailscale.
Diagnosis
Current upstream source shows a mismatch between automatic address selection and link validation.
PeerLinks.ts rejects non-loopback HTTP origins unless isTailnetHost accepts the hostname.
isTailnetHost accepts .ts.net names and Tailscale IPv6 addresses, but deliberately excludes numeric shared-space IPv4 addresses.
The automatic linking flow supplied a numeric IPv4 address confirmed by tailscale status to belong to the target machine. That address therefore cannot pass validation.
Source references are from upstream, not a verified copy of the installed bundle. No automated reproduction was run.
Steps to reproduce
- Connect the Mac desktop app to a Linux environment using SSH through its Tailscale MagicDNS hostname.
- Open Settings → Linked environments → Link on the Mac environment.
- Select the existing Linux environment and attempt to link it.
- Observe T3 automatically trying LAN and numeric Tailscale HTTP addresses.
- Both addresses are rejected with the error below.
Expected behavior: automatic linking selects a usable, trusted route or explains how to configure one.
Version
User reports the latest nightly. Local triage context recorded Mac server version 0.1.4-nightly.20261028.3814. Exact desktop and remote Linux build versions were not independently verified.
Triage CLI version was 0.1.2.
Environment
Mac desktop app on macOS, Darwin 25.6.0, arm64. Local Node version v26.12.1.
Remote Linux environment connected through SSH over Tailscale. tailscale status confirms the target is active with a direct connection.
Evidence
Addresses below are anonymized, retaining their relevant ranges.
PeerLinkError: This environment could not reach linux:
http://192.168.1.20:3773/ is plain http, which would send the link's credentials unencrypted. Use its https address, or its Tailscale address.
http://100.105.1.20:3773/ is plain http, which would send the link's credentials unencrypted. Use its https address, or its Tailscale address.
Related issues
#26431 concerns credentials sent over plain HTTP. This report concerns automatic linking selecting a confirmed Tailscale address that its own validation rejects. No exact duplicate was found.
Fix applied or workaround
No configuration changes were made. Manually linking through the machine’s .ts.net hostname was suggested but has not been tested.
Filed by
Codex, GPT-6.1-sol, via t3 triage.
Activity
Gabriel200622 commentedon Oct 29, 2026
Still reproduces on
0.1.6-nightly.20261029.3892(both ends: Linux desktop AppImage → macOS desktop app, same tailnet, direct connection), via Settings → Linked environments → + Link, picking the machine from the list.One more detail that makes this harder to diagnose: the peer's advertised URLs include its own
http://127.0.0.1:3773/. From the linking machine that loopback address answers as the local environment, soansweringAsgets one success with a non-matchingenvironmentId. Because not every address was refused, therefusals.length === checked.lengthbranch is skipped and the user gets the generic message with no reasons:The span
PeerLinks.answeringAsfailed in ~8 ms. Both non-loopback addresses answer/.well-known/t3/environmentwith the rightenvironmentIdvia curl from the linking machine. The real reasons (plain-http LAN and numeric Tailscale IPv4 both refused bypeerOrigin) are never shown.Suggestions: don't offer a peer's loopback address to another machine, report refusals even when some other address answered as the wrong environment, and offer the peer's MagicDNS
*.ts.netname, whichisTailnetHostaccepts.Workaround that works:
t3 environment link http://<machine>.<tailnet>.ts.net:3773 --pairing-code <code> --access full-access, where the code comes fromt3 auth pairing create --scope orchestration:read --scope orchestration:operate --scope settings:write --scope source-control:writeon the peer. Linked and reachable.Produced by Claude Code (Claude Opus 5.5) via
t3 triage(triage CLI 0.1.5).NathanGlasby commentedon Oct 29, 2026
Still reproducible on the macOS desktop app, version
0.1.6-nightly.20261029.3892(arm64).Linking my Linux environment automatically selected its numeric Tailscale address (
http://100.x.x.x:3773/), then rejected it as plain HTTP.Workaround confirmed: manually adding and linking through
http://<machine>.<tailnet>.ts.net:3773/succeeded. Both linked environments now show Reachable.The numeric address was reachable; the failure was T3’s address validation.
Investigated with Codex, GPT-6.1-sol.
Bafff commentedon Nov 3, 2026
Still reproduces on
0.1.7-nightly.20261102.4093, andt3 environment link http://100.x.x.x:3773 --pairing-code …from the CLI fails the same way as the UI.One extra detail: because the peer's
127.0.0.1URL answers (as the caller itself), the error shown is the generic "could not reach at " instead of the actual cause (the plain-http refusal), so it looks like a network problem. Surfacing the refusal reasons in that message would have saved a lot of debugging.(Comment written with Claude Code on behalf of @Bafff.)
KyleCole90 commentedon Nov 4, 2026
Also reproduces on stable v0.1.7 at both ends: two macOS desktop apps (darwin arm64, macOS 27.2) on the same tailnet, with a direct Tailscale connection between them. I picked the MacBook in Settings → Linked environments → + Link on the Mac mini, and it fails every time.
The error shown:
What each URL did (from the Mac mini's
server.trace.ndjson;PeerLinks.answeringAsgave up after about 83 ms, 2026-11-04 13:17:34 UTC):http://192.168.1.127:3773peerOrigin(plain http to a LAN address); no request senthttp://100.64.96.40:3773peerOrigin, becauseisTailnetHostdoesn't accept 100.64/10 IPv4; no request senthttps://<macbook>.<tailnet>.ts.netHttpClientError: Transport error, because the linking machine has Tailscale DNS (--accept-dns) off, so MagicDNS names don't resolvehttp://127.0.0.1:3773Further detail for this thread:
.ts.netURL passes the security check, and Tailscale config can make it unreachable without T3 saying so. In this setup it also depends on the target allowing inbound tailnet connections. The target hasShieldsUp: true, so TCP from the peer to100.64.96.40times out on every port even thoughtailscale pingsucceeds. T3's error says nothing about this. Users who keep Shields Up on, or MagicDNS off, have no working link path at all, even though both machines see each other on the tailnet.127.0.0.1answer hides the real cause again here. Because one URL succeeded (as the wrong environment),refusals.length !== checked.length, so the message drops the "plain http" refusals and looks like a network outage. Reporting each URL's outcome, or not counting a mismatched environment ID as a success, would have made this a one-minute diagnosis.http://127.0.0.1:3773/, which can never reach the target from another machine.Environment: both ends run T3 Code (Beta) desktop v0.1.7, stable channel, macOS 27.2 arm64, server
databaseMigration79. The target exposes T3 viatailscale serve(tailnet only) proxying to127.0.0.1:3773.Workaround: none that keeps these Tailscale settings. #26475 (accept a Tailscale machine's 100.x address) would fix this case. Note it would still need Shields Up off on the target, since the link needs an inbound connection.
Triaged with Claude Code (Opus 5.5,
claude-opus-5-5) viat3 triage.