The old tmux check hardcoded blank/claude/codex/grok, so a new template
could ship without CI ever reading it. The suite now discovers templates/*/
and, for each: drives the REAL load_template (extracted from bin/box, the
same trick box_tier and install.sh's DEST block get) so box.env must parse
against the actual allowlist with BOX_IMAGE + BOX_USER present; asserts
user-data.yaml exists, declares #cloud-config, and is well-formed YAML
(python3+pyyaml, skipped loudly where absent — CI has both); and keeps the
#65 tmux contract. Fixtures prove the dies a green parse cannot: an unknown
key (no template grows a network) and a missing required key.
Staging-specific: both boot demands proven through the parser, docker + rig
preinstalled, and a creds-free refusal grep — no tailscale/authkey/ssh in
effective cloud-init lines; rig installs those inside the guest. Plus the
cmd_new half, grepped the way the expose guard is: the REQUIRE_VM refusal
orders after pick_mode, and boot.autostart is stamped only under the
T_AUTOSTART guard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Debian 13 cloud image, user 'ops', build-sized resources (the control
plane builds on the target), BOX_REQUIRE_VM + BOX_AUTOSTART. cloud-init
installs docker, rig and tmux and stops: tailscale, openssh-server and
every credential are deliberately absent — rig installs and hardens those
at bootstrap time ('box shell' → 'sudo rig bootstrap workload'), and box
never sees the auth key. No agent, no agent-context file: a server, not
an agent devbox. tmux rides along because 'box tmux' is a contract every
template honors (#65), staging included.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two optional, server-class keys in the box.env allowlist. BOX_REQUIRE_VM=1
refuses both the silent container fallback (no /dev/kvm, exit 1) and an
explicit --container (exit 2): such a template's trust boundary is the VM,
and its guest runs docker. BOX_AUTOSTART=1 stamps boot.autostart=true at
launch, per-instance like limits.*, so the box comes back deterministically
after a host reboot; a --from clone needs no code because 'incus copy'
keeps every non-volatile config key — the same ride the user.* stamps take.
Still no key for a network or a security flag, on purpose.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>