Skip to content

fix(server): open in editor works over Tailscale SSH without a local sshd - #19402

Merged
t3dotgg merged 1 commit into
mainfrom
t3/fix-11052
Oct 14, 2026
Merged

t3dotgg merged 1 commit into
mainfrom
t3/fix-11052

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Oct 14, 2026

Copy link
Copy Markdown
Member

On a host that serves SSH only through Tailscale SSH, "Open in editor" said "No SSH route", even though ssh <tailnet-name> works. The server advertised no SSH targets unless sshd listened on loopback :22, and it checked that before it looked at Tailscale.

This follows the split gate from the triage on #11052:

  • A loopback sshd still advertises the tailnet name and the mDNS name, as before.
  • With no loopback sshd, the server advertises only the tailnet name, and only when Tailscale SSH is on. It reads RunSSH from tailscale debug prefs, because tailscale status --json does not report SSH for the local node. tailscale down keeps RunSSH stored, so WantRunning: false counts as off.
  • A running tailscaled alone (for example --tailscale-serve) still advertises nothing.

@t3tools/tailscale gets readTailscaleSshEnabled. It uses the same command runner as readTailscaleStatus, which is now shared, so readTailscaleStatus behaves the same. No client or contract change: kind: "tailscale" targets already exist.

Proof

  • New test advertises only the tailnet name when Tailscale SSH serves without sshd failed on main (got []) and passes with the fix. Tests also cover Tailscale SSH off and a stored RunSSH after tailscale down.
  • RemoteOpenTargets.test.ts 7/7 and packages/tailscale 15/15 pass. Typecheck passes for packages/tailscale, apps/server, and apps/desktop. Lint is clean on the changed files.
  • Tested with fixtures only. It was not run against a real Tailscale SSH host.

Fixes #11052

Reviewed with sol-loop: 1 round with GPT-6.1-Sol.

Model and harness: Claude Opus 5.5 in Claude Code via T3 Code.

🤖 Generated with Claude Code

…sshd

Remote open targets returned nothing when no sshd listened on loopback
:22, before Tailscale was checked. Tailscale SSH serves the tailnet name
without a local listener, so the editor picker said "No SSH route".

Read RunSSH from `tailscale debug prefs` and advertise the tailnet name
when Tailscale SSH is on, even with no local sshd. The mDNS name still
needs a local sshd, and a running tailscaled alone advertises nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 14, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 14, 2026
@github-actions github-actions Bot added the size:M 30-99 changed lines (additions + deletions). label Oct 14, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 62d6c16

Macroscope's review found this PR approvable — This is a localized bug fix that detects enabled Tailscale SSH and exposes an existing tailnet editor target only for hosts without local sshd. Existing sshd behavior is preserved, failure cases fall back safely, and the new paths are covered by tests.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: c3ce0ac8-c44c-43bf-8acc-b05f0c9f4933
📥 Commits

Reviewing files that changed from the base of the PR and between 2cde3f7 and 62d6c16.

📒 Files selected for processing: 4
  • apps/server/src/environment/RemoteOpenTargets.test.ts
  • apps/server/src/environment/RemoteOpenTargets.ts
  • packages/tailscale/src/tailscale.test.ts
  • packages/tailscale/src/tailscale.ts

Limit details: You’ve used all 10 included reviews currently available.

📝 Walkthrough

Remote target discovery now checks Tailscale SSH preferences. When no local SSH listener exists, it advertises the MagicDNS target only when the name is available and Tailscale SSH is enabled.

Changes: Tailscale SSH Target Discovery

Layer / File(s) Summary
Read Tailscale SSH preferences
packages/tailscale/src/tailscale.ts, packages/tailscale/src/tailscale.test.ts
A shared read-only command runner handles Tailscale commands. New helpers read and parse SSH preferences, with tests for enabled, disabled, missing, and malformed preferences.
Advertise Tailscale SSH targets
apps/server/src/environment/RemoteOpenTargets.ts, apps/server/src/environment/RemoteOpenTargets.test.ts
Target discovery can advertise a MagicDNS name when no loopback SSH listener exists and Tailscale SSH is enabled. Tests cover the status and preferences command results and target outcomes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 62d6c

The change is mergeable after normal checks. Discovery is designed to avoid advertising a target when the tested Tailscale SSH-off state is present.

