refactor(templates): the tenant seeds carry rig's -box family suffix #125
16 changed files with 194 additions and 95 deletions
54
CHANGELOG.md
54
CHANGELOG.md
|
|
@ -5,6 +5,60 @@ which records not just what changed but what each drill run proved.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **The tenant templates carry rig's family suffix: `claude` → `claude-box`,
|
||||||
|
`codex` → `codex-box`, `grok` → `grok-box`, `staging` → `staging-box`**
|
||||||
|
(#123, following heavy-duty/rig#76) — rig is growing a second family of
|
||||||
|
roles, and once a `staging` role can mean either a fleet machine or a box
|
||||||
|
tenant, the bare name stops naming anything. rig's answer is a suffix on
|
||||||
|
the role itself — `-server` for fleet machines, `-box` for box tenants —
|
||||||
|
and box's answer is that a template keeps being named for the role it
|
||||||
|
converges. Templates are the only surface that spells a rig role out loud
|
||||||
|
(`BOX_BOOTSTRAP_ROLE`, auto-run at mint since #81), so a template whose
|
||||||
|
directory says one thing and whose role key says another is a trap with a
|
||||||
|
15-minute fuse: it mints clean and dies at convergence. `blank` keeps its
|
||||||
|
name — it seeds no tenant role, sets no `BOX_BOOTSTRAP_ROLE`, and
|
||||||
|
therefore has nothing to agree with. Two namespaces move apart here and
|
||||||
|
only one of them moved: the template name and the role are now
|
||||||
|
`claude-box`, while the seed USER stays `claude`, because that is the user
|
||||||
|
the rig role converges and the one `box shell` lands in. `test/cli.sh`
|
||||||
|
pins the pair per tenant rather than each half alone — a future rename
|
||||||
|
that moves one and forgets the other mints a box whose role dies looking
|
||||||
|
for a user nobody created.
|
||||||
|
|
||||||
|
**This lands after rig's rename, not before, and the ordering is not a
|
||||||
|
preference.** The seeds install rig from `RIG_REPO`/`RIG_REF`, which
|
||||||
|
default to `heavy-duty/rig@main` and are unpinned until rig#32's releases
|
||||||
|
— so a box minted from these templates asks whatever `main` happens to be
|
||||||
|
for `rig bootstrap claude-box`. Against a pre-rename rig that role does
|
||||||
|
not exist, `cmd_new` refuses to call the box ready, and the operator is
|
||||||
|
handed a failed mint for a change neither repo has finished making. Merged
|
||||||
|
in the other order the window closes instead of opening: rig's roles are a
|
||||||
|
hard cut with no aliases, so the day rig's rename lands, every unmerged
|
||||||
|
box seed naming a bare role is the broken one.
|
||||||
|
|
||||||
|
One deliberate asymmetry: the mint-time hints in `cmd_new` match both the
|
||||||
|
new and the old spelling of `user.box.template`. That is not an alias for
|
||||||
|
the role — nothing here softens the cut, and `rig bootstrap claude` is
|
||||||
|
gone. It reads a stamp left on an *instance* at its own mint time, which
|
||||||
|
every box minted before today carries forever and every clone carries
|
||||||
|
forward; refusing the old spelling would cut nothing over and only drop
|
||||||
|
the login hint on boxes that predate the rename, the same reason
|
||||||
|
`user.claudebox` is honored everywhere else. `migrate-host.sh` stamps
|
||||||
|
re-homed legacy boxes `claude-box`, the name the template has today, so a
|
||||||
|
re-homed box looks like a fresh mint rather than a fossil.
|
||||||
|
|
||||||
|
The **machine**-role half of rig's rename reaches box too, in one place:
|
||||||
|
the tailnet workload join box prints as a next step for a `staging-box`
|
||||||
|
guest is now `sudo rig bootstrap workload-server`. box never runs it — it
|
||||||
|
holds a pre-auth key, and that it stays operator-run is the absence
|
||||||
|
keeping box creds-free end to end — but box does *print* it, in three
|
||||||
|
places that all had to move together (`cmd_new`'s hint, the
|
||||||
|
`staging-box` seed's own comment, and the README). A next step an
|
||||||
|
operator copy-pastes is as wrong as a role box executes, and it fails
|
||||||
|
later and further from the cause.
|
||||||
|
|
||||||
## 0.8.0 — 2026-07-19
|
## 0.8.0 — 2026-07-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
40
README.md
40
README.md
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
**Headless, trust-less, throwaway dev VMs.** One command mints a fresh,
|
**Headless, trust-less, throwaway dev VMs.** One command mints a fresh,
|
||||||
network-isolated Incus box from a **template**; the coding-agent templates
|
network-isolated Incus box from a **template**; the coding-agent templates
|
||||||
hand you a CLI agent on Debian 13 — `claude` (Claude Code), `codex` (OpenAI
|
hand you a CLI agent on Debian 13 — `claude-box` (Claude Code), `codex-box`
|
||||||
Codex), `grok` (xAI Grok) — **box mints, [rig](https://github.com/heavy-duty/rig)
|
(OpenAI Codex), `grok-box` (xAI Grok) — **box mints, [rig](https://github.com/heavy-duty/rig)
|
||||||
converges**: the template is a thin seed, and the agent tooling lands via a
|
converges**: the template is a thin seed, and the agent tooling lands via a
|
||||||
creds-free `rig bootstrap` role auto-run at mint
|
creds-free `rig bootstrap` role auto-run at mint
|
||||||
([#81](https://github.com/heavy-duty/box/issues/81)). The box is the product
|
([#81](https://github.com/heavy-duty/box/issues/81)). The box is the product
|
||||||
|
|
@ -217,13 +217,14 @@ VM boundary itself is proven on real hardware, like the rest of the drill).
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
box new --name work --template claude # a creds-free coding-agent box (~10 min cold)
|
box new --name work --template claude-box # a creds-free coding-agent box (~10 min cold)
|
||||||
box shell work # enter as the template's user
|
box shell work # enter as the template's user
|
||||||
```
|
```
|
||||||
|
|
||||||
Pick whichever coding-agent template you like — `claude`, `codex`, `grok` — or
|
Pick whichever coding-agent template you like — `claude-box`, `codex-box`,
|
||||||
`blank` for none. Inside the box, authenticate as needed. The `claude` template
|
`grok-box` — or `blank` for none. Inside the box, authenticate as needed. The
|
||||||
looks like this; the others follow the same shape with their own login step:
|
`claude-box` template looks like this; the others follow the same shape with
|
||||||
|
their own login step:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
claude # then run /login — copy the URL (press c), open it
|
claude # then run /login — copy the URL (press c), open it
|
||||||
|
|
@ -239,12 +240,12 @@ No coding agent is special — each is one template among several, and adding
|
||||||
another is just another directory. What ships today:
|
another is just another directory. What ships today:
|
||||||
|
|
||||||
| Template | What it becomes |
|
| Template | What it becomes |
|
||||||
| --------- | ---------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------ |
|
||||||
| `blank` | Bare Debian 13 — same isolation, no tooling. The default. |
|
| `blank` | Bare Debian 13 — same isolation, no tooling. The default. |
|
||||||
| `claude` | Claude Code, creds-free — where this project started |
|
| `claude-box` | Claude Code, creds-free — where this project started |
|
||||||
| `codex` | OpenAI Codex CLI, creds-free |
|
| `codex-box` | OpenAI Codex CLI, creds-free |
|
||||||
| `grok` | xAI Grok CLI, creds-free |
|
| `grok-box` | xAI Grok CLI, creds-free |
|
||||||
| `staging` | Server-class: docker + sshd hardening via rig; VM-only, autostarts |
|
| `staging-box` | Server-class: docker + sshd hardening via rig; VM-only, autostarts |
|
||||||
|
|
||||||
**Templates are thin seeds; rig does the becoming**
|
**Templates are thin seeds; rig does the becoming**
|
||||||
([#81](https://github.com/heavy-duty/box/issues/81)). A template is a
|
([#81](https://github.com/heavy-duty/box/issues/81)). A template is a
|
||||||
|
|
@ -255,8 +256,11 @@ pin tokens below). The seed is deliberately small — the tenant user, tmux,
|
||||||
and [rig](https://github.com/heavy-duty/rig) preinstalled, nothing that
|
and [rig](https://github.com/heavy-duty/rig) preinstalled, nothing that
|
||||||
joins a tailnet or admits credentials — and after cloud-init settles, box
|
joins a tailnet or admits credentials — and after cloud-init settles, box
|
||||||
auto-runs the template's **creds-free** tenant role inside the guest
|
auto-runs the template's **creds-free** tenant role inside the guest
|
||||||
(`rig bootstrap claude` / `codex` / `grok` / `staging`,
|
(`rig bootstrap claude-box` / `codex-box` / `grok-box` / `staging-box`,
|
||||||
[rig#31](https://github.com/heavy-duty/rig/issues/31)). The agent CLI,
|
[rig#31](https://github.com/heavy-duty/rig/issues/31); the roles carry a
|
||||||
|
family suffix — `-box` for box tenants, `-server` for fleet machines — and a
|
||||||
|
template is named for the role it converges,
|
||||||
|
[rig#76](https://github.com/heavy-duty/rig/issues/76)). The agent CLI,
|
||||||
docker, the server posture and the agent-context file all come from that
|
docker, the server posture and the agent-context file all come from that
|
||||||
role — convergent and idempotent, so the same command re-run later converges
|
role — convergent and idempotent, so the same command re-run later converges
|
||||||
an *existing* box to a newer spec (`box shell <box>` →
|
an *existing* box to a newer spec (`box shell <box>` →
|
||||||
|
|
@ -265,9 +269,9 @@ an *existing* box to a newer spec (`box shell <box>` →
|
||||||
`box setup-host`, `box teardown-host` or the drill *inside* a box — once,
|
`box setup-host`, `box teardown-host` or the drill *inside* a box — once,
|
||||||
from rig's roles, instead of copy-pasted per template.
|
from rig's roles, instead of copy-pasted per template.
|
||||||
|
|
||||||
**Anything that joins or admits stays operator-run.** The `staging` box's
|
**Anything that joins or admits stays operator-run.** The `staging-box`
|
||||||
tailnet workload join holds a pre-auth key, so box only prints it as the
|
tenant's tailnet workload join holds a pre-auth key, so box only prints it as the
|
||||||
next step — `box shell <name>`, then `sudo rig bootstrap workload` — and
|
next step — `box shell <name>`, then `sudo rig bootstrap workload-server` — and
|
||||||
never sees the key ([#69](https://github.com/heavy-duty/box/issues/69)'s
|
never sees the key ([#69](https://github.com/heavy-duty/box/issues/69)'s
|
||||||
split, kept).
|
split, kept).
|
||||||
|
|
||||||
|
|
@ -279,9 +283,9 @@ carries `@RIG_REPO@`/`@RIG_REF@` tokens that box resolves at mint from the
|
||||||
environment:
|
environment:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
box new --name work --template claude # heavy-duty/rig @ main
|
box new --name work --template claude-box # heavy-duty/rig @ main
|
||||||
RIG_REPO=you/rig RIG_REF=my-branch \
|
RIG_REPO=you/rig RIG_REF=my-branch \
|
||||||
box new --name trial --template claude # a rig branch under review
|
box new --name trial --template claude-box # a rig branch under review
|
||||||
```
|
```
|
||||||
|
|
||||||
Both directions of that edge track `main` unpinned today — said honestly,
|
Both directions of that edge track `main` unpinned today — said honestly,
|
||||||
|
|
|
||||||
35
bin/box
35
bin/box
|
|
@ -185,7 +185,7 @@ OPTIONS
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
# mint a claude box and log in inside it — the tool never handles your token
|
# mint a claude box and log in inside it — the tool never handles your token
|
||||||
box new --name work --template claude
|
box new --name work --template claude-box
|
||||||
box shell work # then: run 'claude', then /login
|
box shell work # then: run 'claude', then /login
|
||||||
|
|
||||||
# log in once, reuse forever: checkpoint the authed box, clone from it
|
# log in once, reuse forever: checkpoint the authed box, clone from it
|
||||||
|
|
@ -242,13 +242,13 @@ help_cmd() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
new) cat <<'EOF'
|
new) cat <<'EOF'
|
||||||
Mint a box. Without --from, launches a fresh box from a template (default:
|
Mint a box. Without --from, launches a fresh box from a template (default:
|
||||||
blank — bare Debian 13, nobody home; --template claude gets Claude Code
|
blank — bare Debian 13, nobody home; --template claude-box gets Claude Code
|
||||||
installed, creds-free, ~10 min cold). With --from, clones an existing box or
|
installed, creds-free, ~10 min cold). With --from, clones an existing box or
|
||||||
one of its snapshots — login state, git creds and clones carry over,
|
one of its snapshots — login state, git creds and clones carry over,
|
||||||
isolation is preserved, and the clone knows its template's user without
|
isolation is preserved, and the clone knows its template's user without
|
||||||
being told.
|
being told.
|
||||||
|
|
||||||
A tenant template (claude, codex, grok, staging) is a THIN seed — the user,
|
A tenant template (claude-box, codex-box, grok-box, staging-box) is a THIN seed — the user,
|
||||||
tmux, rig (#81) — and after cloud-init box auto-runs the creds-free tenant
|
tmux, rig (#81) — and after cloud-init box auto-runs the creds-free tenant
|
||||||
role inside it ('rig bootstrap <role>', rig#31): that role installs the
|
role inside it ('rig bootstrap <role>', rig#31): that role installs the
|
||||||
agent CLI / server posture and the agent-context file. rig is preinstalled
|
agent CLI / server posture and the agent-context file. rig is preinstalled
|
||||||
|
|
@ -286,8 +286,8 @@ BOX_LAUNCH_TIMEOUT=<seconds> (default 600) bounds the 'incus launch' call —
|
||||||
a launch that overruns it fails loudly instead of hanging forever (#93).
|
a launch that overruns it fails loudly instead of hanging forever (#93).
|
||||||
|
|
||||||
box new --name scratch # blank, the default
|
box new --name scratch # blank, the default
|
||||||
box new --name work --template claude
|
box new --name work --template claude-box
|
||||||
box new --name lean --template claude --cpu 2 --memory 3GiB
|
box new --name lean --template claude-box --cpu 2 --memory 3GiB
|
||||||
box new --name feature --from work/authed
|
box new --name feature --from work/authed
|
||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
|
|
@ -473,7 +473,7 @@ The in-box server must listen on 0.0.0.0:<port>, not only its own loopback —
|
||||||
a VM's forwarder connects to the box over the network. Inside an isolated box
|
a VM's forwarder connects to the box over the network. Inside an isolated box
|
||||||
that is safe: nothing but this proxy can reach the port.
|
that is safe: nothing but this proxy can reach the port.
|
||||||
|
|
||||||
box new --name web --template claude
|
box new --name web --template claude-box
|
||||||
box shell web # inside: run a dev server on 0.0.0.0:3000
|
box shell web # inside: run a dev server on 0.0.0.0:3000
|
||||||
box expose web 3000 # then open http://127.0.0.1:3000 in your browser
|
box expose web 3000 # then open http://127.0.0.1:3000 in your browser
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -1183,16 +1183,25 @@ cmd_new() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# The login hint belongs to the claude template — read the EFFECTIVE
|
# The login hint belongs to the claude-box template — read the EFFECTIVE
|
||||||
# template off the instance, so a clone of a claude box gets it too and a
|
# template off the instance, so a clone of a claude-box box gets it too and
|
||||||
# blank box is not told to run a binary it doesn't have.
|
# a blank box is not told to run a binary it doesn't have.
|
||||||
|
#
|
||||||
|
# Both spellings match, and that is not an alias for the ROLE. The role
|
||||||
|
# names are a hard cut (rig#76) — 'rig bootstrap claude' is gone, so the
|
||||||
|
# seeds ask for 'claude-box' and nothing here softens that. What these arms
|
||||||
|
# read is user.box.template, a stamp left on an INSTANCE at its own mint
|
||||||
|
# time: every box minted before the rename carries the bare name forever,
|
||||||
|
# and a clone carries it forward. Refusing the old spelling here would not
|
||||||
|
# cut anything over, it would only drop the hint on boxes that predate the
|
||||||
|
# rename — the same reason user.claudebox is honored everywhere else.
|
||||||
local eff; eff="$(incus config get "$instance" user.box.template 2>/dev/null || true)"
|
local eff; eff="$(incus config get "$instance" user.box.template 2>/dev/null || true)"
|
||||||
[ -z "$eff" ] && [ "$(incus config get "$instance" user.claudebox 2>/dev/null || true)" = 1 ] && eff=claude
|
[ -z "$eff" ] && [ "$(incus config get "$instance" user.claudebox 2>/dev/null || true)" = 1 ] && eff=claude-box
|
||||||
if [ "$eff" = claude ]; then
|
if [ "$eff" = claude-box ] || [ "$eff" = claude ]; then
|
||||||
echo "box: ready — 'box shell $name'. Log into Claude inside: run 'claude' then /login."
|
echo "box: ready — 'box shell $name'. Log into Claude inside: run 'claude' then /login."
|
||||||
elif [ "$eff" = staging ]; then
|
elif [ "$eff" = staging-box ] || [ "$eff" = staging ]; then
|
||||||
echo "box: ready — 'box shell $name'. The tailnet join stays operator-run (it holds a key box must never see):"
|
echo "box: ready — 'box shell $name'. The tailnet join stays operator-run (it holds a key box must never see):"
|
||||||
echo " box shell $name # then: sudo rig bootstrap workload --hostname $name"
|
echo " box shell $name # then: sudo rig bootstrap workload-server --hostname $name"
|
||||||
else
|
else
|
||||||
echo "box: ready — 'box shell $name'."
|
echo "box: ready — 'box shell $name'."
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# box design
|
# box design
|
||||||
|
|
||||||
`box` is a CLI that mints and manages **trust-less, network-isolated VMs
|
`box` is a CLI that mints and manages **trust-less, network-isolated VMs
|
||||||
with a coding agent installed** (`claude`, `codex`, `grok`, or `blank` for
|
with a coding agent installed** (`claude-box`, `codex-box`, `grok-box`, or
|
||||||
none). It is infrastructure, not a project provisioner.
|
`blank` for none). It is infrastructure, not a project provisioner.
|
||||||
|
|
||||||
See issue #3 for the full reframe and rationale. This doc captures the durable
|
See issue #3 for the full reframe and rationale. This doc captures the durable
|
||||||
design decisions.
|
design decisions.
|
||||||
|
|
@ -60,15 +60,17 @@ instead of pretending to sanitize it.
|
||||||
A template is a **thin, creds-free seed** — base image, the tenant user,
|
A template is a **thin, creds-free seed** — base image, the tenant user,
|
||||||
tmux, and [rig](https://github.com/heavy-duty/rig) preinstalled — and what
|
tmux, and [rig](https://github.com/heavy-duty/rig) preinstalled — and what
|
||||||
the box *becomes* lives in rig's bootstrap roles (rig#31): box auto-runs the
|
the box *becomes* lives in rig's bootstrap roles (rig#31): box auto-runs the
|
||||||
template's creds-free tenant role after cloud-init (`rig bootstrap claude` /
|
template's creds-free tenant role after cloud-init (`rig bootstrap claude-box`
|
||||||
`codex` / `grok` / `staging`), which installs the agent CLI or server
|
/ `codex-box` / `grok-box` / `staging-box` — the roles carry a family suffix,
|
||||||
posture. The split is deliberate: cloud-init is a first-boot one-shot —
|
`-box` for box tenants and `-server` for fleet machines, and the templates are
|
||||||
|
named for the roles they converge, rig#76), which installs the agent CLI or
|
||||||
|
server posture. The split is deliberate: cloud-init is a first-boot one-shot —
|
||||||
not convergent, not re-runnable, only parse-and-grep testable — while a rig
|
not convergent, not re-runnable, only parse-and-grep testable — while a rig
|
||||||
role is an idempotent script with effective-state asserts that can also
|
role is an idempotent script with effective-state asserts that can also
|
||||||
converge an *existing* box to a newer spec. Anything that joins a tailnet or
|
converge an *existing* box to a newer spec. Anything that joins a tailnet or
|
||||||
holds a key (staging's workload join) stays operator-run through
|
holds a key (the staging-box tenant's workload join) stays operator-run
|
||||||
`box shell`; box prints it as a next step and never sees the key. The seed's
|
through `box shell`; box prints it as a next step and never sees the key. The
|
||||||
rig install is pinned by `RIG_REPO`/`RIG_REF` at mint (default
|
seed's rig install is pinned by `RIG_REPO`/`RIG_REF` at mint (default
|
||||||
`heavy-duty/rig@main`, unpinned — the honest edge until rig#32's releases),
|
`heavy-duty/rig@main`, unpinned — the honest edge until rig#32's releases),
|
||||||
and box's template suite holds the line with fail-closed absence greps: no
|
and box's template suite holds the line with fail-closed absence greps: no
|
||||||
agent CLI, no docker, no tailscale, no context-file heredocs in any
|
agent CLI, no docker, no tailscale, no context-file heredocs in any
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
`box` mints trust-less, creds-free, isolated VMs with a coding agent already
|
`box` mints trust-less, creds-free, isolated VMs with a coding agent already
|
||||||
installed (`box new/shell/snapshot/restore/exec/down/start/rm/status`) — the
|
installed (`box new/shell/snapshot/restore/exec/down/start/rm/status`) — the
|
||||||
`claude`, `codex`, and `grok` templates each ship a CLI agent. The tool knows
|
`claude-box`, `codex-box`, and `grok-box` templates each ship a CLI agent. The
|
||||||
**nothing** about your project. There is no `install` step and no host-run
|
tool knows **nothing** about your project. There is no `install` step and no
|
||||||
setup script.
|
host-run setup script.
|
||||||
|
|
||||||
A project makes itself easy to stand up inside a box by shipping an optional
|
A project makes itself easy to stand up inside a box by shipping an optional
|
||||||
`.box/` folder. This folder is **agent-facing documentation** — read and
|
`.box/` folder. This folder is **agent-facing documentation** — read and
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,7 @@ if ! timeout -k 10 300 ~/.local/share/box/current/host/setup-host.sh; then
|
||||||
fi
|
fi
|
||||||
inf "host setup complete"
|
inf "host setup complete"
|
||||||
|
|
||||||
# A real server has room for the claude template's resources (8GiB/4cpu), and
|
# A real server has room for the claude-box template's resources (8GiB/4cpu), and
|
||||||
# drilling the real numbers is worth more than drilling shrunken ones. Only
|
# drilling the real numbers is worth more than drilling shrunken ones. Only
|
||||||
# shrink if we must. Since 0.4.0 resources are per-box, stamped from the
|
# shrink if we must. Since 0.4.0 resources are per-box, stamped from the
|
||||||
# template at mint — a profile edit no longer reaches them; the supported
|
# template at mint — a profile edit no longer reaches them; the supported
|
||||||
|
|
@ -386,9 +386,9 @@ inf "host setup complete"
|
||||||
ram="$(awk '/MemTotal/{print int($2/1024/1024)}' /proc/meminfo)"
|
ram="$(awk '/MemTotal/{print int($2/1024/1024)}' /proc/meminfo)"
|
||||||
if [ "$ram" -lt 20 ]; then
|
if [ "$ram" -lt 20 ]; then
|
||||||
export BOX_MEMORY=3GiB BOX_CPU=2
|
export BOX_MEMORY=3GiB BOX_CPU=2
|
||||||
note "host has ${ram}GiB RAM — minting at 3GiB/2cpu via BOX_MEMORY/BOX_CPU (the claude template's 8GiB/4cpu is what was NOT drilled)"
|
note "host has ${ram}GiB RAM — minting at 3GiB/2cpu via BOX_MEMORY/BOX_CPU (the claude-box template's 8GiB/4cpu is what was NOT drilled)"
|
||||||
else
|
else
|
||||||
inf "host has ${ram}GiB RAM — drilling the claude template's resources (8GiB/4cpu) unchanged"
|
inf "host has ${ram}GiB RAM — drilling the claude-box template's resources (8GiB/4cpu) unchanged"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
KVM=0; [ -e /dev/kvm ] && KVM=1
|
KVM=0; [ -e /dev/kvm ] && KVM=1
|
||||||
|
|
@ -492,10 +492,10 @@ fi
|
||||||
|
|
||||||
# --- templates: the mint surface is itself a surface to test ----------------
|
# --- templates: the mint surface is itself a surface to test ----------------
|
||||||
tpl_missing=""
|
tpl_missing=""
|
||||||
for t in blank claude codex grok; do
|
for t in blank claude-box codex-box grok-box; do
|
||||||
box templates 2>/dev/null | grep -q "^ $t" || tpl_missing="$tpl_missing $t"
|
box templates 2>/dev/null | grep -q "^ $t" || tpl_missing="$tpl_missing $t"
|
||||||
done
|
done
|
||||||
[ -z "$tpl_missing" ] && ok "templates: lists blank, claude, codex, grok" \
|
[ -z "$tpl_missing" ] && ok "templates: lists blank, claude-box, codex-box, grok-box" \
|
||||||
|| no "templates listing is missing:$tpl_missing"
|
|| no "templates listing is missing:$tpl_missing"
|
||||||
box new --name tpl --template nosuch 2>&1 | grep -q 'no such template' \
|
box new --name tpl --template nosuch 2>&1 | grep -q 'no such template' \
|
||||||
&& ok "unknown template refused, points at 'box templates'" || no "an unknown template was not refused"
|
&& ok "unknown template refused, points at 'box templates'" || no "an unknown template was not refused"
|
||||||
|
|
@ -551,19 +551,22 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# The generic mechanic (metadata, placement, user, isolation parity) is proven
|
# The generic mechanic (metadata, placement, user, isolation parity) is proven
|
||||||
# once by blank+claude and needs no per-template repeat. What a NEW template
|
# once by blank+claude-box and needs no per-template repeat. What a NEW template
|
||||||
# still has to prove is its own payload: the CLI installs, lands on the
|
# still has to prove is its own payload: the CLI installs, lands on the
|
||||||
# non-interactive exec PATH, and answers --version. One mint each.
|
# non-interactive exec PATH, and answers --version. One mint each.
|
||||||
|
# The box NAME stays the bare agent name — it is what the pre-flight banner
|
||||||
|
# announces and what teardown deletes — while the TEMPLATE carries rig#76's
|
||||||
|
# family suffix. They are two different namespaces and only one of them moved.
|
||||||
for t in codex grok; do
|
for t in codex grok; do
|
||||||
case "$t" in codex) bin=codex; user=codex ;; grok) bin=grok; user=grok ;; esac
|
case "$t" in codex) bin=codex; user=codex ;; grok) bin=grok; user=grok ;; esac
|
||||||
printf '\n minting a %s box (cold — validates the template install)…\n' "$t"
|
printf '\n minting a %s box (cold — validates the template install)…\n' "$t"
|
||||||
if mint_box "/tmp/mint-$t.log" --name "$t" --template "$t"; then
|
if mint_box "/tmp/mint-$t.log" --name "$t" --template "$t-box"; then
|
||||||
[ "$(incus config get "$t" user.box.user 2>/dev/null)" = "$user" ] \
|
[ "$(incus config get "$t" user.box.user 2>/dev/null)" = "$user" ] \
|
||||||
&& ok "$t: template user stamped ($user)" || no "$t: user.box.user not $user"
|
&& ok "$t: template user stamped ($user)" || no "$t: user.box.user not $user"
|
||||||
if timeout -k 5 30 box exec "$t" -- "$bin" --version </dev/null >/dev/null 2>&1; then
|
if timeout -k 5 30 box exec "$t" -- "$bin" --version </dev/null >/dev/null 2>&1; then
|
||||||
ok "$t: '$bin --version' answers via box exec — installed and on the non-interactive PATH"
|
ok "$t: '$bin --version' answers via box exec — installed and on the non-interactive PATH"
|
||||||
else
|
else
|
||||||
no "$t: '$bin --version' FAILED via exec — not installed, or not on exec's PATH (the claude template's #15 bug)"
|
no "$t: '$bin --version' FAILED via exec — not installed, or not on exec's PATH (the claude-box template's #15 bug)"
|
||||||
inf "PATH as exec sees it: $(timeout -k 5 20 box exec "$t" -- printenv PATH </dev/null 2>/dev/null)"
|
inf "PATH as exec sees it: $(timeout -k 5 20 box exec "$t" -- printenv PATH </dev/null 2>/dev/null)"
|
||||||
# Do not throw the evidence away — say WHAT the installer actually left.
|
# Do not throw the evidence away — say WHAT the installer actually left.
|
||||||
# Do NOT throw the evidence away — say what the installer actually left
|
# Do NOT throw the evidence away — say what the installer actually left
|
||||||
|
|
@ -583,10 +586,10 @@ for t in codex grok; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
printf '\n minting a claude box (cold, ~10 min)…\n'
|
printf '\n minting a claude-box box (cold, ~10 min)…\n'
|
||||||
t0=$SECONDS
|
t0=$SECONDS
|
||||||
if mint_box /tmp/mint-drill.log --name drill --template claude; then
|
if mint_box /tmp/mint-drill.log --name drill --template claude-box; then
|
||||||
ok "box new --name drill --template claude ($((SECONDS - t0))s)"
|
ok "box new --name drill --template claude-box ($((SECONDS - t0))s)"
|
||||||
else
|
else
|
||||||
no "box new FAILED — tail: $(tail -3 /tmp/mint-drill.log | tr '\n' ' ')"
|
no "box new FAILED — tail: $(tail -3 /tmp/mint-drill.log | tr '\n' ' ')"
|
||||||
timeout -k 5 60 incus delete -f drill >/dev/null 2>&1
|
timeout -k 5 60 incus delete -f drill >/dev/null 2>&1
|
||||||
|
|
@ -799,7 +802,7 @@ fi
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
phase "E. box expose — a deliberate loopback door (#55)"
|
phase "E. box expose — a deliberate loopback door (#55)"
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# archive is a running claude box (node is installed). Start a DETACHED
|
# archive is a running claude-box box (node is installed). Start a DETACHED
|
||||||
# listener on 0.0.0.0 inside it, expose the port, and prove the door works
|
# listener on 0.0.0.0 inside it, expose the port, and prove the door works
|
||||||
# from the HOST's loopback. Then prove removing it closes the door, and that a
|
# from the HOST's loopback. Then prove removing it closes the door, and that a
|
||||||
# NON-exposed port still obeys the ingress drop — the feature must not
|
# NON-exposed port still obeys the ingress drop — the feature must not
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,11 @@ legacy_boxes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Re-home one box. Legacy boxes are all claude boxes (the only template the old
|
# Re-home one box. Legacy boxes are all claude boxes (the only template the old
|
||||||
# tool minted), so the new metadata is the claude template's.
|
# tool minted), so the new metadata is the claude-box template's. The stamp
|
||||||
|
# names the template as it is called TODAY, not as it was called when the box
|
||||||
|
# was minted: it is what 'box shell' and the mint hints read, so a re-homed
|
||||||
|
# box should look like a fresh claude-box mint, not like a fossil (rig#76's
|
||||||
|
# family suffix — the template is named for the role it converges).
|
||||||
rehome_one() {
|
rehome_one() {
|
||||||
local b="$1" st
|
local b="$1" st
|
||||||
incus config get "$b" user.claudebox >/dev/null 2>&1 || { warn "$b is not a legacy box (no user.claudebox tag) — skipping"; return 1; }
|
incus config get "$b" user.claudebox >/dev/null 2>&1 || { warn "$b is not a legacy box (no user.claudebox tag) — skipping"; return 1; }
|
||||||
|
|
@ -75,7 +79,7 @@ rehome_one() {
|
||||||
|
|
||||||
# 1. TAG FIRST — additive and reversible. A box that stops here is still a
|
# 1. TAG FIRST — additive and reversible. A box that stops here is still a
|
||||||
# valid legacy box (the old tag is untouched) AND now a new one.
|
# valid legacy box (the old tag is untouched) AND now a new one.
|
||||||
incus config set "$b" user.box=1 user.box.template=claude user.box.user=claude \
|
incus config set "$b" user.box=1 user.box.template=claude-box user.box.user=claude \
|
||||||
|| { warn "$b: could not set new metadata — left untouched"; return 1; }
|
|| { warn "$b: could not set new metadata — left untouched"; return 1; }
|
||||||
|
|
||||||
# 2. Stop, reassign the profile (this is the network move), restart. Incus
|
# 2. Stop, reassign the profile (this is the network move), restart. Incus
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
# The claude template — a thin, creds-free seed (#81): Debian 13, the
|
# The claude-box template — a thin, creds-free seed (#81): Debian 13, the
|
||||||
# 'claude' user, tmux and rig. What the box BECOMES — the Claude Code CLI,
|
# 'claude' user, tmux and rig. What the box BECOMES — the Claude Code CLI,
|
||||||
# docker, node, the agent-context file with its #80 guard — is rig's job:
|
# docker, node, the agent-context file with its #80 guard — is rig's job:
|
||||||
# box auto-runs 'rig bootstrap claude' after mint (heavy-duty/rig#31).
|
# box auto-runs 'rig bootstrap claude-box' after mint (heavy-duty/rig#31).
|
||||||
|
# The template is named for the role it converges, suffix and all
|
||||||
|
# (heavy-duty/rig#76): rig's roles carry a family suffix — '-server' for
|
||||||
|
# fleet machines, '-box' for box tenants — and a seed that named the bare
|
||||||
|
# 'claude' would ask a post-rename rig for a role that no longer exists.
|
||||||
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
||||||
# key for a network or a security flag, on purpose — the shared box-net
|
# key for a network or a security flag, on purpose — the shared box-net
|
||||||
# profile is the placement contract and no template can weaken it.
|
# profile is the placement contract and no template can weaken it.
|
||||||
|
|
@ -14,4 +18,4 @@ BOX_USER="claude"
|
||||||
BOX_CPU="4"
|
BOX_CPU="4"
|
||||||
BOX_MEMORY="8GiB"
|
BOX_MEMORY="8GiB"
|
||||||
BOX_DISK="60GiB"
|
BOX_DISK="60GiB"
|
||||||
BOX_BOOTSTRAP_ROLE="claude"
|
BOX_BOOTSTRAP_ROLE="claude-box"
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
||||||
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
||||||
# docker, no context-file heredoc. Everything this box becomes comes from
|
# docker, no context-file heredoc. Everything this box becomes comes from
|
||||||
# 'rig bootstrap claude' (heavy-duty/rig#31), which box auto-runs after
|
# 'rig bootstrap claude-box' (heavy-duty/rig#31), which box auto-runs after
|
||||||
# mint; the agent-context file — including the #80 guard (never run
|
# mint; the agent-context file — including the #80 guard (never run
|
||||||
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
||||||
# once, instead of being copy-pasted per template.
|
# once, instead of being copy-pasted per template.
|
||||||
|
|
@ -21,7 +21,7 @@ packages:
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
runcmd:
|
runcmd:
|
||||||
# Preinstall rig so the box can converge — and re-converge — via
|
# Preinstall rig so the box can converge — and re-converge — via
|
||||||
# 'rig bootstrap claude'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
# 'rig bootstrap claude-box'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
||||||
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
||||||
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
||||||
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
# The codex template — a thin, creds-free seed (#81): Debian 13, the
|
# The codex-box template — a thin, creds-free seed (#81): Debian 13, the
|
||||||
# 'codex' user, tmux and rig. What the box BECOMES — the OpenAI Codex CLI,
|
# 'codex' user, tmux and rig. What the box BECOMES — the OpenAI Codex CLI,
|
||||||
# docker, node, the agent-context file with its #80 guard — is rig's job:
|
# docker, node, the agent-context file with its #80 guard — is rig's job:
|
||||||
# box auto-runs 'rig bootstrap codex' after mint (heavy-duty/rig#31).
|
# box auto-runs 'rig bootstrap codex-box' after mint (heavy-duty/rig#31).
|
||||||
|
# The template is named for the role it converges, suffix and all
|
||||||
|
# (heavy-duty/rig#76): rig's roles carry a family suffix — '-server' for
|
||||||
|
# fleet machines, '-box' for box tenants — and a seed that named the bare
|
||||||
|
# 'codex' would ask a post-rename rig for a role that no longer exists.
|
||||||
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
||||||
# key for a network or a security flag, on purpose — the shared box-net
|
# key for a network or a security flag, on purpose — the shared box-net
|
||||||
# profile is the placement contract and no template can weaken it.
|
# profile is the placement contract and no template can weaken it.
|
||||||
|
|
@ -14,4 +18,4 @@ BOX_USER="codex"
|
||||||
BOX_CPU="4"
|
BOX_CPU="4"
|
||||||
BOX_MEMORY="8GiB"
|
BOX_MEMORY="8GiB"
|
||||||
BOX_DISK="60GiB"
|
BOX_DISK="60GiB"
|
||||||
BOX_BOOTSTRAP_ROLE="codex"
|
BOX_BOOTSTRAP_ROLE="codex-box"
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
||||||
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
||||||
# docker, no context-file heredoc. Everything this box becomes comes from
|
# docker, no context-file heredoc. Everything this box becomes comes from
|
||||||
# 'rig bootstrap codex' (heavy-duty/rig#31), which box auto-runs after
|
# 'rig bootstrap codex-box' (heavy-duty/rig#31), which box auto-runs after
|
||||||
# mint; the agent-context file — including the #80 guard (never run
|
# mint; the agent-context file — including the #80 guard (never run
|
||||||
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
||||||
# once, instead of being copy-pasted per template.
|
# once, instead of being copy-pasted per template.
|
||||||
|
|
@ -21,7 +21,7 @@ packages:
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
runcmd:
|
runcmd:
|
||||||
# Preinstall rig so the box can converge — and re-converge — via
|
# Preinstall rig so the box can converge — and re-converge — via
|
||||||
# 'rig bootstrap codex'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
# 'rig bootstrap codex-box'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
||||||
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
||||||
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
||||||
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
# The grok template — a thin, creds-free seed (#81): Debian 13, the
|
# The grok-box template — a thin, creds-free seed (#81): Debian 13, the
|
||||||
# 'grok' user, tmux and rig. What the box BECOMES — the xAI Grok CLI,
|
# 'grok' user, tmux and rig. What the box BECOMES — the xAI Grok CLI,
|
||||||
# docker, the agent-context file with its #80 guard — is rig's job:
|
# docker, the agent-context file with its #80 guard — is rig's job:
|
||||||
# box auto-runs 'rig bootstrap grok' after mint (heavy-duty/rig#31).
|
# box auto-runs 'rig bootstrap grok-box' after mint (heavy-duty/rig#31).
|
||||||
|
# The template is named for the role it converges, suffix and all
|
||||||
|
# (heavy-duty/rig#76): rig's roles carry a family suffix — '-server' for
|
||||||
|
# fleet machines, '-box' for box tenants — and a seed that named the bare
|
||||||
|
# 'grok' would ask a post-rename rig for a role that no longer exists.
|
||||||
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
||||||
# key for a network or a security flag, on purpose — the shared box-net
|
# key for a network or a security flag, on purpose — the shared box-net
|
||||||
# profile is the placement contract and no template can weaken it.
|
# profile is the placement contract and no template can weaken it.
|
||||||
|
|
@ -14,4 +18,4 @@ BOX_USER="grok"
|
||||||
BOX_CPU="4"
|
BOX_CPU="4"
|
||||||
BOX_MEMORY="8GiB"
|
BOX_MEMORY="8GiB"
|
||||||
BOX_DISK="60GiB"
|
BOX_DISK="60GiB"
|
||||||
BOX_BOOTSTRAP_ROLE="grok"
|
BOX_BOOTSTRAP_ROLE="grok-box"
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
# A thin, creds-free seed (#81): the tenant user, tmux (#65), and rig —
|
||||||
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
# nothing that joins a tailnet or admits credentials, no agent CLI, no
|
||||||
# docker, no context-file heredoc. Everything this box becomes comes from
|
# docker, no context-file heredoc. Everything this box becomes comes from
|
||||||
# 'rig bootstrap grok' (heavy-duty/rig#31), which box auto-runs after
|
# 'rig bootstrap grok-box' (heavy-duty/rig#31), which box auto-runs after
|
||||||
# mint; the agent-context file — including the #80 guard (never run
|
# mint; the agent-context file — including the #80 guard (never run
|
||||||
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
# 'box setup-host' or the drill inside a box) — is rendered by that role,
|
||||||
# once, instead of being copy-pasted per template.
|
# once, instead of being copy-pasted per template.
|
||||||
|
|
@ -21,7 +21,7 @@ packages:
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
runcmd:
|
runcmd:
|
||||||
# Preinstall rig so the box can converge — and re-converge — via
|
# Preinstall rig so the box can converge — and re-converge — via
|
||||||
# 'rig bootstrap grok'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
# 'rig bootstrap grok-box'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
||||||
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
||||||
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
||||||
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
# The staging template — a thin, creds-free, server-class seed (#81, the
|
# The staging-box template — a thin, creds-free, server-class seed (#81, the
|
||||||
# re-cut of #69's layering): Debian 13, the 'ops' user, tmux and rig. The
|
# re-cut of #69's layering): Debian 13, the 'ops' user, tmux and rig. The
|
||||||
# server posture — docker, sshd hardening — is rig's job: box auto-runs
|
# server posture — docker, sshd hardening — is rig's job: box auto-runs
|
||||||
# 'rig bootstrap staging' after mint (heavy-duty/rig#31). The tailnet
|
# 'rig bootstrap staging-box' after mint (heavy-duty/rig#31). The template is
|
||||||
|
# named for the role it converges, suffix and all (heavy-duty/rig#76): rig's
|
||||||
|
# roles carry a family suffix — '-server' for fleet machines, '-box' for box
|
||||||
|
# tenants — and a seed that named the bare 'staging' would ask a post-rename
|
||||||
|
# rig for a role that no longer exists. Server-CLASS, not a fleet machine:
|
||||||
|
# this is still a box tenant, so it takes '-box', not '-server'. The tailnet
|
||||||
# workload join holds a key and therefore STAYS operator-run:
|
# workload join holds a key and therefore STAYS operator-run:
|
||||||
# box shell <name> # then: sudo rig bootstrap workload --hostname <name>
|
# box shell <name> # then: sudo rig bootstrap workload-server --hostname <name>
|
||||||
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
# KEY="value" only. Parsed against an allowlist, never sourced; there is no
|
||||||
# key for a network or a security flag, on purpose — the shared box-net
|
# key for a network or a security flag, on purpose — the shared box-net
|
||||||
# profile is the placement contract and no template can weaken it.
|
# profile is the placement contract and no template can weaken it.
|
||||||
|
|
@ -21,4 +26,4 @@ BOX_MEMORY="8GiB"
|
||||||
BOX_DISK="60GiB"
|
BOX_DISK="60GiB"
|
||||||
BOX_REQUIRE_VM="1"
|
BOX_REQUIRE_VM="1"
|
||||||
BOX_AUTOSTART="1"
|
BOX_AUTOSTART="1"
|
||||||
BOX_BOOTSTRAP_ROLE="staging"
|
BOX_BOOTSTRAP_ROLE="staging-box"
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
# A thin, creds-free, server-class seed (#81): the 'ops' user, tmux (#65),
|
# A thin, creds-free, server-class seed (#81): the 'ops' user, tmux (#65),
|
||||||
# and rig — nothing that joins a tailnet or admits credentials, no docker,
|
# and rig — nothing that joins a tailnet or admits credentials, no docker,
|
||||||
# no sshd config, no keys. The server posture comes from
|
# no sshd config, no keys. The server posture comes from
|
||||||
# 'rig bootstrap staging' (heavy-duty/rig#31), which box auto-runs after
|
# 'rig bootstrap staging-box' (heavy-duty/rig#31), which box auto-runs after
|
||||||
# mint; the tailnet workload join holds a pre-auth key and stays
|
# mint; the tailnet workload join holds a pre-auth key and stays
|
||||||
# operator-run ('box shell' → 'sudo rig bootstrap workload'), exactly as
|
# operator-run ('box shell' → 'sudo rig bootstrap workload-server'), exactly as
|
||||||
# #69 designed it — box never sees the key.
|
# #69 designed it — box never sees the key.
|
||||||
users:
|
users:
|
||||||
- name: ops
|
- name: ops
|
||||||
|
|
@ -22,7 +22,7 @@ packages:
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
runcmd:
|
runcmd:
|
||||||
# Preinstall rig so the box can converge — and re-converge — via
|
# Preinstall rig so the box can converge — and re-converge — via
|
||||||
# 'rig bootstrap staging'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
# 'rig bootstrap staging-box'. @RIG_REPO@/@RIG_REF@ are the pin point (#81):
|
||||||
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
# box substitutes them at mint from the RIG_REPO/RIG_REF environment
|
||||||
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
# (default heavy-duty/rig @ main — unpinned, tracking main, the same
|
||||||
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
# honest edge as rig's own unpinned box install, until rig#32 ships a
|
||||||
34
test/cli.sh
34
test/cli.sh
|
|
@ -310,16 +310,22 @@ done
|
||||||
# is its trust boundary (its guest runs docker, via rig) and a server returns
|
# is its trust boundary (its guest runs docker, via rig) and a server returns
|
||||||
# from a host reboot without an operator. Pinned to the FILE so neither can
|
# from a host reboot without an operator. Pinned to the FILE so neither can
|
||||||
# quietly vanish in a rewrite.
|
# quietly vanish in a rewrite.
|
||||||
check "staging: demands VM mode (BOX_REQUIRE_VM=1)" 0 "" \
|
check "staging-box: demands VM mode (BOX_REQUIRE_VM=1)" 0 "" \
|
||||||
grep -qx 'BOX_REQUIRE_VM="1"' "$ROOT/templates/staging/box.env"
|
grep -qx 'BOX_REQUIRE_VM="1"' "$ROOT/templates/staging-box/box.env"
|
||||||
check "staging: demands autostart (BOX_AUTOSTART=1)" 0 "" \
|
check "staging-box: demands autostart (BOX_AUTOSTART=1)" 0 "" \
|
||||||
grep -qx 'BOX_AUTOSTART="1"' "$ROOT/templates/staging/box.env"
|
grep -qx 'BOX_AUTOSTART="1"' "$ROOT/templates/staging-box/box.env"
|
||||||
check "staging: the tenant role is 'staging'" 0 "ROLE=staging" tpl "$ROOT" staging
|
check "staging-box: the tenant role is 'staging-box'" 0 "ROLE=staging-box" tpl "$ROOT" staging-box
|
||||||
check "staging: the seed user is rig's default for the role ('ops')" 0 "USER=ops" tpl "$ROOT" staging
|
check "staging-box: the seed user is rig's default for the role ('ops')" 0 "USER=ops" tpl "$ROOT" staging-box
|
||||||
# The agent tenants: role = user = template name, rig's default mapping.
|
# The agent tenants. Two names, not one: the TEMPLATE is named for the rig role
|
||||||
for t in claude codex grok; do
|
# it converges — suffix and all, since rig's roles carry a family suffix
|
||||||
check "$t: role and user are '$t' (rig's default tenant mapping)" 0 "USER=$t REQUIRE_VM= AUTOSTART= ROLE=$t" \
|
# ('-box' for box tenants, '-server' for fleet machines, rig#76) — while the
|
||||||
tpl "$ROOT" "$t"
|
# seed USER stays the bare agent name, because that is the user rig's role
|
||||||
|
# converges and the one 'box shell' lands in. The pairing is the whole point of
|
||||||
|
# pinning it here: a rename that moves one and forgets the other mints a box
|
||||||
|
# whose role dies looking for a user that was never created.
|
||||||
|
for u in claude codex grok; do
|
||||||
|
check "$u-box: role is '$u-box', seed user is '$u' (rig's tenant mapping)" \
|
||||||
|
0 "USER=$u REQUIRE_VM= AUTOSTART= ROLE=$u-box" tpl "$ROOT" "$u-box"
|
||||||
done
|
done
|
||||||
# blank stays a box with NOBODY home: no rig, no role — same isolation, no
|
# blank stays a box with NOBODY home: no rig, no role — same isolation, no
|
||||||
# tooling, and nothing auto-runs in it.
|
# tooling, and nothing auto-runs in it.
|
||||||
|
|
@ -426,11 +432,11 @@ check "new: the overran-but-registered branch says so (not the wedge story)" 0 "
|
||||||
# shellcheck disable=SC2016 # the $-strings are literals in the target file
|
# shellcheck disable=SC2016 # the $-strings are literals in the target file
|
||||||
check "new: BOX_LAUNCH_TIMEOUT is documented in box help new" 0 "" bash -c '
|
check "new: BOX_LAUNCH_TIMEOUT is documented in box help new" 0 "" bash -c '
|
||||||
"'"$ROOT"'/bin/box" help new | grep "BOX_LAUNCH_TIMEOUT" | grep -q 600'
|
"'"$ROOT"'/bin/box" help new | grep "BOX_LAUNCH_TIMEOUT" | grep -q 600'
|
||||||
# staging's creds-holding join stays OPERATOR-run: cmd_new may print it as a
|
# staging-box's creds-holding join stays OPERATOR-run: cmd_new may print it as
|
||||||
# next step, but no template and no code path auto-runs "rig bootstrap
|
# a next step, but no template and no code path auto-runs "rig bootstrap
|
||||||
# workload" — the one absence that keeps box creds-free end to end.
|
# workload-server" — the one absence that keeps box creds-free end to end.
|
||||||
check "new: the workload join is printed, never exec'd" 1 "" bash -c '
|
check "new: the workload join is printed, never exec'd" 1 "" bash -c '
|
||||||
grep "rig bootstrap workload" "'"$ROOT"'/bin/box" | grep -q "incus exec"'
|
grep "rig bootstrap workload-server" "'"$ROOT"'/bin/box" | grep -q "incus exec"'
|
||||||
check "templates: no template names a creds-holding role" 1 "" bash -c '
|
check "templates: no template names a creds-holding role" 1 "" bash -c '
|
||||||
grep -h "^BOX_BOOTSTRAP_ROLE=" "'"$ROOT"'"/templates/*/box.env | grep -qE "workload|host|custom"'
|
grep -h "^BOX_BOOTSTRAP_ROLE=" "'"$ROOT"'"/templates/*/box.env | grep -qE "workload|host|custom"'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue