Skip to content

Surface the Tailscale SSH check login link on a remote host (DEV-2106) - #9

Merged
ZmeiGorynych merged 7 commits into
mainfrom
egor/dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on
Oct 18, 2026
Merged

ZmeiGorynych merged 7 commits into
mainfrom
egor/dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on

Conversation

@ZmeiGorynych

@ZmeiGorynych ZmeiGorynych commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Fixes DEV-2106.

Why

When a remote host is reached over Tailscale SSH and the tailnet rule uses "action": "check", an expired check makes sshd print a login link on standard error and hold the connection until someone approves it in a browser. BatchMode=yes doesn't stop this. The hub only read SSH's standard error after the process ended, so the host sat in connecting forever with no reason and no way to find the link. The only workaround was running ssh <alias> true in a terminal.

What changes

  • Hub: until the daemon's first frame, a remote link reads SSH's standard error as it arrives. A To authenticate, visit: https://login.tailscale.com/… line makes the host needs-login, with the URL in a new host-entry field login. The link stays open and no failure is counted. Once the check is approved, the same SSH process continues and the host connects with no retry.
  • One approval connects every page: when one session's link to a host greets, every other session's needs-login link to that host is redialed at once. The redial reuses the now-authenticated control master. Sessions in any other status are left alone.
  • Page: a needs-login host gets a banner, "box: needs Tailscale login", with an Open login link button (target="_blank", rel="noopener noreferrer"). Its repo tabs show needs login.
  • Expired check: if SSH gives up, the host falls back to the normal reconnecting/down path with SSH's reason. The failure count carries over from before the check.

Before: the host stays connecting with no reason. After:

box: needs Tailscale login  [Open login link]

Safety of the link

The URL ends up in an href, so it is checked at both ends. The parser and the wire decoder accept only strings starting with https://login.tailscale.com/, at most 1024 characters, and present exactly when the status is needs-login. Lookalikes such as https://login.tailscale.com.evil/…, http: and javascript: are rejected.

Compatibility

BROWSER_PROTOCOL_VERSION goes from 8 to 9. The daemon protocol is unchanged, so no daemon restart is needed. An open page reloads once under the existing stale-bundle rule.

Tests

  • Unit tests:
    • the line parser and line scanner
    • host transitions
    • a remote link with a fake SSH (URL reported within 1 s, reported once, ignored after the first frame, close or end)
    • stale login callbacks from replaced links
    • the wire rules for login
    • web banner and status helpers
  • Process tests with a per-invocation fake-SSH login gate:
    • approval over the same process
    • expiry, then a new URL
    • host-unavailable for requests to a needs-login host
    • three-session sibling redial
  • An e2e test for the banner, the link attributes and the tab status.

Also: the WebGL LRU property test made one assertion per addon at every step. It now makes a single check (921 ms → 41 ms), so it no longer times out under full-suite load.

The spec change is in openspec/changes/dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Remote hosts awaiting Tailscale SSH authentication now show a “needs login” status and a banner with a secure link to complete sign-in.
    • Host status updates automatically after authentication, and pending connections can retry without restarting the daemon.
    • Added architecture and remote-host guidance to the documentation.

ZmeiGorynych and others added 3 commits October 17, 2026 17:03
OpenSpec change for a needs-login host status carrying the Tailscale
check URL, live stderr scanning on remote links, sibling-session
redial, and the page banner with the login link.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… link

Unit, process and e2e tests for the needs-login host status: the login
line parser and line scanner, live stderr scanning on a remote link,
host transitions, stale login callbacks, the login field's wire rules
(browser protocol 9), the web banner and tab status, and sibling-session
redial. The fake SSH gains a per-invocation Tailscale login gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A remote link scans SSH's standard error live until the daemon's first
frame and reports a login.tailscale.com check URL; the host becomes
needs-login with the URL in the new host-entry field `login` (browser
protocol 9). When one session's link to a host greets, other sessions'
needs-login links to it redial at once. The page shows a banner with an
"Open login link" button and "needs login" on the host's repo tabs.

Also keeps the protocol 8 hosts sample in the wire corpus, updates the
version strings tests expect, and makes the WebGL LRU property test's
per-step check one assertion so it no longer times out under load.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: MotleyAI/worktree-term/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 4ea39397-5fe8-44d9-8a86-1610bd1e3125
📥 Commits

Reviewing files that changed from the base of the PR and between b938295 and 129652a.

