Skip to main content
← Back to list
01Issue
BugOpenSwamp CLI
AssigneesNone

Relationships

#1423 tailscale transport hangs indefinitely on Tailscale SSH check-mode auth; never surfaces the login URL

Opened by dmc · 7/27/2026

Summary

When a host in an @swamp/ssh fleet uses a kind: tailscale transport and the connecting user's Tailscale SSH check-mode authentication has lapsed (ACL ssh rule with action: "check"), exec (and presumably script/copy) blocks indefinitely on the interactive tailscale ssh browser auth flow. The underlying tailscale ssh prints # To authenticate, visit: https://[HOST-1]/a/<token> and waits; swamp surfaces nothing — no URL, no status, no indication that auth is what it's waiting on. The caller just hangs.

Environment

  • swamp 20260725.011748.0-sha.bb2cb3ef
  • @swamp/ssh v2026.07.02.1 (typeVersion 2026.07.02.1)
  • Target: Alpine host, tailscale 3.23.3, reached as kind: tailscale, user: root
  • Tailnet SSH rule is check-mode (periodic browser re-auth, default 12h)

Reproduction

  1. Fleet host with transport: { kind: tailscale, user: root }, tailnet ACL grants SSH via action: "check".
  2. Let the check-mode auth lapse (or use a fresh session that has never authed).
  3. Run: swamp model method run <fleet> exec --arg hosts=<host> --arg command='echo ok' --arg captureOutput=true

Observed (no timeout set): the call hangs indefinitely. tailscale ssh opens a login URL in a browser on the swamp host; swamp gives the caller no signal. Headless (e.g. under swamp serve) this can never complete.

Observed (with --arg timeoutSec=12): killed after the timeout, returning:

exec failed on 1/1 host(s): valkey (killed by SIGTERM: # To authenticate, visit: https://[HOST-1]/a/<redacted-token>)

So the auth URL does exist in stderr, but is only visible because swamp collapses stderr to its last line and the URL happened to be last. Anything printed after it would be dropped.

Impact

  • Interactive: silent hang with no explanation; the user has no idea they need to complete a browser login.
  • Headless / swamp serve: a scheduled workflow step hangs forever (or until an unrelated timeout), because there is no browser to complete check-mode auth and no machine-readable signal to fail fast on.

Suggested fixes

  1. Detect the interactive-auth-required condition (the [HOST-1]/a/ URL, and/or tailscale ssh blocking on a TTY prompt) and fail fast with the URL surfaced as a structured field rather than blocking — e.g. an error like tailscale SSH auth required: visit <url>.
  2. Apply a sane default timeout to tailscale-transport connections so a lapsed-auth host can't hang a method/step forever when timeoutSec isn't set.
  3. Stop collapsing stderr to the last line on failure — return the full captured stderr (or at least the auth URL) so diagnostic context (fingerprints, URLs, multi-line tailscale messages) isn't lost. This truncation also obscured an unrelated earlier failure ("...node's SSH host key as advertised via the Tailscale coordination server.").
  4. Optionally run tailscale ssh non-interactively / with -o BatchMode-equivalent so it errors instead of prompting.

Workaround

Set timeoutSec on the method and pattern-match the returned error for [HOST-1]/a/ to detect and surface the auth URL. For automation, use an action: "accept" SSH ACL rule (no periodic check-auth) instead of "check".

Upstream repository: https://github.com/swamp-club/swamp-extensions

Environment

  • Extension: @swamp/ssh@2026.07.02.1
  • swamp: 20260725.011748.0-sha.bb2cb3ef
  • OS: darwin (aarch64)
  • Deno: 2.8.3
  • Shell: /bin/zsh
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

7/27/2026, 12:54:27 AM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

dmc commented 7/27/2026, 12:54:44 AM

Clarification: the auto-redactor replaced a public domain — every [HOST-1] in the body is [HOST-1]. So the detection signal in fix #1 is specifically the URL pattern https://[HOST-1]/a/<token>, and the captured error was literally:

exec failed on 1/1 host(s): <host> (killed by SIGTERM: # To authenticate, visit: https://[HOST-1]/a/<token>)

[HOST-1] is Tailscale's public login host, not private infra.

dmc commented 7/27/2026, 12:55:03 AM

Correcting the redaction (this content is a public domain, deliberately unsanitized): everywhere the body and my previous comment show [HOST-1], the real value is Tailscale's public login host. The detection signal for fix #1 is the URL pattern that host serves under /a/<token>, i.e. the # To authenticate, visit: … line emitted by tailscale ssh. Matching on that host + /a/ path is how a caller can distinguish 'check-mode auth required' from other failures.

Sign in to post a ripple.