Add spokes to your fleet

A spoke is a machine that runs your coding agents. Your hub polls it over SSH across your Tailscale network and drives its tmux. Adding a spoke is four moves: install the tooling, join the tailnet, register the roster line, confirm reachability.

Prerequisites

Step 1 — Install the spoke tooling

On the new machine, from inside the codebase you want to add:

curl -fsSL https://dev-mux.com/install | bash

That one line is the whole install. It asks which role (answer spoke), which codebase, and which hub to register with, then installs the tooling, wires the agent hooks, and registers the spoke for you.

Run the same line again from inside a second codebase on the same machine to add that one too — it detects the existing install and adopts just the new codebase, leaving your setup alone.

If you would rather do the wiring by hand:

REPO_ROOT="$PWD" dev install-hooks
# edit .dev/session.conf, then:
dev up

The REPO_ROOT="$PWD" prefix matters: without it, dev install-hooks targets the machine's *default* repo, which for a second codebase is the wrong one.

See Install for platform specifics (macOS PATH, Raspberry Pi, cloud VM), and your hub's own Help → Guides panel for the step-by-step version of this page — including an agent runbook you can paste straight into Claude Code.

Step 2 — Join the spoke to your tailnet

The spoke must be on the same tailnet as your hub so the hub can reach it by name:

tailscale up --ssh

Now the hub can address the spoke by its Tailscale name. No inbound port is opened; the spoke is only reachable to your tailnet, and only your hub polls it.

This step cannot be skipped or automated away. Your hub identifies a spoke by asking Tailscale who is on the other end of the connection — there is no password and no API token, so tailnet membership *is* the credential. A machine that is not on your tailnet cannot register itself and cannot be reached by the hub.

Step 3 — Register the roster line

Your hub keeps a roster of spokes. A roster stanza names the spoke, its host, the transport, and the repo path:

vm web1
  host web1
  transport ssh
  repo /opt/web1
  wake web1

You rarely write this by hand. The installer in step 1 registers the spoke for you, and there are two direct commands if you need them:

# from the spoke — self-register, authenticated by your tailnet identity
dev register --hub http://your-hub:8787 --repo /opt/web1 --name web1

# or from the hub — works over SSH, no dependency on the spoke's own tooling
devf adopt web1 /opt/web1 --name web1

On a managed hub this is done for you through the cockpit when you add a spoke — you supply the name, host, and repo path, and the hub appends the stanza atomically. Registration is idempotent: adding a spoke that is already in the roster is a no-op, never a duplicate.

Step 4 — Confirm the hub can reach it

From your hub, a single fleet health poll tells you the state of every spoke:

devf health-fleet

For a single new spoke, the two targeted checks are quicker. On the spoke:

dev onboard-check --hub http://your-hub:8787

Every line should start OK; a FAIL line names its own remedy. And from the hub, to prove the hub can drive it over SSH:

devf spoke-preflight web1

Each spoke reports one of: reachable (polling cleanly), quiet (reachable, nothing running), asleep (a Spot VM you can wake), or unreachable (genuinely down). A new spoke should read reachable within a poll cycle. If it does not, see Troubleshooting.

Spot VMs: sleep and wake

A preemptible / Spot cloud VM can be asleep rather than dead — stopped to save money, ready to wake on demand. The roster's wake line names the box to your wake tool. From the cockpit or the CLI you can power an asleep spoke back on, and the dashboard distinguishes asleep (wakeable) from unreachable (down). If you run no Spot VMs, you need no wake tooling at all.

Special-case spokes

What durability means here

Once a spoke is in your fleet, the agent sessions you start run inside tmux on the hub side of the control plane and on the spoke itself. Your clients — laptop, phone, cockpit — use tmux-backed sessions with automatic reconnect, so disconnecting a client never interrupts the agents working on your spokes.

Next steps