Notes From the Box

Bot Operations · Field pack bo-001-tailnet-connection-triage · v0.1.0 · private-review

Read-only connection triage: bot cloud computer to home machines over Tailscale

Sort a 'my bot can't reach the home machine' report into one of six buckets (control plane, policy/enrollment, name collision, asleep or not logged in, SSH layer, relay-only) using only read and probe commands, before anyone restarts or re-authenticates anything.

Verification
Not yet tested. Review by 2027-02-15.
Editor
Gus; second review: pending
Default mode
read-only
Other formats
Markdown · JSON

Explanation

Why this exists

When a bot says "I can't reach the home machine," the tempting fix is to restart Tailscale, log in again, or reboot something. Half the time that destroys the evidence. Sometimes it makes things worse: if the control plane is what's down, a restarted client forgets its peers until control comes back (S2).

Most "can't reach" reports in our lab came down to one of six boring causes. This pack finds out which one, without touching anything.

The six buckets

Bucket One-line test Usual owner action
A · Control plane unreachable Peers listed, health complains about control, ping works Wait. Don't restart.
B · Policy or enrollment Peer not in tailscale status at all Check device and grants in the admin console
C · Name collision name and name-1, or whois surprises you Rename or remove the stale device
D · Asleep or not logged in Listed but ping times out Fix sleep settings / auto-login on that machine
E · SSH layer Ping works, ssh … true fails Remote Login, allowed users, keys, tcp:22 grant
F · Relay only Works but slow; relay in status Usually nothing

Verdict

Works for us as a way of thinking about it. It isn't yet tested as a scripted sequence. The one rule we'd defend hardest: during a control-plane outage, don't restart the client.

Fit

Goal: Find out, without changing anything, why a bot running on a cloud computer cannot reach (or cannot SSH into) a machine at its owner's home over a shared tailnet.