📒 Files selected for processing: 10
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/.openspec.yaml
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/design.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/proposal.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/hub/spec.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/protocol/spec.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/web/spec.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/tasks.md
  • openspec/specs/hub/spec.md
  • openspec/specs/protocol/spec.md
  • openspec/specs/web/spec.md
💤 Files with no reviewable changes: 7
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/.openspec.yaml
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/web/spec.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/proposal.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/protocol/spec.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/design.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/tasks.md
  • openspec/changes/archive/2026-10-18-dev-2106-worktree-term-surface-the-tailscale-ssh-check-login-link-on/specs/hub/spec.md

Included review availability: This review used your included allowance. 3 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

📝 Walkthrough

The hub now detects Tailscale SSH login checks and reports a needs-login host state with a validated URL. The browser protocol advances to version 9. The router can redial matching sessions after a host connects, and the web UI displays a login link and status.

Changes: Tailscale SSH login handling

Layer / File(s) Summary
Browser protocol host contract
src/protocol/*, src/cli/run.test.ts, test/process/cli.test.ts, test/process/install-local.test.ts, openspec/specs/protocol/spec.md, openspec/changes/archive/.../specs/protocol/spec.md
The browser protocol version changes from 8 to 9; the daemon protocol remains 6. Host entries gain a nullable login URL that is required only for needs-login, with validation, fixtures, and version expectations updated.
SSH stderr login detection
src/platform/dialer/*, src/hub/links/*, openspec/changes/archive/.../design.md, openspec/changes/archive/.../proposal.md
The dialer parses supported Tailscale login URLs from newline-delimited stderr. ProcessLink reports each URL before the first protocol frame and ignores repeated URLs and output after closure or termination.
Host login state and session redial
src/hub/router/*, test/process/hub-router.test.ts, test/support/fake-ssh.ts, test/support/daemon-host.ts, test/process/fake-ssh.test.ts, openspec/specs/hub/spec.md, openspec/changes/archive/.../specs/hub/spec.md, openspec/changes/archive/.../tasks.md, openspec/changes/archive/.../.openspec.yaml
Host transitions preserve failure counts while setting or clearing login state. When a session greets a host, the router redials matching pending-login links in other sessions. Tests cover retries, stale callbacks, unavailable-host errors, and cross-session behavior.
Login banner and repo-tab status
src/web/ui/*, src/web/main/styles.css, test/e2e/*, README.md, openspec/specs/web/spec.md, openspec/changes/archive/.../specs/web/spec.md
The web UI displays a login link for needs-login hosts and shows “needs login” on repo tabs. The end-to-end test checks the link and the status clearing after connection. The README adds architecture and remote-host guidance.

WebGL LRU test assertion

Layer / File(s) Summary
Addon disposal assertion
src/web/terminals/webgl-lru.test.ts
The consistency assertion now checks that no addon has been disposed more than once.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant LoginBanner
  participant SSHProcess
  participant ProcessLink
  participant HostLink
  participant Router
  participant OtherSession
  Browser->>LoginBanner: Open the host login URL
  SSHProcess->>ProcessLink: Write login banner to stderr
  ProcessLink->>HostLink: Report parsed login URL
  HostLink->>Browser: Send needs-login host entry
  Browser->>SSHProcess: Approve login check
  SSHProcess->>HostLink: Exchange daemon hello
  HostLink->>Router: Report greeted host
  Router->>OtherSession: Redial matching needs-login link
Loading
Merge Risk: ⚪ Minimal · up to 12965

The change surfaces the Tailscale approval link and allows waiting sessions to reconnect after approval. The reviewed contracts and behavior show no unresolved issue, so the PR appears ready for normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the primary change: exposing the Tailscale SSH check login link for remote hosts.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 31 files. (10 skipped: 10 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

ZmeiGorynych and others added 4 commits October 18, 2026 11:30
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… runs

rmSync empties a directory once and then retries only removing it, so a
call recorded by an SSH run that started before the hub was killed made
cleanup fail with ENOTEMPTY after 5.5 s. Cleanup now also kills runs that
have not recorded their call yet and retries the whole removal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@ZmeiGorynych
ZmeiGorynych merged commit 2f2eb53 into main Oct 18, 2026
3 checks passed
ZmeiGorynych added a commit that referenced this pull request Oct 18, 2026
…ce-the-tailscale-ssh-check-login-link-on

Surface the Tailscale SSH check login link on a remote host (DEV-2106)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant