fix(bootstrap): converge the tailnet hostname on an already-joined box #7

Merged
dan-claude-bot merged 1 commit from fix/tailnet-hostname-converges into main 2026-07-12 17:00:32 +00:00
dan-claude-bot commented 2026-07-12 15:47:41 +00:00 (Migrated from github.com)

Follow-up to #6, found in the same session.

The gap

bootstrap skips tailscale up when the box has already joined — that is what lets a
re-run work without spending a pre-auth key (#6). But skipping it also skipped
--hostname, so the tailnet name never converged.

A box that joined under the wrong name stayed misnamed permanently, and re-running
rig — the documented repair path — could not fix it. rig is convergent by contract
("safe to re-run; a second run changes nothing"); this was the one field that wasn't.

Concretely: an operator ran rig bootstrap control-plane on the prod box without
--hostname. Roles are near-identical (control-plane and workload differ only in a
log line), so the run was harmless — except the box took the role as its name, and no
amount of re-running could rename it.

The fix

The already-joined path now compares the live tailnet hostname against --hostname and
reconciles it with tailscale set --hostname — no re-auth, no key. A failure warns
rather than dies: a cosmetic name is not worth failing a bootstrap over.

rig-bootstrap: tailnet already joined; skipping tailscale up (no pre-auth key needed)
rig-bootstrap: tailnet hostname is 'control-plane', want 'prod-box' — converging

Why renaming is safe here

  • It cannot void an ACL grant. Tailscale ACLs cannot bind a rule's dst to a
    hostname — it must be a tag, an IP, or a hosts alias. (That constraint is exactly why
    our acl.hujson pins coolify-box to an IP.)
  • It will not clobber a deliberate rename. A machine renamed in the admin console
    keeps that name and is no longer overridden by the device hostname.

bash -n + shellcheck clean. The tailscale status --peers=false self-line parse was
checked against real output from a live node.

🤖 Generated with Claude Code

Follow-up to #6, found in the same session. ## The gap `bootstrap` skips `tailscale up` when the box has already joined — that is what lets a re-run work **without spending a pre-auth key** (#6). But skipping it also skipped `--hostname`, so the **tailnet** name never converged. A box that joined under the wrong name stayed misnamed **permanently**, and re-running rig — the documented repair path — could not fix it. rig is convergent by contract (*"safe to re-run; a second run changes nothing"*); this was the one field that wasn't. Concretely: an operator ran `rig bootstrap control-plane` on the **prod** box without `--hostname`. Roles are near-identical (`control-plane` and `workload` differ only in a log line), so the run was harmless — except the box took the *role* as its name, and no amount of re-running could rename it. ## The fix The already-joined path now compares the live tailnet hostname against `--hostname` and reconciles it with `tailscale set --hostname` — no re-auth, no key. A failure `warn`s rather than `die`s: a cosmetic name is not worth failing a bootstrap over. ``` rig-bootstrap: tailnet already joined; skipping tailscale up (no pre-auth key needed) rig-bootstrap: tailnet hostname is 'control-plane', want 'prod-box' — converging ``` ## Why renaming is safe here - **It cannot void an ACL grant.** Tailscale ACLs cannot bind a rule's `dst` to a hostname — it must be a tag, an IP, or a `hosts` alias. (That constraint is exactly why our `acl.hujson` pins `coolify-box` to an IP.) - **It will not clobber a deliberate rename.** A machine renamed in the admin console keeps that name and is no longer overridden by the device hostname. `bash -n` + `shellcheck` clean. The `tailscale status --peers=false` self-line parse was checked against real output from a live node. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/rig#7
No description provided.