Round-1 convergence from all three reviewers, both findings real:
A version string used to be a path fragment: 'box uninstall
../../../.ssh' resolved below versions/ and rm -rf'd wherever it
landed, 'box use' could point current outside the root, and a hostile
flat-tree VERSION could steer the migration's mv the same way. One
strict validator now gates every caller — only [A-Za-z0-9._+-], no
leading '.' or '-' — byte-identical in install.sh and bin/box like
existing_boxes, diff-guarded in the tests, with traversal regressions
on use, uninstall and the migration (which now refuses BEFORE the tree
moves anywhere).
--force is uninstall's installer-family consent, and --purge-host now
forwards it: teardown-host.sh gets --yes under --force/BOX_YES, so the
combined non-interactive uninstall no longer dies at teardown's own
prompt. CI's drill now runs the combined verb with --force alone (no
BOX_YES, no TTY) — the exact invocation that used to abort.
Also grok's polish, taken: current flips by rename (ln to a side name,
mv -Tf over — no window with no current) in both install.sh and 'box
use'; BOX_REINSTALL swaps by two renames and deletes LAST; and the
single-version path refuses while current is dangling (readlink -f
resolves a missing last component, so the guard checks the DIRECTORY,
not just the string).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test/cli.sh drives REAL installer runs via BOX_INSTALL_SOURCE (temp
BOX_HOME/BOX_BIN, a fabricated second version for upgrades): fresh install
lands versioned, --version answers through the chain, side-by-side installs,
same-version no-op, BOX_REINSTALL, the 0.6.0 flat-tree migration, stale-
symlink healing, single-version and full uninstalls asserted to zero residue
(a planted survivor makes it scream INCOMPLETE), working-tree refusals, and
byte-identity of the existing_boxes copies. The rehearsal job now installs
via install.sh itself — CI proves the installer under review — and ends with
the uninstall drill: revoke --purge, teardown --yes, box uninstall, then
assert nothing is left (networks, profiles, nft, units, files, symlinks).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
drill/multiuser.sh: two throwaway users, real grants, real boxes, and the
contract measured from INSIDE them — #74's criteria (a)-(f) plus what the
findings added: the cross-user sibling drop, the closed private-bridge
escape hatches, incus-user re-sync survival, scoped revoke --purge. Its own
first runs are in RUNS.md (MU-1..3): both false FAILs were the instrument,
and both lessons (keep the mint's narration; probe a SIBLING's name, never
your own — /etc/hosts answers for you) are now comments in the script.
test/cli.sh: box_tier() driven against a shim id for all five cases
(including both-groups → admin: membership wins at the socket), the two
copies diffed byte-identical, the tier scripts' usage contracts driven for
real, and grep-guards on every load-bearing line a daemon-free run cannot
exercise — the expose guard's position, the boxnet-and-only-boxnet
narrowing, the snapshot allow, 'incus:snapshot restore'.
ci.yml: a second job stands up the real stack on the runner (setup-host,
doctor, then the rehearsal in container mode) — every PR now proves the
tier's semantics against a live daemon. The tier's mechanics are
instance-type-independent (the drop, the ACL, dns.mode and port_isolation
bind to boxnet, not the instance); the VM trust boundary itself stays a
real-hardware ritual, like the full drill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
box had no CI and no unit tests — only the live-host drill. Mirror rig's CI:
one `check` job = globstar `shellcheck -x` over bin/* and **/*.sh, then
`bash test/cli.sh`. The suite is dependency-free and runs non-root with no
Incus: the full CLI contract; install.sh's DEST/BINDIR branch driven
functionally against a shim `id` (both tiers + the BOX_HOME/BOX_BIN overrides);
the root-only a+rX and #66's confirm/no-op flow grep-guarded; tmux asserted in
every template. Pre-existing repo shellcheck findings (bin/box SC2034/SC2015/
SC2020, and file-level SC2015 idioms in doctor.sh/wipe.sh/migrate-host.sh) were
resolved — real fixes where behaviour allows, reasoned disables otherwise — so
the new CI is green over the whole repo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>