Pre-merge checks: Passed 4
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary fix: enabling editor access over Tailscale SSH when no local sshd is present.
Description check Passed The description clearly explains the problem, implementation, scope, test coverage, observed results, limitations, issue reference, and agent attribution. It uses "Proof" instead of "Verification" and…
Linked Issues check Passed Issue #11052 requires remote target discovery for Tailscale SSH without a loopback SSH listener. RemoteOpenTargets.ts now reads the MagicDNS name and readTailscaleSshEnabled, then advertises a `ki…
Out of Scope Changes check Passed The changes remain within issue #11052. The shared Tailscale command runner, readTailscaleSshEnabled, parser tests, and remote-target tests directly support the required discovery behavior. No unrel…
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 4.9 KiB −23 B (−0.5%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB −23 B (−1.9%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.8 KiB −41 B (−0.2%) 29.3 KiB ✅
Codex Live turn messages 2 1 −1 (−50.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB −24 B (−0.5%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB −24 B (−2.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB −41 B (−0.2%) 29.3 KiB ✅
Claude Live turn messages 2 1 −1 (−50.0%) 8 ✅

Baseline: 2cde3f7 · PR result: 62d6c16 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@t3dotgg
t3dotgg merged commit b037b9a into main Oct 14, 2026
33 checks passed
@t3dotgg
t3dotgg deleted the t3/fix-11052 branch October 14, 2026 11:49
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 14, 2026
## What's Changed
* fix(clients): # suggestions include pull requests linked to the thread by @t3dotgg in pingdotgg/t3code#19357
* fix(clients): pairing a switched-off computer again turns it back on by @t3dotgg in pingdotgg/t3code#19354
* fix(cli): t3 triage reports the server's version, not its own, as the user's build by @t3dotgg in pingdotgg/t3code#19359
* fix(client-runtime): a new WSL backend no longer shows Limited permissions until reload by @t3dotgg in pingdotgg/t3code#19361
* fix(web): action shortcuts recorded in the browser now save by @t3dotgg in pingdotgg/t3code#19365
* fix(clients): large pasted images no longer break later Claude image reads by @t3dotgg in pingdotgg/t3code#19364
* fix(server): settled and idle-released threads no longer stay Waiting by @t3dotgg in pingdotgg/t3code#19355
* perf(client-runtime): merged pull requests are no longer re-read every minute by @t3dotgg in pingdotgg/t3code#19374
* fix(server): new Codex threads no longer miss slow MCP servers' tools by @t3dotgg in pingdotgg/t3code#19368
* fix(github): requested teams now show in pull request reviewers by @t3dotgg in pingdotgg/t3code#19375
* fix(ssh): SSH launch no longer picks a port another server answers on by @t3dotgg in pingdotgg/t3code#19372
* fix(server): a failed T3 Connect relink no longer leaves a stale managed tunnel by @t3dotgg in pingdotgg/t3code#19383
* fix(shared): Windows .cmd shims no longer print DEP0190 on every spawn by @t3dotgg in pingdotgg/t3code#19385
* fix(server): commit and title generation use your custom model when no text model is set by @t3dotgg in pingdotgg/t3code#19386
* fix(web): T3 Connect settings can repair a link that drifted to publish-only by @t3dotgg in pingdotgg/t3code#19389
* fix(server): failed Claude text generation shows the CLI's own error by @t3dotgg in pingdotgg/t3code#19394
* fix(web): terminal tells the shell its new width as soon as you release a resize drag by @t3dotgg in pingdotgg/t3code#19395
* fix(web): keybindings page says to connect an environment when none is connected by @t3dotgg in pingdotgg/t3code#19400
* fix(chat): Codex follow-up suggestions show their label instead of raw markup by @t3dotgg in pingdotgg/t3code#19401
* fix(server): open in editor works over Tailscale SSH without a local sshd by @t3dotgg in pingdotgg/t3code#19402
* fix(server): a deregistered T3 Connect host shows it was removed and offers a relink by @t3dotgg in pingdotgg/t3code#19379
* perf(server): idle servers no longer scan every process every 5 seconds by @t3dotgg in pingdotgg/t3code#19399
* chore(typecheck): bump tsc-rs to 0.7.0 by @t3dotgg in pingdotgg/t3code#19408
* fix(server): bound Bitbucket API requests with 30s timeout by @kvnloo in pingdotgg/t3code#14140
* fix(server): new worktrees no longer fail when a background fetch runs at the same time by @t3dotgg in pingdotgg/t3code#19411
* perf(server): thread search runs on the SQLite reader worker by @bompus in pingdotgg/t3code#19382
* perf(server): traces keep ~40% more history and no longer block the event loop by @t3dotgg in pingdotgg/t3code#19430
* fix(shared): SQLite checkpoints no longer fall back to the main thread silently by @t3dotgg in pingdotgg/t3code#19417
* perf(server): unchanged node updates no longer write to SQLite by @t3dotgg in pingdotgg/t3code#19429
* fix(ssh): reconnecting keeps the remote server and its agents running by @t3dotgg in pingdotgg/t3code#19432
* feat(observability): SQL spans show lock wait and connection; stalls keep their own history by @t3dotgg in pingdotgg/t3code#19425
* fix(server): threads woken by notifications no longer load their whole history by @t3dotgg in pingdotgg/t3code#19423
* fix(server): worktree cleanup no longer runs sweeps back to back by @t3dotgg in pingdotgg/t3code#19418


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261014.3105...v0.0.46-nightly.20261014.3118

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261014.3118
github-actions Bot added a commit to davidvanderklay/t3code-flake that referenced this pull request Oct 14, 2026
## What's Changed
* fix(clients): # suggestions include pull requests linked to the thread by @t3dotgg in pingdotgg/t3code#19357
* fix(clients): pairing a switched-off computer again turns it back on by @t3dotgg in pingdotgg/t3code#19354
* fix(cli): t3 triage reports the server's version, not its own, as the user's build by @t3dotgg in pingdotgg/t3code#19359
* fix(client-runtime): a new WSL backend no longer shows Limited permissions until reload by @t3dotgg in pingdotgg/t3code#19361
* fix(web): action shortcuts recorded in the browser now save by @t3dotgg in pingdotgg/t3code#19365
* fix(clients): large pasted images no longer break later Claude image reads by @t3dotgg in pingdotgg/t3code#19364
* fix(server): settled and idle-released threads no longer stay Waiting by @t3dotgg in pingdotgg/t3code#19355
* perf(client-runtime): merged pull requests are no longer re-read every minute by @t3dotgg in pingdotgg/t3code#19374
* fix(server): new Codex threads no longer miss slow MCP servers' tools by @t3dotgg in pingdotgg/t3code#19368
* fix(github): requested teams now show in pull request reviewers by @t3dotgg in pingdotgg/t3code#19375
* fix(ssh): SSH launch no longer picks a port another server answers on by @t3dotgg in pingdotgg/t3code#19372
* fix(server): a failed T3 Connect relink no longer leaves a stale managed tunnel by @t3dotgg in pingdotgg/t3code#19383
* fix(shared): Windows .cmd shims no longer print DEP0190 on every spawn by @t3dotgg in pingdotgg/t3code#19385
* fix(server): commit and title generation use your custom model when no text model is set by @t3dotgg in pingdotgg/t3code#19386
* fix(web): T3 Connect settings can repair a link that drifted to publish-only by @t3dotgg in pingdotgg/t3code#19389
* fix(server): failed Claude text generation shows the CLI's own error by @t3dotgg in pingdotgg/t3code#19394
* fix(web): terminal tells the shell its new width as soon as you release a resize drag by @t3dotgg in pingdotgg/t3code#19395
* fix(web): keybindings page says to connect an environment when none is connected by @t3dotgg in pingdotgg/t3code#19400
* fix(chat): Codex follow-up suggestions show their label instead of raw markup by @t3dotgg in pingdotgg/t3code#19401
* fix(server): open in editor works over Tailscale SSH without a local sshd by @t3dotgg in pingdotgg/t3code#19402
* fix(server): a deregistered T3 Connect host shows it was removed and offers a relink by @t3dotgg in pingdotgg/t3code#19379
* perf(server): idle servers no longer scan every process every 5 seconds by @t3dotgg in pingdotgg/t3code#19399
* chore(typecheck): bump tsc-rs to 0.7.0 by @t3dotgg in pingdotgg/t3code#19408
* fix(server): bound Bitbucket API requests with 30s timeout by @kvnloo in pingdotgg/t3code#14140
* fix(server): new worktrees no longer fail when a background fetch runs at the same time by @t3dotgg in pingdotgg/t3code#19411
* perf(server): thread search runs on the SQLite reader worker by @bompus in pingdotgg/t3code#19382
* perf(server): traces keep ~40% more history and no longer block the event loop by @t3dotgg in pingdotgg/t3code#19430
* fix(shared): SQLite checkpoints no longer fall back to the main thread silently by @t3dotgg in pingdotgg/t3code#19417
* perf(server): unchanged node updates no longer write to SQLite by @t3dotgg in pingdotgg/t3code#19429
* fix(ssh): reconnecting keeps the remote server and its agents running by @t3dotgg in pingdotgg/t3code#19432
* feat(observability): SQL spans show lock wait and connection; stalls keep their own history by @t3dotgg in pingdotgg/t3code#19425
* fix(server): threads woken by notifications no longer load their whole history by @t3dotgg in pingdotgg/t3code#19423
* fix(server): worktree cleanup no longer runs sweeps back to back by @t3dotgg in pingdotgg/t3code#19418


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261014.3105...v0.0.46-nightly.20261014.3118

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261014.3118
vedprakash2302 added a commit to vedprakash2302/Cody that referenced this pull request Oct 15, 2026
Brings in upstream through pingdotgg#19257 (339179f).

- Partly superseded: upstream's Tailscale SSH remote open (pingdotgg#19402) replaces Cody's detection. Cody now uses upstream's `tailscale debug prefs` read, which also treats `tailscale down` as off, and drops its `tailscale get` reader, Windows skip, and Tailscale 1.102 requirement. Cody keeps only the tailscale-ssh target with the server's account, so editors sign in as that user.
- Adapted: Cody's install-when-idle updates now skip a version upstream reports as not applied (pingdotgg#19291), instead of reinstalling and restarting after every idle period.
- Server browser: kept Windows work-account sign-in beside upstream's copy recording (pingdotgg#19561).
- Mobile branch list: kept Cody's worktreeBaseRef import beside upstream's linked PR suggestions (pingdotgg#19357).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Open in editor reports no SSH route when using Tailscale SSH without a localhost listener

2 participants