From 53dc2b90470ba60098507a8a0564845dd0be662e Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Sat, 18 Jul 2026 19:47:20 +0000 Subject: [PATCH] docs: BOX_SUBNET, the setup-host refusal, and the doctor signature (#80) README's setup-host section documents the subnet input and the refusal; 'box help setup-host' names BOX_SUBNET and the nested-box trap; 'box help doctor' names the #80 signature it now judges; the changelog carries the full story, including that suggested fix 4 (the agent-context guard note for the templates) lands in heavy-duty/rig#31's bootstrap roles per the thin-templates split (#81). Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ README.md | 14 ++++++++++++++ bin/box | 17 +++++++++++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbde0ce..03fdaa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,34 @@ which records not just what changed but what each drill run proved. ### Added +- **`setup-host` refuses a claimed subnet, and `BOX_SUBNET` picks another** + (#80) — run inside a box, `setup-host` used to build a nested `boxnet` on + the exact subnet and gateway of the guest's own uplink: the guest then held + its gateway's address as a *local* address, carried duplicate connected + routes for its uplink subnet, and suffered intermittent, self-recovering + egress blackouts that looked like flaky internet (measured live: ~24–36 s + outages, roughly hourly, with the host clean throughout). `setup-host` now + scans the target subnet **before any mutation** — the default route's + gateway inside it, or any non-`boxnet` interface holding an address in it — + and refuses, naming the way out. A prior `boxnet` owning the subnet is the + legitimate converge path and does not trip it. `BOX_SUBNET=` + (validated, alongside the existing `BOX_DNS`) moves the whole stack: the + bridge address, the ACL's gateway carve-out (now converged via + `network acl edit`, so a bridge moved off a colliding subnet no longer + strands box DNS behind a stale `/32`), the firewall (`box-firewall` reads + the gateway off the live bridge), and every drill/migrate probe that used + to hardcode `10.88`. +- **`box doctor` knows the #80 signature** — a default gateway held as a + LOCAL address, and duplicate connected routes for the uplink subnet, judged + from `ip route`/`ip addr` on the machine doctor runs on (both tiers, before + any daemon check — the nested daemon answering could be the impostor) and + probed *inside* every box it examines. The existing "egress broken but DNS + fine" split now names itself as #80's fingerprint (the impostor dnsmasq on + a captured gateway keeps resolving while IP egress dies), and the admin ACL + section verifies the gateway carve-out matches `boxnet`'s actual gateway. + The agent-context guard for the templates (suggested fix 4) lands in + heavy-duty/rig#31's bootstrap roles per the thin-templates split (#81). + - **Server-posture template keys** (#81, carved from #69) — two optional `box.env` allowlist keys. `BOX_REQUIRE_VM=1` refuses both the silent container fallback (no `/dev/kvm`, exit 1) and an explicit `--container` diff --git a/README.md b/README.md index 64b3764..71e6f2e 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,20 @@ re-apply at boot via `box-firewall.service` — no post-reboot ritual. If the host lacks `dnsmasq-base` (Debian cloud images skip Recommends): `sudo apt-get install -y dnsmasq-base`. +The stack's subnet is `10.88.0.0/24` by default; `BOX_SUBNET` picks another +`/24` (`BOX_SUBNET=10.89.0.0/24 box setup-host` — the bridge address, the +ACL's gateway carve-out and the firewall all derive from it). setup-host +**refuses to build on a subnet something already claims** — most tellingly +when this machine's own default gateway sits inside it, which means it is +being run *inside a box*: a nested `boxnet` on the guest's own uplink subnet +captures its gateway address and blackholes the guest's egress in +intermittent, maddening-to-attribute blackouts +([#80](https://github.com/heavy-duty/box/issues/80)). `BOX_SUBNET` is the +sanctioned way out for a nested or otherwise-conflicted install, and +`box doctor` recognizes the poisoned state (a gateway held as a local +address, duplicate uplink routes) on the machine it runs on and inside every +box it probes. + A host still carrying the pre-0.4.0 stack: `box migrate-host --all-boxes` re-homes each legacy box onto `boxnet` (authed state preserved), and `box migrate-host --retire-legacy` removes the old bridge and profile once no diff --git a/bin/box b/bin/box index a57d6ec..b8fd45a 100755 --- a/bin/box +++ b/bin/box @@ -459,8 +459,12 @@ EOF Answer "is this host fit to mint boxes?" from ground truth, not config claims: is the Incus daemon answering, is a dnsmasq actually serving boxnet, does the kernel's bridge port say 'isolated on', is the resolver pinned or is a -host VPN's DNS leaking into boxes, can a box actually resolve names. Every -check exists because its fault has happened — most kill a cold mint with a +host VPN's DNS leaking into boxes, can a box actually resolve names — and +the #80 nested-stack signature: a default gateway held as a LOCAL address, +or duplicate connected routes for the uplink subnet, judged on this machine +AND inside every box it probes (a box stack installed inside a box squats on +the guest's gateway and blackholes its egress, intermittently). Every check +exists because its fault has happened — most kill a cold mint with a cloud-init error that names none of them. --fix also revert what a drill run may have left behind @@ -484,7 +488,16 @@ install.sh runs it for you, so this is for re-applying by hand. One run is enough. If it has to add you to the incus-admin group it re-runs itself under that group — no re-login, no second invocation. +The stack's subnet is 10.88.0.0/24 by default; BOX_SUBNET picks another /24 +(the bridge, the gateway carve-out and the firewall all derive from it). It +REFUSES, before touching anything, where the target subnet is already claimed +— most tellingly when this machine's own default gateway sits inside it, +i.e. when you are running setup-host INSIDE a box: a nested stack on the +guest's own uplink subnet captures its gateway address and blackholes its +egress, intermittently (issue #80). The sanctioned way to nest: + box setup-host + BOX_SUBNET=10.89.0.0/24 box setup-host # nested, or a conflicted host Multi-user hosts: setup-host builds the stack once, for everyone. An admin then hands individual users the restricted tier with 'box grant ' —