feat(bootstrap): staging role — host archetype for box-minted staging VMs #23

Merged
dan-claude-bot merged 3 commits from feat/staging-role into main 2026-07-17 21:24:53 +00:00
dan-claude-bot commented 2026-07-17 15:50:41 +00:00 (Migrated from github.com)

staging bootstrap role — the host archetype for box-minted staging VMs

Adds a fourth bootstrap role, staging, alongside control-plane / workload / runner: the machine whose job is to host staging boxes — Incus VMs minted by the box CLI (companion: heavy-duty/box#68), each later converged from inside with rig bootstrap workload and registered in the control plane as its own server.

Role semantics:

  • Default hostname staging (convention: default = role name).
  • Tag policy on the effective tag: mint the key with tag:local; the role refuses tag:server in verify_effective_tag, exit 1, exactly where and how the runner refusal lives. A staging host is never managed by the control plane — its guest VMs are — and the fleet has already been bitten by a host wrongly carrying tag:server.
  • /dev/kvm absence is an advisory warning in the execution path (the role is rehearsed in containers), never a failure; arg validation stays pure and root-free.
  • Closing log points at the box CLI: install box, box setup-host to prepare Incus, then box new --template staging. rig deliberately installs no Incus and no box — box's setup-host is the single owner of Incus config; two tools converging one daemon is drift by construction.

The first commit is the implementation plan (docs/plans/2026-07-17-staging-role.md), per repo convention; implementation follows on this branch.

Note for review: issue #22 was written before #20 merged, so its acceptance criterion names the now-removed --ts-tag flag. The plan's addendum reconciles this — the tag:server refusal lands on the effective tag (the strictly stronger, post-#20 layer). While adding staging to the dispatcher's role list, the bin/rig bootstrap usage line also drops the stale [--ts-tag <tag>] / "defaults to tag:ci" text that #20 left behind.

Out of scope, deliberately: no dev role yet, no VM provisioning, no Coolify API.

Closes #22

🤖 Generated with Claude Code

## `staging` bootstrap role — the host archetype for box-minted staging VMs Adds a fourth bootstrap role, `staging`, alongside `control-plane` / `workload` / `runner`: the machine whose job is to **host** staging boxes — Incus VMs minted by the `box` CLI (companion: heavy-duty/box#68), each later converged from inside with `rig bootstrap workload` and registered in the control plane as its own server. Role semantics: - Default hostname `staging` (convention: default = role name). - Tag policy on the **effective** tag: mint the key with `tag:local`; the role **refuses `tag:server`** in `verify_effective_tag`, exit 1, exactly where and how the `runner` refusal lives. A staging *host* is never managed by the control plane — its guest VMs are — and the fleet has already been bitten by a host wrongly carrying `tag:server`. - `/dev/kvm` absence is an advisory warning in the execution path (the role is rehearsed in containers), never a failure; arg validation stays pure and root-free. - Closing log points at the box CLI: install box, `box setup-host` to prepare Incus, then `box new --template staging`. rig deliberately installs **no Incus and no box** — box's `setup-host` is the single owner of Incus config; two tools converging one daemon is drift by construction. **The first commit is the implementation plan** (`docs/plans/2026-07-17-staging-role.md`), per repo convention; implementation follows on this branch. Note for review: issue #22 was written before #20 merged, so its acceptance criterion names the now-removed `--ts-tag` flag. The plan's addendum reconciles this — the `tag:server` refusal lands on the effective tag (the strictly stronger, post-#20 layer). While adding `staging` to the dispatcher's role list, the `bin/rig` bootstrap usage line also drops the stale `[--ts-tag <tag>]` / "defaults to tag:ci" text that #20 left behind. Out of scope, deliberately: no `dev` role yet, no VM provisioning, no Coolify API. Closes #22 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dan-claude-bot commented 2026-07-17 15:52:22 +00:00 (Migrated from github.com)

Verification

Implementation is complete on this branch (plan → feat(bootstrap)docs). Results, all as non-root on Debian:

  • bash test/cli.sh74 passed, 0 failed, exit 0. New checks: staging parses and reaches the root check (exit 1, must run as root); staging --ts-tag tag:server dies at arg validation (exit 2, points at the key — validation still precedes the root check); the staging effective-tag refusal message is present in the shipped script (grep guard, same rationale as the runner-install repo-guard grep); unknown roles still exit 2.
  • shellcheck, exactly as ci.yml runs it (shopt -s globstar; files=(bin/* **/*.sh); shellcheck -x "${files[@]}") — clean, zero findings.
  • bash -n on commands/bootstrap.sh, bin/rig, test/cli.sh — clean.
  • Dispatcher smoke: rig --help lists the four roles; rig bootstrap staging (non-root, TS_AUTHKEY=x) → exit 1 must run as root; rig bootstrap staging --ts-tag tag:server → exit 2 with the removed-flag message.

Not exercised here (needs root + a real tailnet, per the repo's rehearsal convention): the full converge on a pristine box, the post-join tag:server refusal firing live, and the /dev/kvm warning on real hardware. The plan's Test Plan section spells out that rehearsal.

🤖 Generated with Claude Code

## Verification Implementation is complete on this branch (plan → `feat(bootstrap)` → `docs`). Results, all as non-root on Debian: - **`bash test/cli.sh`** — `74 passed, 0 failed`, exit 0. New checks: `staging` parses and reaches the root check (exit 1, `must run as root`); `staging --ts-tag tag:server` dies at arg validation (exit 2, points at the key — validation still precedes the root check); the staging effective-tag refusal message is present in the shipped script (grep guard, same rationale as the runner-install repo-guard grep); unknown roles still exit 2. - **shellcheck**, exactly as `ci.yml` runs it (`shopt -s globstar; files=(bin/* **/*.sh); shellcheck -x "${files[@]}"`) — clean, zero findings. - **`bash -n`** on `commands/bootstrap.sh`, `bin/rig`, `test/cli.sh` — clean. - **Dispatcher smoke:** `rig --help` lists the four roles; `rig bootstrap staging` (non-root, `TS_AUTHKEY=x`) → exit 1 `must run as root`; `rig bootstrap staging --ts-tag tag:server` → exit 2 with the removed-flag message. Not exercised here (needs root + a real tailnet, per the repo's rehearsal convention): the full converge on a pristine box, the post-join `tag:server` refusal firing live, and the `/dev/kvm` warning on real hardware. The plan's Test Plan section spells out that rehearsal. 🤖 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#23
No description provided.