feat!: claudebox becomes box — the Claude box is one template among several #52

Merged
dan-claude-bot merged 7 commits from feat/box-rename into main 2026-07-14 15:44:10 +00:00
dan-claude-bot commented 2026-07-14 14:23:21 +00:00 (Migrated from github.com)

Closes #17 — the third act of the #12 split, unblocked by #16's measured close.

box new --name work                       # the claude template, as always
box new --name scratch --template blank   # a box with nobody home — same isolation
box templates                             # what this install can mint
box new --name feature --from work/authed # unchanged; metadata rides the clone
  1. Clean cut at 0.4.0 — binary is box, no claudebox shim; the installer retires the old symlink on upgrade and says so. Dual-tag matching and the legacy-user fallback ship regardless (below).
  2. Default template = claudebox new --name work behaves exactly like claudebox new --name work did.
  3. Repo stays heavy-duty/claudebox, binary is box — the documented curl URL keeps working; CLAUDEBOX_* installer env vars stay (they name the repo, which didn't move).

The mechanic (per the issue, with its audit receipts)

  • Launch stamps the template onto the instance: user.box=1, user.box.template, user.box.user. shell/exec/tmux read the user back via box_user()${u:-} fallbacks, never || (audit B4) — and incus copy carries user.* (audit B2), so a clone knows its user without consulting the template (the A9 drill check already probes exactly this).
  • Templates: templates/<t>/box.env + verbatim user-data.yaml. box.env is parsed against a strict allowlist, never sourced — and there is no key for a network or security flag, on purpose. Unknown key → hard error naming it (tested: BOX_NETWORK=lanunknown key 'BOX_NETWORK' — … there is no key for a network, on purpose).
  • The placement contract: every template launches with the shared box-net profile — isolated NIC (security.port_isolation) + root disk, nothing else. Resources are per-instance from box.env, overridable at mint via BOX_CPU/BOX_MEMORY/BOX_DISK env — which is also how the drill shrinks boxes on a small host, since a profile edit no longer reaches limits.
  • Compat is the tag, not the name: resolve_box(), list, the doctor's probe discovery, and the drill's tenant check all honor user.claudebox=1 forever; the legacy tag maps to the claude user. A pre-rename box lists, shells into claude, and works as a --from source. (list --json filters on the new tag only — the one documented compat edge.)

Deliberate divergence from the issue's table

The host-stack resource names are not renamed: claudenet, claude-isolate, the nft tables, claudebox-firewall.{sh,service}. They are host-internal, invisible to users of the CLI, and renaming them breaks every provisioned host for zero user-visible gain. What did change: setup-host now creates box-net (and no longer creates claude-dev); an existing claude-dev is left alone while legacy boxes reference it (Incus refuses to delete an in-use profile anyway) and teardown removes both.

Drill and doctor stay green — by construction, to be proven by run

Every exact-string dependency was walked: command invocations, the ^box: announce-line skips, the 'box info archive' pointer, the A-phase tag semantics (now probing user.box), the hatch's user.box config check, the profile preflight/cleanup (both profiles), and the resource-lowering path (now BOX_MEMORY/BOX_CPU exports). Verified locally: bash -n on every script, plus live box --version / templates / help / did-you-mean / both template-rejection paths.

To drill this branch before merging (the drill self-installs from main by default):

bash drill/drill.sh --repo claude-hdb/claudebox --ref feat/box-rename

Expected: 47/47, with the resource note now reading minting at 3GiB/2cpu via BOX_MEMORY/BOX_CPU. Acceptance items that need the live run: blank-template mint + isolation parity, and a pre-rename box on the host surviving every verb.

VERSION: 0.3.0 → 0.4.0. After this merges, #46's verb is box doctor and #6's is box tmux — both already read the template user.

🤖 Generated with Claude Code

Closes #17 — the third act of the #12 split, unblocked by #16's measured close. ```sh box new --name work # the claude template, as always box new --name scratch --template blank # a box with nobody home — same isolation box templates # what this install can mint box new --name feature --from work/authed # unchanged; metadata rides the clone ``` ## The three open calls, taken as recommended 1. **Clean cut at 0.4.0** — binary is `box`, no `claudebox` shim; the installer retires the old symlink on upgrade and says so. Dual-tag matching and the legacy-user fallback ship regardless (below). 2. **Default template = `claude`** — `box new --name work` behaves exactly like `claudebox new --name work` did. 3. **Repo stays `heavy-duty/claudebox`, binary is `box`** — the documented curl URL keeps working; `CLAUDEBOX_*` installer env vars stay (they name the repo, which didn't move). ## The mechanic (per the issue, with its audit receipts) - Launch stamps the template onto the instance: `user.box=1`, `user.box.template`, `user.box.user`. `shell`/`exec`/`tmux` read the user back via `box_user()` — `${u:-}` fallbacks, never `||` (audit B4) — and `incus copy` carries `user.*` (audit B2), so **a clone knows its user without consulting the template** (the A9 drill check already probes exactly this). - **Templates**: `templates/<t>/box.env` + verbatim `user-data.yaml`. box.env is parsed against a strict allowlist, **never sourced** — and there is no key for a network or security flag, on purpose. Unknown key → hard error naming it (tested: `BOX_NETWORK=lan` → `unknown key 'BOX_NETWORK' — … there is no key for a network, on purpose`). - **The placement contract**: every template launches with the shared `box-net` profile — isolated NIC (`security.port_isolation`) + root disk, nothing else. Resources are per-instance from box.env, overridable at mint via `BOX_CPU`/`BOX_MEMORY`/`BOX_DISK` env — which is also how the drill shrinks boxes on a small host, since a profile edit no longer reaches limits. - **Compat is the tag, not the name**: `resolve_box()`, `list`, the doctor's probe discovery, and the drill's tenant check all honor `user.claudebox=1` forever; the legacy tag maps to the `claude` user. A pre-rename box lists, shells into claude, and works as a `--from` source. (`list --json` filters on the new tag only — the one documented compat edge.) ## Deliberate divergence from the issue's table The host-stack **resource names are not renamed**: `claudenet`, `claude-isolate`, the nft tables, `claudebox-firewall.{sh,service}`. They are host-internal, invisible to users of the CLI, and renaming them breaks every provisioned host for zero user-visible gain. What did change: setup-host now creates **box-net** (and no longer creates claude-dev); an existing claude-dev is left alone while legacy boxes reference it (Incus refuses to delete an in-use profile anyway) and teardown removes both. ## Drill and doctor stay green — by construction, to be proven by run Every exact-string dependency was walked: command invocations, the `^box:` announce-line skips, the `'box info archive'` pointer, the A-phase tag semantics (now probing `user.box`), the hatch's `user.box` config check, the profile preflight/cleanup (both profiles), and the resource-lowering path (now `BOX_MEMORY`/`BOX_CPU` exports). Verified locally: `bash -n` on every script, plus live `box --version` / `templates` / `help` / did-you-mean / both template-rejection paths. **To drill this branch before merging** (the drill self-installs from main by default): ```sh bash drill/drill.sh --repo claude-hdb/claudebox --ref feat/box-rename ``` Expected: 47/47, with the resource note now reading `minting at 3GiB/2cpu via BOX_MEMORY/BOX_CPU`. Acceptance items that need the live run: blank-template mint + isolation parity, and a pre-rename box on the host surviving every verb. VERSION: 0.3.0 → **0.4.0**. After this merges, #46's verb is `box doctor` and #6's is `box tmux` — both already read the template user. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dan-claude-bot commented 2026-07-14 14:36:40 +00:00 (Migrated from github.com)

Second commit, per operator direction — the host-name divergence is reversed and the cut completed:

  • Full box- rename: boxnet (on 10.88.0.0/24 — a pre-rename host may still carry claudenet on 10.87, and two bridges must not claim one subnet), box-isolate, nft inet box/bridge box, box-firewall.{sh,service}. teardown-host.sh now strips both name generations, so one script uninstalls a host of any age.
  • Default template is blankbox new --name x mints bare Debian; the Claude box is --template claude. The /login hint follows the effective template read off the instance (clones included).
  • The drill validates templates: listing, unknown-template refusal, BOX_NETWORK rejected by name from a planted bad template, and a full blank mint — default resolves to blank, metadata stamped, box-net placement, exec lands in dev, no claude binary, isolation parity (egress + pinned DNS).
  • drill/wipe.sh — the staging-server reset button: both tag generations, every drill-named instance, networks/ACLs/profiles/firewall of both generations, cached images, --purge-storage for the pool. Ends by asserting the absence of every artifact instead of trusting removal exit codes.

Validation sequence for the staging server:

git fetch origin && git checkout feat/box-rename   # or after merge, main
bash drill/wipe.sh --purge-storage                 # bare host (also re-fires the #51 btrfs bootstrap)
bash drill/drill.sh --repo claude-hdb/claudebox --ref feat/box-rename

Expect the previous 47 checks plus ~12 template checks, everything rebuilt under the new names.

Second commit, per operator direction — the host-name divergence is reversed and the cut completed: - **Full `box-` rename**: `boxnet` (on **10.88.0.0/24** — a pre-rename host may still carry `claudenet` on 10.87, and two bridges must not claim one subnet), `box-isolate`, nft `inet box`/`bridge box`, `box-firewall.{sh,service}`. `teardown-host.sh` now strips **both name generations**, so one script uninstalls a host of any age. - **Default template is `blank`** — `box new --name x` mints bare Debian; the Claude box is `--template claude`. The /login hint follows the *effective* template read off the instance (clones included). - **The drill validates templates**: listing, unknown-template refusal, `BOX_NETWORK` rejected by name from a planted bad template, and a full blank mint — default resolves to blank, metadata stamped, `box-net` placement, `exec` lands in `dev`, no claude binary, isolation parity (egress + pinned DNS). - **`drill/wipe.sh`** — the staging-server reset button: both tag generations, every drill-named instance, networks/ACLs/profiles/firewall of both generations, cached images, `--purge-storage` for the pool. Ends by asserting the **absence** of every artifact instead of trusting removal exit codes. Validation sequence for the staging server: ```sh git fetch origin && git checkout feat/box-rename # or after merge, main bash drill/wipe.sh --purge-storage # bare host (also re-fires the #51 btrfs bootstrap) bash drill/drill.sh --repo claude-hdb/claudebox --ref feat/box-rename ``` Expect the previous 47 checks plus ~12 template checks, everything rebuilt under the new names.
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#52
No description provided.