Set up Tailscale (the connection wizard)
This is the question everyone asks before they buy: *how does your hub reach my machines without me opening my network to the world?* The answer is Tailscale — a private, encrypted, peer-to-peer network (a "tailnet") that only your devices can join. dev-mux rides on top of it. There are no inbound firewall holes, no public SSH port, and no VPN concentrator to run.
You do exactly one thing by hand: create an OAuth client scoped to the tag tag:devmux-hub in Tailscale Trust credentials, and connect it in the dev-mux onboarding wizard. The wizard mints a tagged auth key from that client, stores it encrypted, and destroys it the moment your hub is online. You never copy a tskey-auth- value.
This guide and the wizard show the same steps from one shared source.
Why Tailscale, and why it is safe
- Private by default. A tailnet is your own overlay network. Machines outside it cannot see or reach your hub or spokes. Nothing is exposed to the public internet.
- No inbound ports. Tailscale establishes connections outbound from each machine, so you never open port 22 (or anything else) to the internet. Your hub reaches a spoke because both are on *your* tailnet, not because the spoke is publicly reachable.
- Least privilege via tags. The auth key we mint is tagged
tag:devmux-hub. Tags are how Tailscale expresses "this node is a dev-mux hub, and here is exactly what it may talk to." The OAuth client authorizes precisely that role and nothing more. - We never hold your whole network. The OAuth client secret you paste is used once, in memory, to mint a single-use auth key. That key is stored encrypted (in Google Secret Manager on the managed side) and destroyed after join. We do not keep a standing credential to your Tailscale account.
Durability is not the network's job
A common misconception: people assume a special always-on tunnel is what keeps their agent sessions alive. It is not. Your sessions live on the hub inside tmux. dev-mux clients use tmux-backed sessions with automatic reconnect — so if your laptop sleeps, your Wi-Fi drops, or you roam from Wi-Fi to cellular, nothing is lost. You reconnect and land exactly where you left off, because the work was never running on your client.
Tailscale's job is *reach* (the hub can talk to your spokes); tmux on the hub is what provides *durability*. Keep those two ideas separate and the whole system makes sense.
Create your tailnet once
If you do not already have a Tailscale account, create one at tailscale.com and install Tailscale on the machines you want in your fleet. Signing in with your identity provider creates your tailnet automatically.
Connect your tailnet
- Define the hub tag. In the Tailscale admin console, open Access controls, select JSON editor, and add this block at the top level of the policy file. If the policy already has a
tagOwnerssection, add only the"tag:devmux-hub": []entry to it. Select Save.
Sources: JSON editor, tag owners."tagOwners": { "tag:devmux-hub": [], }, - Create a scoped OAuth client. Open Trust credentials. Select Credential, then OAuth. Add a description such as
dev-mux hub, then select Continue. Under OAuth scopes, select Keys → Auth Keys → Write. In Tags, addtag:devmux-hub. Select Generate credential.Sources: OAuth clients, trust-credential scopes. - Copy the client credentials. On the Credential created page, copy both the client ID and the client secret. Tailscale shows the secret only once. Paste them into the matching fields below or in your private onboarding link.Sources: OAuth clients.
- Copy your tailnet DNS name. Open DNS, copy the tailnet DNS name that ends in
.ts.net, and paste it into Your tailnet name.Sources: tailnet names.
Return to the dev-mux onboarding wizard and select Connect with Tailscale.
Behind the scenes, dev-mux exchanges the client for a short-lived API token, mints a single-use tag:devmux-hub auth key with Pre-approved set (so a tailnet with device approval enabled does not leave the hub sitting unapproved), stores that key in Secret Manager, and injects it via the hub's instance metadata. You never place a key in a config file, a repo, or a log. The hub uses the minted key exactly once — to join your tailnet — and then it is a normal, tagged node on your network. The OAuth client secret is not stored.
Confirm the hub joined
Back in the Tailscale admin console, your hub now appears in the machines list, tagged tag:devmux-hub and connected. In the dev-mux cockpit, your hub shows as online and your spokes become reachable.
*The hub has joined your tailnet as a tagged node. Note Key expiry: No expiry — applying tag:devmux-hub disables node-key expiry automatically, so the hub will not silently drop off your network months from now. dev-mux can now reach your spokes over the private network.*
> Node key expiry is already handled. Tagged nodes do not expire — applying tag:devmux-hub disables node-key expiry for you (visible as Key expiry: No expiry above), so there is nothing to turn off by hand. This is one of the reasons the tag is not optional: an untagged hub would inherit the default expiry and drop off your network months later, in the middle of a working session.
Auth-key fallback
If you cannot create an OAuth client (some tailnet roles cannot), the wizard still accepts a tagged auth key. This is the original D-6 path; prefer OAuth when you can.
Auth-key fallback. Open Keys and select Generate auth key. Use these settings:
- Reusable: off. The key connects one hub and is deleted after the hub joins.
- Ephemeral: off. The hub is a persistent server.
- Pre-approved: on if Tailscale shows the option. Tailscale shows it only when device approval is enabled.
- Tags: on, with
tag:devmux-hub.
Source: Tailscale auth keys.
Copy the key
Tailscale shows the generated key once. Copy it now. It looks like tskey-auth-.... Treat it like a password: it authorizes a node to join your network.
Paste it into the wizard
Open the dev-mux onboarding wizard, expand Advanced: paste a tskey-auth- key instead, and paste the tskey-auth-... value.
dev-mux stores the key encrypted and injects it into your hub at provisioning time.
What you did, in one sentence
You created a tag:devmux-hub OAuth client, connected it in the wizard, and your managed hub joined your private tailnet — giving the hub reach to your spokes with no inbound ports, while tmux on the hub (not the network) keeps your sessions durable through tmux-backed sessions with automatic reconnect.
Next steps
- Add spokes now that your hub is on your tailnet.
- Billing — note that a commercial Team tailnet is a paid Tailscale plan; see the pricing note there.
- Troubleshooting if a spoke shows
tailnet policy does not permit...or does not appear.