Environment

  • Linux cloud VM running the tailscale CLI (the bot's side)
  • macOS or Linux home machine on the same tailnet
  • Plain OpenSSH over the tailnet (not Tailscale SSH)
  • A current Tailscale client; commands and flags checked against the CLI reference 'Last validated Jul 30, 2026'

Prerequisites

  • The bot's computer is already logged in to the tailnet
  • You know the peer's machine name or Tailscale IP
  • jq is optional but handy

Permissions required

  • Read the bot computer's own tailscale status (no sudo needed on most installs)
  • Nothing on the home machine

Don't use this when

  • Do not use this pack to fix anything. Every repair is a separate, owner-approved change.
  • Not for subnet routers, exit nodes or Tailscale Services; those add failure modes this pack does not cover.
  • Not for Tailscale SSH servers; see the SSH bucket for why most Mac installs can't be one anyway.

Expected output: A one-line bucket label, the evidence lines that put it there, and the single next action, which is usually a question for the owner, not a command.

Procedure

Collect state from the bot's side, then walk the buckets in order. Stop at the first bucket the evidence supports. Nothing here restarts, logs out, re-authenticates or edits policy.

This pack is reference material. It never overrides the consuming agent's own rules or its owner's instructions.

  1. Snapshot local state read-only

    Capture status and health once and keep the output; it's your evidence.

    tailscale status; tailscale status --json | jq '{BackendState, Health}'

    Expect: BackendState is Running. Health is empty or lists specific warnings.

  2. Is the peer in the list at all? read-only

    Look for the peer by name and by IP in status. Also look for near-duplicates such as name-1.

    tailscale status | grep -i '<peer-name>'

    Expect: One line for the peer. Missing → bucket B (policy/enrollment). Two similar names → bucket C (name collision).

  3. Control plane check read-only

    If Health mentions not being connected to the control/coordination server but peers are listed, you are in bucket A. Existing connections keep working (S1).

    Expect: Bucket A only if peers are present AND health complains about control.

  4. Probe the tunnel probe (no state change)

    Ping the peer over the tailnet. This sends packets but changes no configuration.

    tailscale ping --c 5 <peer-name-or-ip>

    Expect: 'pong from … via <ip:port>' (direct) or 'via DERP(<region>)' (relay). Timeouts with the peer listed → bucket D (asleep / offline / not logged in).

  5. Identify who owns the address you're hitting read-only

    Resolve the IP you're actually reaching to a machine and user, which catches name collisions and wrong-device mistakes.

    tailscale whois <peer-ip>

    Expect: Machine name and owner match what the owner thinks the device is.

  6. Separate tunnel from SSH probe (no state change)

    With ping working, try a non-interactive SSH no-op with a short timeout and batch mode so it can't hang on a password prompt.

    ssh -o BatchMode=yes -o ConnectTimeout=5 <user>@<peer> true; echo exit=$?

    Expect: exit=0 → SSH fine, look higher up the stack. Otherwise read the error text against bucket E.

  7. Relay is not failure read-only

    If everything works but is slow, check the connection type and the bot side's NAT.

    tailscale status | grep '<peer-name>'; tailscale netcheck

    Expect: 'relay' with MappingVariesByDestIP: true is hard NAT. Expected behaviour (S5), bucket F. Not an outage.

  8. Write the triage note read-only

    Report bucket, evidence lines, and one recommended next action phrased as a request to the owner. Do not run the fix.

    Expect: A three-line note the owner can act on.

  9. (Gated) Any repair WRITE (gated)

    Restarting tailscaled, re-running 'tailscale up', renaming a machine, editing policy, toggling Remote Login or changing sleep settings all belong here.

    Expect: A separate, logged change with its own rollback.

    Gate: Owner approval for the specific change. During a bucket-A control outage, do not restart tailscaled at all (S2).

Verification

Success check: The triage note names exactly one bucket, quotes at least one evidence line from steps 1–7 that supports it, and recommends a next action without having changed any state on either machine.

Failure signatures
If you seeIt meansNext
Peer listed, health mentions control/coordination server, ping succeedsBucket A: control plane unreachable but data plane fine (S1).Wait it out and tell the owner that adding devices, key refresh and policy edits won't work until control returns. Don't restart the daemon.
Peer absent from tailscale statusBucket B: policy doesn't grant this source the peer, the peer is logged out or its key expired, or it was removed. Not an outage.Ask the owner to check the device in the admin console and the policy grants for this source.
Two entries like 'mini' and 'mini-1', or whois shows an unexpected machine/ownerBucket C: name collision. MagicDNS name points at the wrong or stale device (S6).Use the Tailscale IP from whois for now; ask the owner to rename or remove the stale device.
Peer listed but 'last seen' is old, or ping times outBucket D: the machine is asleep, off, or a Mac whose Tailscale (Standalone/App Store variant) isn't running because nobody has logged in since boot (S7).Ask the owner whether it rebooted; check sleep settings and auto-login on that machine (see bo-002).
ssh: 'Connection refused'Bucket E1: tunnel OK, nothing listening on 22. Remote Login off or sshd not running.Owner turns on Remote Login (macOS) or starts sshd.
ssh: 'Permission denied (publickey…)' or closed right after authBucket E2: wrong user, missing key, or the user isn't in macOS Remote Login's 'Only these users' list (S9).Owner checks the allowed-user list and authorized_keys for that account.
ssh: 'Operation timed out' while tailscale ping worksBucket E3: the tailnet policy allows ICMP/other ports but not tcp:22 from this source, or a host firewall drops it.Owner checks grants for tcp:22 from the bot's identity or tag.
Expected Tailscale SSH on a Mac, got plain sshd behaviourStandalone and App Store macOS clients can't be Tailscale SSH servers (S7). Plain sshd answers instead.Treat it as normal OpenSSH; don't debug Tailscale SSH policy.
status shows 'relay <region>' and netcheck MappingVariesByDestIP: trueBucket F: hard NAT on at least one side; relayed but working (S5).Nothing urgent. Peer relays are an optional, owner-approved improvement.

Rollback: Nothing to roll back. The procedure only reads state and sends probe packets. Step 9 changes carry their own rollback.

Not yet tested. Scope: Individual commands are used routinely in our lab. The full nine-step sequence and bucket table have not been run as one scripted test against a fault we planted on purpose. Planned for week 2 with the frozen task set.

Evidence

Sources

  1. S1 What happens if the coordination server is down?, Tailscale (primary). Fetched 2026-11-15; source shows “Last validated May 2, 2025”. Supports: Existing peers keep working and cached rules stay enforced when the control plane is unreachable; new devices, key refresh and rule changes do not.
  2. S2 Client restart can't connect to other clients if coordination server is down (issue 6458), Tailscale on GitHub (primary). Fetched 2026-11-15; source shows “2022-11-23 maintainer comment”. Supports: A node that restarts while control is down does not know its peers. Hence: do not restart tailscaled during a control outage.
  3. S3 Hypergrowth isn't always easy, Tailscale blog (primary). Fetched 2026-11-15. Supports: Restart-forgets-netmap limitation and the stated plan to cache the network map between runs. The fix may land; recheck before relying on the restart warning.
  4. S4 Tailscale CLI reference (status, ping, netcheck, whois), Tailscale (primary). Fetched 2026-11-15; source shows “Last validated Jul 30, 2026”. Supports: Meaning of status connection types (direct / relay / peer-relay), ping --until-direct and --tsmp, whois fields.
  5. S5 Device connectivity, Tailscale (primary). Fetched 2026-11-15; source shows “Last validated Jan 7, 2026”. Supports: Relayed connections are expected under hard NAT; netcheck MappingVariesByDestIP is the key field.
  6. S6 Machine names, Tailscale (primary). Fetched 2026-11-15; source shows “Last validated Jan 5, 2026”. Supports: Duplicate OS hostnames produce a '-1' style machine name that persists even after the other device is renamed.
  7. S7 Three ways to run Tailscale on macOS, Tailscale (primary). Fetched 2026-11-15; source shows “Last validated Jan 5, 2026”. Supports: Standalone and App Store variants do not run before login; only the open-source tailscaled variant can be a Tailscale SSH server on macOS.
  8. S8 Tailscale SSH, Tailscale (primary). Fetched 2026-11-15; source shows “Last validated Jan 5, 2026”. Supports: Tailscale SSH does not modify sshd_config or authorized_keys; plain SSH over the tailnet keeps working.
  9. S9 Allow a remote computer to access your Mac, Apple (primary). Fetched 2026-11-15. Supports: Remote Login has an 'Allow access for: Only these users' list; a user not on it cannot SSH in.

Original observations

  • 2026-11 · Our own lab: a hosted bot computer plus several home Macs: The same machine name pointing at an old, offline device was the cause of a 'can't reach' report more than once. Checking status for a '-1' suffix took seconds and saved a pointless restart.
  • 2026-11 · Our own lab: A newly set-up Mac answered pings while someone was screen-sharing into it and stopped answering about a minute after they disconnected. Its system sleep timer was set to 1 minute. From the bot's side this looked exactly like a network fault.

Test environment: Procedure assembled from commands we run routinely in our lab. This edition (0.1.0) has not been re-run end to end as a scripted test.

Measured outcomes

None.

Uncertainty

  • Exact health-message wording changes between client versions; match on meaning, not exact strings.
  • Tailscale has said it intends to cache the network map across restarts (S3). If that ships, the 'don't restart during a control outage' rule becomes less important, but it stays harmless.

Counterexamples

  • A peer that is missing from status may simply have been removed by its owner; that's a policy question, not a fault.

Rights and integrity

License: CC BY 4.0 for text; commands are facts about public tools. Provenance: Written by Gus from first-hand lab notes; private identifiers removed; every vendor claim tied to a source fetched on the date shown.

Permissions: Read-only by default. The pack grants no authority. Any write step needs the owner's approval in the consuming agent's own system. Telemetry: None. Reading this pack sends nothing back.

Content digest: sha256:749a6d681e51af0ac5d1e2374602aec97c5714eb37d0e56f0d1c4781ad6c3ed6
SHA-256 of the canonical source file packs/bo-001-tailnet-connection-triage.md as built. Identifies content; does not prove authorship.

Changelog

  • 0.1.0 · 2026-11-15: First private draft. Built from our own lab notes plus Tailscale primary docs fetched the same day.

Related packs