feat: global install (#71), tmux (#65), CI + tests; folds #66 #73

Merged
dan-claude-bot merged 7 commits from feat/multiuser-hosts into main 2026-07-18 00:24:38 +00:00
dan-claude-bot commented 2026-07-17 23:04:04 +00:00 (Migrated from github.com)

What

Fixes the first half of the multi-user-host bug — box: command not found for other operators — and gives box the CI + tests it never had. Folds in and supersedes #66 (its 5 commits are preserved, crediting @claude-bot-andresmgsl — please confirm before closing #66).

Scope change: this PR originally also carried the restricted-incus-tier work (#72). A Task-0 rehearsal on real hardware (Debian 13 / Incus 6.0.4) vetoed that design — incus-user walls each user into a private incusbr-<uid> and blocks box's shared, hardened boxnet. That work has been split out to #74 (redesign, built on the measured facts) and preserved on feat/restricted-tier-wip. See the rehearsal write-up. This PR is now only the verified-good parts and carries no substrate gate.

Closes #71, #65; carries #66's #63/#64.

#71 — global install

Run as root, box installs once to /opt/box with a /usr/local/bin shim, world-readable (chmod -R a+rX, root-guarded) — so every operator has box on PATH. Per-user ($HOME/.local) install is byte-identical to before; BOX_HOME/BOX_BIN still override. This is the path rig installs box through (rig#24's box role; companion rig PR #28).

Verified on real hardware: as root, install.sh + setup-host built the whole stack end-to-end (btrfs pool, boxnet, box-isolate ACL, box-net profile, nft box-to-box drop, firewall) on a fresh Debian 13 / Incus 6.0.4 host.

#65 — tmux

box tmux runs tmux new-session inside the box, but the templates didn't install tmux (tmux: command not found). tmux is now in every template's cloud-init (blank needed it; the others already had it).

CI + tests (new — box had neither)

.github/workflows/ci.yml mirrors rig's: one check job = globstar shellcheck -x over bin/* **/*.sh, then bash test/cli.sh. The suite is dependency-free, non-root, no-Incus: the full CLI contract; install.sh's DEST/BINDIR branch driven functionally against a shim id (both tiers + 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 were resolved so CI is green over the whole tree.

Testing

  • bash test/cli.sh33 passed, 0 failed (non-root, no Incus).
  • shellcheck -x bin/* **/*.sh → clean.
  • Global install + setup-host exercised end-to-end on a real Incus 6.0.4 host.

🤖 Generated with Claude Code

## What Fixes the first half of the multi-user-host bug — *`box: command not found` for other operators* — and gives box the CI + tests it never had. **Folds in and supersedes #66** (its 5 commits are preserved, crediting @claude-bot-andresmgsl — please confirm before closing #66). > **Scope change:** this PR originally also carried the restricted-`incus`-tier work (#72). A Task-0 rehearsal on real hardware (Debian 13 / Incus 6.0.4) **vetoed that design** — incus-user walls each user into a private `incusbr-<uid>` and blocks box's shared, hardened `boxnet`. That work has been split out to **#74** (redesign, built on the measured facts) and preserved on `feat/restricted-tier-wip`. See the [rehearsal write-up](https://github.com/heavy-duty/box/pull/73#issuecomment-5008542760). **This PR is now only the verified-good parts** and carries no substrate gate. Closes **#71**, **#65**; carries #66's **#63/#64**. ## #71 — global install Run as root, box installs **once** to `/opt/box` with a `/usr/local/bin` shim, world-readable (`chmod -R a+rX`, root-guarded) — so every operator has `box` on PATH. Per-user (`$HOME/.local`) install is byte-identical to before; `BOX_HOME`/`BOX_BIN` still override. This is the path **rig installs box through** (rig#24's `box` role; companion rig PR #28). **Verified on real hardware:** as root, `install.sh` + `setup-host` built the whole stack end-to-end (btrfs pool, `boxnet`, `box-isolate` ACL, `box-net` profile, nft box-to-box drop, firewall) on a fresh Debian 13 / Incus 6.0.4 host. ## #65 — tmux `box tmux` runs `tmux new-session` inside the box, but the templates didn't install tmux (`tmux: command not found`). `tmux` is now in every template's cloud-init (`blank` needed it; the others already had it). ## CI + tests (new — box had neither) `.github/workflows/ci.yml` mirrors rig's: one `check` job = globstar `shellcheck -x` over `bin/* **/*.sh`, then `bash test/cli.sh`. The suite is **dependency-free, non-root, no-Incus**: the full CLI contract; `install.sh`'s DEST/BINDIR branch driven functionally against a shim `id` (both tiers + `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 were resolved so CI is green over the whole tree. ## Testing - `bash test/cli.sh` → **33 passed, 0 failed** (non-root, no Incus). - `shellcheck -x bin/* **/*.sh` → clean. - Global install + `setup-host` exercised end-to-end on a real Incus 6.0.4 host. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dan-claude-bot commented 2026-07-17 23:38:15 +00:00 (Migrated from github.com)

🔴 Task-0 rehearsal run — the substrate vetoes the #72 design

Ran the rehearsal on a real Debian 13 / Incus 6.0.4 host (nested KVM). Full write-up in the plan doc (docs/plans/2026-07-17-multiuser-hosts.md, just pushed).

Verified GOOD (mergeable):

  • #71 global install + setup-host built the entire stack end-to-end (btrfs pool, boxnet, box-isolate ACL, box-net profile, nft box-to-box drop, firewall). incus-user.socket ships enabled + active.
  • incus-user confinement works: a plain incus-group user is auto-confined to a restricted project (user-<uid>), seeing only their own instances.

The veto (criteria a/b FAIL):
incus-user does not share boxnet. It gives each user a private auto-bridge incusbr-<uid> and sets restricted.networks.access: incusbr-<uid>:

  • incus network show boxnet as the user → Error: Network not found
  • box new --profile box-net / ensure_boxnet_profilefails (profile references a network they can't use)

So box's whole isolation stack lives on boxnet, which restricted users never touchbox new doesn't work for them as written. The fix is real but is a redesign: an admin must set restricted.networks.access boxnet,incusbr-<uid> (both — boxnet alone conflicts with the auto default profile) and install the box-net profile per-project, and the restricted project doesn't exist until first use — so it needs an admin convergence hook, not setup-host.

Recommendation: split this PR. #71 + #65 + CI + folded #66 are verified and can merge. #72 (restricted tier) is NOT ready — it stays behind the Task-0 gate and moves to a redesign issue built on these measured facts. Keeping this PR in draft until we decide the split.

## 🔴 Task-0 rehearsal run — the substrate vetoes the #72 design Ran the rehearsal on a real **Debian 13 / Incus 6.0.4** host (nested KVM). Full write-up in the plan doc (`docs/plans/2026-07-17-multiuser-hosts.md`, just pushed). **Verified GOOD (mergeable):** - **#71 global install + `setup-host`** built the entire stack end-to-end (btrfs pool, boxnet, box-isolate ACL, box-net profile, nft box-to-box drop, firewall). `incus-user.socket` ships enabled + active. - **`incus-user` confinement works**: a plain `incus`-group user is auto-confined to a restricted project (`user-<uid>`), seeing only their own instances. **The veto (criteria a/b FAIL):** incus-user does **not** share `boxnet`. It gives each user a *private* auto-bridge `incusbr-<uid>` and sets `restricted.networks.access: incusbr-<uid>`: - `incus network show boxnet` as the user → `Error: Network not found` - `box new --profile box-net` / `ensure_boxnet_profile` → **fails** (profile references a network they can't use) So box's **whole isolation stack lives on boxnet, which restricted users never touch** — `box new` doesn't work for them as written. The fix is real but is a **redesign**: an admin must set `restricted.networks.access boxnet,incusbr-<uid>` (both — boxnet alone conflicts with the auto default profile) *and* install the box-net profile per-project, and the restricted project doesn't exist until first use — so it needs an admin convergence hook, not `setup-host`. **Recommendation:** split this PR. **#71 + #65 + CI + folded #66 are verified and can merge.** **#72 (restricted tier) is NOT ready** — it stays behind the Task-0 gate and moves to a redesign issue built on these measured facts. Keeping this PR in draft until we decide the split.
dan-claude-bot commented 2026-07-18 00:04:07 +00:00 (Migrated from github.com)

Re-scoped + force-pushed. After the Task-0 rehearsal vetoed the #72 restricted-tier design (comment above), I split #72 out to #74 (redesign on the measured incus-user facts) and preserved that code on feat/restricted-tier-wip. This branch is now #71 (global install) + #65 (tmux) + CI/tests, folding #66 — all verified, no substrate gate. Global install + setup-host were exercised end-to-end on a real Debian 13 / Incus 6.0.4 host. Ready to come out of draft once #66's fold is acknowledged.

**Re-scoped + force-pushed.** After the Task-0 rehearsal vetoed the #72 restricted-tier design (comment above), I split #72 out to **#74** (redesign on the measured incus-user facts) and preserved that code on `feat/restricted-tier-wip`. This branch is now **#71 (global install) + #65 (tmux) + CI/tests, folding #66** — all verified, no substrate gate. Global install + `setup-host` were exercised end-to-end on a real Debian 13 / Incus 6.0.4 host. Ready to come out of draft once #66's fold is acknowledged.
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/box#73
No description provided.