Skip to content

First-class Tailscale support (status, serve/funnel, doctor, docs) - #2451

Open
sethkarten wants to merge 1 commit into
mainfrom
rsi/tailscale-support
Open

sethkarten wants to merge 1 commit into
mainfrom
rsi/tailscale-support

Conversation

@sethkarten

@sethkarten sethkarten commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Motivation

Every AI harness needs to support Tailscale: agents run in containers, on laptops, and in CI; Tailscale is the standard zero-config way to reach them (user directive). This gives prime-agent first-class Tailscale awareness instead of leaving users to hand-wire it.

What it adds

  • prime-agent tailscale - tailnet state, MagicDNS name, and served endpoints (listen/path -> target from tailscale serve status --json)
  • prime-agent tailscale serve --port <n> [--funnel] - wraps tailscale serve --bg localhost:<n> / tailscale funnel --bg localhost:<n>; requires an explicit --port (never guesses a default), validates 1-65535 BEFORE any side effect, exits 1 on every failure
  • post-serve verification: tailscale can exit 0 after only printing an interactive enable URL without configuring anything (enableFeatureInteractive); the wrapper re-reads serve status --json and refuses success when the target is absent
  • --json machine-readable status
  • doctor integration (human mode only; doctor --json stays pure JSON)
  • docs: four patterns (Tailscale SSH remote control, serve/funnel exposure, MagicDNS reach from cloud containers, adding Tailscale's MCP server)

Backend-state diagnosis

BackendState drives the diagnosis, not just Self.Online: a node whose daemon is Running but currently unreachable is reported "up on a tailnet (currently offline)", not wrongly told to run tailscale up. Top-level MagicDNSSuffix is deprecated upstream; CurrentTailnet.MagicDNSSuffix is preferred when present.

Verification

  • 16 vitest tests: probe/stopped/offline states, serve argv construction (serve|funnel --bg localhost:<n> pinned exactly), post-serve verification (pending-enable detection), parser forms (--port n, --port=n, bare serve refusal, unknown subcommand errors), status failure diagnostics (empty-stderr case)
  • CLI detection uses Node ENOENT (no which dependency; Windows-safe)
  • serve spawn: stdin inherited (funnel's first enable prompts interactively), 60s timeout
  • dual-reviewer gate passed (Macroscope + Bugbot personas, empirical probes); all 13+ findings fixed in-branch
  • npm run check green (budget-clean: test lines <= source lines)

Note

Medium Risk
The new commands shell out to Tailscale and can expose local ports on the tailnet or publicly via funnel; mistakes in parsing or verification could misreport success, though serve requires an explicit port and post-serve checks mitigate false positives.

Overview
Adds prime-agent tailscale so the CLI can probe the local Tailscale install, report tailnet/MagicDNS/served endpoints, and wrap tailscale serve --bg / funnel --bg with an explicit --port (optional --funnel). Status supports --json; serve validates the port up front, re-reads serve status --json after a zero exit (so pending interactive enable flows do not count as success), and checks funnel ACL state when --funnel is set.

prime-agent doctor (human output only) appends one-line Tailscale facts from the same probe logic. Help/registry wiring, docs/tailscale.md, a changelog note, and vitest coverage (argv parsing, backend offline vs stopped, post-serve verification) ship with the feature.

Reviewed by Cursor Bugbot for commit 295255b. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add tailscale command for status, serve/funnel, and doctor integration

  • Adds a tailscale command group to the prime-agent CLI. The default status mode reports tailnet state, MagicDNS identity, and served endpoints, with machine-readable output via --json.
  • tailscale serve requires a numeric port, validates the port range, and exposes the local port via Tailscale serve or funnel. It verifies the exact port is configured post-command.
  • prime-agent doctor now includes Tailscale detection, reporting whether the CLI is missing, unusable, disconnected, offline, or on a tailnet.
  • Risk: runTailscale in tailscale.ts uses synchronous hidden execution with a 15-second timeout and SIGKILL termination; ensure this blocking call does not stall the CLI on hung processes.

Macroscope summarized 295255b.

@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown

Prime Agent performance — completed

PR 295255b3 compared with main ff40ea24.

Overall: 0 regressed · 0 improved · 41 no clear change.

Metric Main This PR Change
Cold startup 1,689.5 ms 1,687.2 ms ≈ -2.4 ms (-0.14%)
Warm startup 1,007.4 ms 1,007.2 ms ≈ -0.2 ms (-0.02%)
Installation 13.55 s 13.23 s ≈ -0.32 s (-2.39%)
Compressed release artifacts 72.61 MB 72.65 MB ≈ +0.05 MB (+0.06%)
Installed footprint 568.46 MB 568.51 MB ≈ +0.05 MB (+0.01%)
Idle memory, summed RSS 1,272.31 MB 1,216.12 MB ≈ -56.20 MB (-4.42%)

Python runtime

Metric Main This PR Change
Python kernel startup 110.6 ms 100.1 ms ≈ -10.5 ms (-9.46%)
Python cell round trip 0.562 ms 0.522 ms ≈ -0.040 ms (-7.19%)
Empty bash command 13.9 ms 11.7 ms ≈ -2.1 ms (-15.39%)
Bash git status 19.4 ms 16.9 ms ≈ -2.5 ms (-12.91%)
Bash 32 KiB output 13.8 ms 12.7 ms ≈ -1.1 ms (-8.16%)
35 cells / 9 shell calls 205.6 ms 186.4 ms ≈ -19.2 ms (-9.34%)
Python interrupt to done 1.751 ms 1.599 ms ≈ -0.152 ms (-8.67%)
Python state snapshot 28.2 ms 26.9 ms ≈ -1.3 ms (-4.61%)
Python state restore 436.5 ms 420.4 ms ≈ -16.1 ms (-3.68%)
Python idle RSS 27.73 MB 28.72 MB ≈ +0.99 MB (+3.58%)
Python RSS after pandas workload 96.04 MB 96.87 MB ≈ +0.83 MB (+0.86%)

Session transport

Metric Main This PR Change
Private frame decode, 32 MiB in 8 KiB chunks 29.1 ms 30.2 ms ≈ +1.1 ms (+3.75%)

UI interactions

Metric Main This PR Change
Resume large session (cold) 3,899.2 ms 4,165.8 ms ≈ +266.6 ms (+6.84%)
CPU, resume large session 6,330.0 ms 6,520.0 ms ≈ +190.0 ms (+3.00%)
Switch into large session 3,754.6 ms 3,768.4 ms ≈ +13.8 ms (+0.37%)
CPU, switch into large session 5,260.0 ms 5,470.0 ms ≈ +210.0 ms (+3.99%)
Open agents view from a session 160.6 ms 161.0 ms ≈ +0.4 ms (+0.25%)
CPU, open agents view 320.0 ms 320.0 ms ≈ +0.0 ms (+0.00%)
Full agents roster, many sessions 4.45 s 4.45 s ≈ +9.7e-07 s (+0.00%)
CPU, full agents roster 3.19 s 2.97 s ≈ -0.22 s (-6.90%)
Open another session from agents view 2,525.4 ms 2,558.0 ms ≈ +32.6 ms (+1.29%)
CPU, open from agents view 2,390.0 ms 2,490.0 ms ≈ +100.0 ms (+4.18%)
Reopen resident large session 568.9 ms 554.0 ms ≈ -14.9 ms (-2.62%)
CPU, reopen resident session 740.0 ms 760.0 ms ≈ +20.0 ms (+2.70%)
Open subagent session at depth 6 20,734.0 ms 21,130.7 ms ≈ +396.8 ms (+1.91%)
CPU, open subagent at depth 6 9,670.0 ms 9,770.0 ms ≈ +100.0 ms (+1.03%)
Open chain parent from agents view 4,095.4 ms 3,995.1 ms ≈ -100.3 ms (-2.45%)
CPU, open chain parent 3,780.0 ms 3,730.0 ms ≈ -50.0 ms (-1.32%)
Scheduled catalog, first request 1,851.4 ms 2,023.8 ms ≈ +172.3 ms (+9.31%)
CPU, scheduled catalog 2,990.0 ms 3,260.0 ms ≈ +270.0 ms (+9.03%)
Scheduled catalog, repeated request 3.9 ms 3.5 ms ≈ -0.4 ms (-9.96%)
CPU, repeated catalog 0.0 ms 10.0 ms ≈ +10.0 ms (N/A)
Cold worker with three catalog scans 960.2 ms 1,012.1 ms ≈ +51.9 ms (+5.40%)
CPU, cold worker and scans 1,580.0 ms 1,790.0 ms ≈ +210.0 ms (+13.29%)
UI memory after interactions 2,727.19 MB 2,693.46 MB ≈ -33.73 MB (-1.24%)

Sandbox cost: ~$0.1492 — no inference calls.
Run, logs, and downloadable raw results

Methodology and samples

Main resolved at 2026-09-18T09:44:30.733940+00:00. Harness ff40ea24.
Linux x64, 4 vCPU, 8 GB RAM, 20 GB disk; region us.
Image: node:24-bookworm@sha256:be23f54a88d34e8824c741b19b91064094f92c1c97b194144bfc8b50d67258e2.
Stock tools, skills, daemon, and Python bootstrap enabled; fresh homes and a fixed Git fixture.
Onboarding is dismissed; the editor starts without a selected model or submitted prompt.
Medians shown. Arrows require a 20% timing/memory change plus absolute floors and IQR.
These practical noise floors are not a statistical significance test.
Cold means stopped Prime processes; OS filesystem caches are not flushed.
No model requests or credentials. Installation excludes build/setup time.
Installer tarballs use loopback; npm/Python downloads use the network with fresh caches.
Artifact size counts release tarballs; footprint after first use includes registry packages.
MB is decimal. Summed RSS can double-count shared pages; PSS is recorded when available.
Provisioning, setup, and build durations are recorded separately in the raw results.
Kernel probes use the installed JSONL runtime, outside the TUI/TypeScript host.
Per trial: 50 Python cells, 5 calls per shell case, and one 35-cell mix (9 git status calls).
Cell/shell values are batch means; other runtime timings are single operations.
State fixture: a 10,000-row × 8-column integer DataFrame and a 10,000-integer list.
Restore runs in a fresh kernel, including pandas imports; kernel startup is excluded.
Kernel RSS covers the isolated Python process; loaded RSS follows the pandas workload.
Transport benches run node against the prepared source build, outside the installed home.
Frame decode times one 32 MiB private frame, snapshot-chunk header, pushed in
8 KiB chunks; the wire shape of multi-MB frames on the daemon-worker channels.
UI trials use a fresh fixture set: 194 top-level sessions including one ~40 MB transcript,
40 ledger fan-out children, and a 6-deep subagent chain (~46 spawn edges).
Large fixtures hold 1,999 complete triples (~5 MB JSONL); medium 119; subagents 399 each.
Interactions: cold --resume of a large session, warm /resume switch, left-arrow to agents view,
roster settle with many saved sessions, search-and-open of another large session,
reattaching to that resident session, opening the chain parent, and drilling to depth 6.
Readiness is the rendered transcript tail plus a confirmed editor echo.
CPU metrics sum utime+stime across the whole benchmark-user process tree per interaction.
UI memory sums RSS after the interactions; PTY byte counts are in the raw results.
A separate catalog fixture has 2,300 sessions, 2,298 edges, and 13 paused scheduled-job owners.
Catalog timings cover first/repeated reads and cold worker creation under three pending scans.
All expected jobs and owner metadata are checked; worker readiness excludes TUI rendering.
Costs estimate full sandbox lifetimes at configured rates, including setup and build.
Budget target: $1; not a billing cap. Performance changes are informational.
Failed or incomplete execution fails the workflow; saved artifacts remain available.
Each side stops a phase after 2 identical consecutive failures.
Skipped trials are not attempted samples. Warm startup requires a successful cold launch.

Metric Main successful/attempted PR successful/attempted Main spread PR spread
Cold startup 10/10 10/10 IQR 102.4 ms IQR 127.3 ms
Warm startup 10/10 10/10 IQR 64.7 ms IQR 90.0 ms
Installation 3/3 3/3 range 0.56 s range 1.75 s
Compressed release artifacts 1/1 1/1
Installed footprint 1/1 1/1
Idle memory, summed RSS 10/10 10/10 IQR 98.44 MB IQR 130.32 MB
Python kernel startup 10/10 10/10 IQR 7.3 ms IQR 3.4 ms
Python cell round trip 10/10 10/10 IQR 0.121 ms IQR 0.099 ms
Empty bash command 10/10 10/10 IQR 2.3 ms IQR 2.3 ms
Bash git status 10/10 10/10 IQR 2.4 ms IQR 2.5 ms
Bash 32 KiB output 10/10 10/10 IQR 2.3 ms IQR 2.3 ms
35 cells / 9 shell calls 10/10 10/10 IQR 20.4 ms IQR 32.6 ms
Python interrupt to done 10/10 10/10 IQR 0.398 ms IQR 0.283 ms
Python state snapshot 10/10 10/10 IQR 1.8 ms IQR 1.1 ms
Python state restore 10/10 10/10 IQR 49.4 ms IQR 57.1 ms
Python idle RSS 10/10 10/10 IQR 2.74 MB IQR 2.90 MB
Python RSS after pandas workload 10/10 10/10 IQR 2.37 MB IQR 3.73 MB
Private frame decode, 32 MiB in 8 KiB chunks 10/10 10/10 IQR 1.5 ms IQR 3.9 ms
Resume large session (cold) 3/3 3/3 range 742.7 ms range 849.9 ms
CPU, resume large session 3/3 3/3 range 320.0 ms range 780.0 ms
Switch into large session 3/3 3/3 range 51.2 ms range 204.1 ms
CPU, switch into large session 3/3 3/3 range 520.0 ms range 460.0 ms
Open agents view from a session 3/3 3/3 range 4.7 ms range 23.8 ms
CPU, open agents view 3/3 3/3 range 100.0 ms range 70.0 ms
Full agents roster, many sessions 3/3 3/3 range 0.81 s range 0.41 s
CPU, full agents roster 3/3 3/3 range 0.31 s range 0.38 s
Open another session from agents view 3/3 3/3 range 152.2 ms range 58.7 ms
CPU, open from agents view 3/3 3/3 range 160.0 ms range 290.0 ms
Reopen resident large session 3/3 3/3 range 68.8 ms range 21.9 ms
CPU, reopen resident session 3/3 3/3 range 60.0 ms range 80.0 ms
Open subagent session at depth 6 3/3 3/3 range 322.3 ms range 910.5 ms
CPU, open subagent at depth 6 3/3 3/3 range 300.0 ms range 210.0 ms
Open chain parent from agents view 3/3 3/3 range 46.2 ms range 100.4 ms
CPU, open chain parent 3/3 3/3 range 30.0 ms range 310.0 ms
Scheduled catalog, first request 3/3 3/3 range 49.8 ms range 353.3 ms
CPU, scheduled catalog 3/3 3/3 range 320.0 ms range 900.0 ms
Scheduled catalog, repeated request 3/3 3/3 range 2.9 ms range 3.7 ms
CPU, repeated catalog 3/3 3/3 range 10.0 ms range 10.0 ms
Cold worker with three catalog scans 3/3 3/3 range 60.3 ms range 191.6 ms
CPU, cold worker and scans 3/3 3/3 range 190.0 ms range 260.0 ms
UI memory after interactions 3/3 3/3 range 24.20 MB range 137.89 MB

@sethkarten
sethkarten enabled auto-merge (squash) September 18, 2026 06:12
Comment thread packages/coding-agent/src/cli/tailscale.ts Fixed
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
@sethkarten sethkarten changed the title [RSI] First-class Tailscale support (status, serve/funnel, doctor, docs) First-class Tailscale support (status, serve/funnel, doctor, docs) Sep 18, 2026
@sethkarten
sethkarten force-pushed the rsi/tailscale-support branch 2 times, most recently from 9d87c71 to 99b8beb Compare September 18, 2026 06:43
Comment thread packages/coding-agent/src/cli/public-command.ts Outdated
Comment thread packages/coding-agent/src/cli/tailscale.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/cli/tailscale.ts
@sethkarten
sethkarten force-pushed the rsi/tailscale-support branch from 99b8beb to 75cac35 Compare September 18, 2026 07:42
Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/docs/tailscale.md Outdated
Comment thread packages/coding-agent/docs/tailscale.md
Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
@sethkarten
sethkarten force-pushed the rsi/tailscale-support branch from 75cac35 to 147f6ad Compare September 18, 2026 08:41
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
Comment thread packages/coding-agent/.changes/tailscale-support.md Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts Outdated
@sethkarten
sethkarten force-pushed the rsi/tailscale-support branch from 147f6ad to 5170bc3 Compare September 18, 2026 09:12
Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5170bc3. Configure here.

Comment thread packages/coding-agent/src/cli/tailscale.ts
Comment thread packages/coding-agent/src/cli/tailscale.ts
…funnel, doctor, docs)

prime-agent tailscale: tailnet and MagicDNS detection with teaching
errors, serve --port [--funnel] wrapping tailscale serve/funnel for any
local bridge, a Tailscale section in doctor, and docs for the three
patterns (Tailscale SSH remote control, tailnet exposure, MagicDNS
reach from cloud containers + the official Tailscale MCP connector).
No new dependencies: shells out to the tailscale CLI detected at runtime.
@sethkarten
sethkarten force-pushed the rsi/tailscale-support branch from 5170bc3 to 295255b Compare September 18, 2026 09:44
@sethkarten

Copy link
Copy Markdown
Contributor Author

CORRECTION (process incident, transparency): replies at 147f6ad and 5170bc3 claimed some fixes that were lost from the tree before those commits were pushed. Audit of the pushed commits shows these claims were NOT in those commits but ARE now verifiably in 295255b (grep-verified in the pushed head):

  • ENOENT vs other-spawn-error taxonomy in probeTailscale (claimed at 147f6ad)
  • error-before-missing-CLI ordering in status and serve (claimed at 147f6ad)
  • single-probe --json (payload and exit code from the same probe; claimed at 147f6ad)
  • spawnSyncHidden at all spawn sites (claimed at 147f6ad)
  • killSignal: "SIGKILL" (claimed at 5170bc3; separate correction posted on that thread)
  • tailscaleStatusJson unexport (claimed at 5170bc3)

Root cause: a multi-step edit round where one edit step mutated the file in memory but was not persisted before an inspection step, and a later step re-read the file and wrote a tree missing the unpersisted change. Fixes that WERE correctly present at those commits: funnel AllowFunnel check, docs (userspace networking, Tailscale SSH prerequisites), changeset wording, host-suffix comment. All four of today's new findings trace exactly to the lost edits and are fixed at 295255b with tests green and the full check chain green. My process now greps the COMMIT content before posting any "fixed at " reply.

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.

2 participants