forked from heavy-duty/box
feat: host lifecycle as verbs (setup-host/teardown-host/migrate-host) + .box/ convention
Two things: 1. The host scripts are first-class verbs now — 'box setup-host', 'box teardown-host [--purge-incus]', 'box migrate-host --box <n>'. Nobody should have to run ~/.local/share/box/host/<script>.sh; that read like an external script and exposed an install path. Each verb execs the installed script with its flags passed through (same pattern as 'box doctor'). README, doctor hints, and the uninstall section point at the verbs now. 2. The repo-runbook convention is '.box/', not '.claudebox/'. Renamed across docs and the templates' agent briefing; the briefing tells the agent to read either, and box-recipe.md notes the rename, so repos still shipping '.claudebox/' keep working through the transition. (Consuming repos rename their own folder — tracked separately.) Also widened the help command column for the longer verb names, and fixed one sed-casualty where a broad '.claudebox'→'.box' pass had turned the README's legacy user.claudebox tag into user.box.
This commit is contained in:
parent
4eb6b35a7b
commit
455fbc656e
8 changed files with 90 additions and 36 deletions
19
README.md
19
README.md
|
|
@ -18,7 +18,7 @@ live in a shared profile no template can touch, so `blank` is a box with
|
||||||
nobody home — not a box with the safety off.
|
nobody home — not a box with the safety off.
|
||||||
|
|
||||||
**The tool knows nothing about your projects.** You just `git clone` inside a
|
**The tool knows nothing about your projects.** You just `git clone` inside a
|
||||||
box. A repo can ship an optional [`.claudebox/`](docs/box-recipe.md)
|
box. A repo can ship an optional [`.box/`](docs/box-recipe.md)
|
||||||
runbook that Claude Code reads and acts on — there is no `install` step and no
|
runbook that Claude Code reads and acts on — there is no `install` step and no
|
||||||
host-run setup. See [docs/box-design.md](docs/box-design.md) for the
|
host-run setup. See [docs/box-design.md](docs/box-design.md) for the
|
||||||
design rationale.
|
design rationale.
|
||||||
|
|
@ -44,7 +44,7 @@ retires the old `claudebox` symlink. (No `git clone` needed.)
|
||||||
## One-time host setup (Ubuntu 24.04 / Debian 13)
|
## One-time host setup (Ubuntu 24.04 / Debian 13)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
~/.local/share/box/host/setup-host.sh # run twice if it adds you to incus-admin (re-login between)
|
box setup-host # run twice if it adds you to incus-admin (re-login between)
|
||||||
```
|
```
|
||||||
|
|
||||||
Idempotent. Installs Incus and creates the isolation stack: the `boxnet` NAT
|
Idempotent. Installs Incus and creates the isolation stack: the `boxnet` NAT
|
||||||
|
|
@ -70,7 +70,7 @@ claude # then run /login — copy the URL (press c), o
|
||||||
# in YOUR browser, paste the code back. No host CLI needed.
|
# in YOUR browser, paste the code back. No host CLI needed.
|
||||||
gh auth login # or drop a PAT in — your git credentials, your call
|
gh auth login # or drop a PAT in — your git credentials, your call
|
||||||
git clone https://github.com/you/project && cd project
|
git clone https://github.com/you/project && cd project
|
||||||
claude # if the repo has .claudebox/, Claude reads it and sets up
|
claude # if the repo has .box/, Claude reads it and sets up
|
||||||
```
|
```
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
@ -158,7 +158,8 @@ dev/test only.
|
||||||
## Boxes are just Incus instances
|
## Boxes are just Incus instances
|
||||||
|
|
||||||
A box is an ordinary Incus instance tagged `user.box=1` (pre-0.4.0 boxes
|
A box is an ordinary Incus instance tagged `user.box=1` (pre-0.4.0 boxes
|
||||||
carry `user.claudebox=1`, honored forever). box wraps the box lifecycle and the isolation model — not all of Incus. It owns a command
|
carry `user.claudebox=1`, honored forever). box wraps the box lifecycle and
|
||||||
|
the isolation model — not all of Incus. It owns a command
|
||||||
when it must enforce something Incus can't see: that tag (it will not stop,
|
when it must enforce something Incus can't see: that tag (it will not stop,
|
||||||
rename or delete an instance it didn't mint), the isolation stack, or the
|
rename or delete an instance it didn't mint), the isolation stack, or the
|
||||||
creds-free snapshot workflow. For everything else, there's the door:
|
creds-free snapshot workflow. For everything else, there's the door:
|
||||||
|
|
@ -227,9 +228,9 @@ diagnoses the host faults that have actually happened: a wedged Incus daemon,
|
||||||
a dnsmasq that silently isn't serving, a VPN resolver that boxes would
|
a dnsmasq that silently isn't serving, a VPN resolver that boxes would
|
||||||
inherit.
|
inherit.
|
||||||
|
|
||||||
## Recipes: the `.claudebox/` convention
|
## Recipes: the `.box/` convention
|
||||||
|
|
||||||
A repo that wants to be easy to stand up in a box ships an optional `.claudebox/`
|
A repo that wants to be easy to stand up in a box ships an optional `.box/`
|
||||||
folder — a runbook Claude reads and follows (install deps, start services,
|
folder — a runbook Claude reads and follows (install deps, start services,
|
||||||
template env, seed data, smoke-test). It is agent-facing documentation, not a
|
template env, seed data, smoke-test). It is agent-facing documentation, not a
|
||||||
host-executed script. See [docs/box-recipe.md](docs/box-recipe.md).
|
host-executed script. See [docs/box-recipe.md](docs/box-recipe.md).
|
||||||
|
|
@ -237,9 +238,9 @@ host-executed script. See [docs/box-recipe.md](docs/box-recipe.md).
|
||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
~/.local/share/box/host/teardown-host.sh # boxes, network, ACL, profile, firewall
|
box teardown-host # boxes, network, ACL, profile, firewall
|
||||||
~/.local/share/box/host/teardown-host.sh --purge-incus # ...and Incus itself
|
box teardown-host --purge-incus # ...and Incus itself
|
||||||
rm -rf ~/.local/share/box ~/.local/bin/box # the CLI
|
rm -rf ~/.local/share/box ~/.local/bin/box # the CLI itself
|
||||||
```
|
```
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
|
||||||
51
bin/box
51
bin/box
|
|
@ -53,6 +53,9 @@ CMDS=(
|
||||||
"expose^<box> <port> [<host-port>] | --list | --remove <port>^box^Forward a box port to the host's loopback — see a dev server^fn:cmd_expose^"
|
"expose^<box> <port> [<host-port>] | --list | --remove <port>^box^Forward a box port to the host's loopback — see a dev server^fn:cmd_expose^"
|
||||||
"incus^<box> -- <args...>^box^Escape hatch: run any incus command against a box^fn:cmd_incus^"
|
"incus^<box> -- <args...>^box^Escape hatch: run any incus command against a box^fn:cmd_incus^"
|
||||||
"doctor^[--fix | --pin-dns]^^Is this host fit to mint boxes? Diagnose the daemon, network, DNS, isolation^fn:cmd_doctor^"
|
"doctor^[--fix | --pin-dns]^^Is this host fit to mint boxes? Diagnose the daemon, network, DNS, isolation^fn:cmd_doctor^"
|
||||||
|
"setup-host^^^One-time host setup: Incus, the boxnet stack, the profile, the firewall^fn:cmd_setup_host^"
|
||||||
|
"teardown-host^[--purge-incus]^^Remove the box host stack (both name generations)^fn:cmd_teardown_host^"
|
||||||
|
"migrate-host^--box <n> | --all-boxes | --retire-legacy^^Move a host from the pre-0.4.0 stack onto box^fn:cmd_migrate_host^"
|
||||||
"status^^^Deprecated alias for 'list'^fn:cmd_status^"
|
"status^^^Deprecated alias for 'list'^fn:cmd_status^"
|
||||||
"help^[<command>]^^This help, or 'box help <command>' for one command^fn:cmd_help^"
|
"help^[<command>]^^This help, or 'box help <command>' for one command^fn:cmd_help^"
|
||||||
)
|
)
|
||||||
|
|
@ -119,7 +122,7 @@ EOF
|
||||||
local r v sum
|
local r v sum
|
||||||
for r in "${CMDS[@]}"; do
|
for r in "${CMDS[@]}"; do
|
||||||
IFS='^' read -r v _ _ sum _ _ <<<"$r"
|
IFS='^' read -r v _ _ sum _ _ <<<"$r"
|
||||||
printf ' %-9s %s\n' "$v" "$sum"
|
printf ' %-13s %s\n' "$v" "$sum"
|
||||||
done
|
done
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
|
|
||||||
|
|
@ -371,6 +374,37 @@ cloud-init error that names none of them.
|
||||||
Exit 0 = clean; 1 = problems found (each printed with its fix). Read-only
|
Exit 0 = clean; 1 = problems found (each printed with its fix). Read-only
|
||||||
unless --fix or --pin-dns is given.
|
unless --fix or --pin-dns is given.
|
||||||
EOF
|
EOF
|
||||||
|
;;
|
||||||
|
setup-host) cat <<'EOF'
|
||||||
|
Prepare this host to mint boxes — one time. Installs Incus and builds the
|
||||||
|
isolation stack: the boxnet NAT bridge (resolver pinned), the box-isolate
|
||||||
|
ACL, the box-net profile, and the firewall rules, all re-applied at boot.
|
||||||
|
Idempotent — safe to re-run after a box upgrade to pick up stack changes.
|
||||||
|
|
||||||
|
If it has to add you to the incus-admin group it will say so and exit; log
|
||||||
|
back in (or 'sg incus-admin') and run it again.
|
||||||
|
|
||||||
|
box setup-host
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
teardown-host) cat <<'EOF'
|
||||||
|
Remove the box host stack — all boxes, the boxnet/claudenet networks, the
|
||||||
|
ACLs, the profiles, and the firewall rules of BOTH name generations (current
|
||||||
|
and pre-0.4.0). Asks first.
|
||||||
|
|
||||||
|
box teardown-host # the stack; leaves Incus installed
|
||||||
|
box teardown-host --purge-incus # ...and uninstall Incus too
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
migrate-host) cat <<'EOF'
|
||||||
|
Move a host from the pre-0.4.0 'claudebox' stack onto 'box'. Re-homing
|
||||||
|
preserves a box's authed state (no re-login) — it only re-tags and reassigns
|
||||||
|
the profile, then verifies the box works on its new network leg.
|
||||||
|
|
||||||
|
box migrate-host --box <name> # re-home one legacy box
|
||||||
|
box migrate-host --all-boxes # re-home every legacy box
|
||||||
|
box migrate-host --retire-legacy # remove the old stack (once no legacy box remains)
|
||||||
|
EOF
|
||||||
;;
|
;;
|
||||||
status) cat <<'EOF'
|
status) cat <<'EOF'
|
||||||
Deprecated alias for 'box list'. It ignored the <box> argument it
|
Deprecated alias for 'box list'. It ignored the <box> argument it
|
||||||
|
|
@ -424,6 +458,8 @@ while [ $# -gt 0 ]; do
|
||||||
if [ "$cmd" = doctor ]; then args+=("$1"); shift; continue; fi
|
if [ "$cmd" = doctor ]; then args+=("$1"); shift; continue; fi
|
||||||
# expose's own flags (--list, --remove) are positional to it, not box's
|
# expose's own flags (--list, --remove) are positional to it, not box's
|
||||||
if [ "$cmd" = expose ]; then args+=("$1"); shift; continue; fi
|
if [ "$cmd" = expose ]; then args+=("$1"); shift; continue; fi
|
||||||
|
# the host verbs delegate their flags to the scripts they wrap
|
||||||
|
case "$cmd" in setup-host|teardown-host|migrate-host) args+=("$1"); shift; continue ;; esac
|
||||||
if [ "$cmd" = exec ] || [ "$cmd" = incus ]; then
|
if [ "$cmd" = exec ] || [ "$cmd" = incus ]; then
|
||||||
usage_error "unknown option: $1 — a command's own flags go after --, as in '$(synopsis_of "$cmd")'"
|
usage_error "unknown option: $1 — a command's own flags go after --, as in '$(synopsis_of "$cmd")'"
|
||||||
fi
|
fi
|
||||||
|
|
@ -843,6 +879,19 @@ cmd_doctor() {
|
||||||
exec bash "$script" "${args[@]}"
|
exec bash "$script" "${args[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The host lifecycle scripts, as first-class verbs — nobody should have to know
|
||||||
|
# where the install tree keeps its scripts. Each execs the installed script
|
||||||
|
# with its flags passed through; the script owns its own behavior (setup-host's
|
||||||
|
# incus-admin re-login dance, teardown's confirmation, migrate's per-box work).
|
||||||
|
host_script() { # $1 = script basename under host/
|
||||||
|
local script="$root/host/$1"
|
||||||
|
[ -f "$script" ] || die "$1 not found at $script — re-run install.sh"
|
||||||
|
exec bash "$script" "${args[@]}"
|
||||||
|
}
|
||||||
|
cmd_setup_host() { host_script setup-host.sh; }
|
||||||
|
cmd_teardown_host() { host_script teardown-host.sh; }
|
||||||
|
cmd_migrate_host() { host_script migrate-host.sh; }
|
||||||
|
|
||||||
cmd_help() { show_help "${args[0]:-}"; }
|
cmd_help() { show_help "${args[0]:-}"; }
|
||||||
|
|
||||||
# The escape hatch. The box is resolved and tag-checked; everything else is
|
# The escape hatch. The box is resolved and tag-checked; everything else is
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ design decisions.
|
||||||
|
|
||||||
- **The tool** mints isolated boxes with Claude installed but **unauthenticated**.
|
- **The tool** mints isolated boxes with Claude installed but **unauthenticated**.
|
||||||
It knows nothing about projects, secrets, recipes, or memory.
|
It knows nothing about projects, secrets, recipes, or memory.
|
||||||
- **The agent** (Claude Code, inside the box) reads an optional `.claudebox/`
|
- **The agent** (Claude Code, inside the box) reads an optional `.box/`
|
||||||
runbook in a cloned repo and acts on it. The recipe's consumer is the
|
runbook in a cloned repo and acts on it. The recipe's consumer is the
|
||||||
reasoning agent, not host machinery.
|
reasoning agent, not host machinery.
|
||||||
|
|
||||||
|
|
@ -45,10 +45,10 @@ Log in once → snapshot → spin up authed boxes from it.
|
||||||
|
|
||||||
cloud-init installs a global `~/.claude/CLAUDE.md` in every box telling Claude it
|
cloud-init installs a global `~/.claude/CLAUDE.md` in every box telling Claude it
|
||||||
is running in a box (trust-less, ephemeral, creds-free) and to treat a
|
is running in a box (trust-less, ephemeral, creds-free) and to treat a
|
||||||
repo's `.claudebox/` folder as its bootstrap runbook. No "tell it" step, no host
|
repo's `.box/` folder as its bootstrap runbook. No "tell it" step, no host
|
||||||
execution.
|
execution.
|
||||||
|
|
||||||
## `.claudebox/` is optional, agent-facing documentation
|
## `.box/` is optional, agent-facing documentation
|
||||||
|
|
||||||
Not host-executed shell. A repo that wants to be easy to stand up in a sandbox
|
Not host-executed shell. A repo that wants to be easy to stand up in a sandbox
|
||||||
ships a runbook (prose + optional scripts the agent may run). A repo that does
|
ships a runbook (prose + optional scripts the agent may run). A repo that does
|
||||||
|
|
@ -56,12 +56,12 @@ not, you set up by hand. The tool enforces no contract; there is no `install`.
|
||||||
|
|
||||||
## What box owns, and what it doesn't
|
## What box owns, and what it doesn't
|
||||||
|
|
||||||
Boxes are ordinary Incus instances, tagged `user.claudebox=1`. That makes every
|
Boxes are ordinary Incus instances, tagged `user.box=1`. That makes every
|
||||||
Incus verb a candidate feature request — `rename`, `info`, `file push`, on
|
Incus verb a candidate feature request — `rename`, `info`, `file push`, on
|
||||||
forever — and wrapping them one at a time grows a worse `incus`. The rule:
|
forever — and wrapping them one at a time grows a worse `incus`. The rule:
|
||||||
|
|
||||||
> **box owns a command when it must enforce an invariant Incus cannot see:**
|
> **box owns a command when it must enforce an invariant Incus cannot see:**
|
||||||
> the `user.claudebox=1` boundary (never touch an instance we didn't mint), the
|
> the `user.box=1` boundary (never touch an instance we didn't mint), the
|
||||||
> isolation stack (`claude-dev` profile + `claudenet` + ACL), or the creds-free
|
> isolation stack (`claude-dev` profile + `claudenet` + ACL), or the creds-free
|
||||||
> snapshot→clone workflow. Everything else is Incus's job.
|
> snapshot→clone workflow. Everything else is Incus's job.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# The `.claudebox/` convention
|
# The `.box/` convention
|
||||||
|
|
||||||
`box` mints trust-less, creds-free, isolated VMs with Claude Code already
|
`box` mints trust-less, creds-free, isolated VMs with Claude Code already
|
||||||
installed (`box new/shell/snapshot/restore/exec/down/start/rm/status`). The
|
installed (`box new/shell/snapshot/restore/exec/down/start/rm/status`). The
|
||||||
|
|
@ -6,13 +6,17 @@ tool knows **nothing** about your project. There is no `install` step and no
|
||||||
host-run setup script.
|
host-run setup script.
|
||||||
|
|
||||||
A project makes itself easy to stand up inside a box by shipping an optional
|
A project makes itself easy to stand up inside a box by shipping an optional
|
||||||
`.claudebox/` folder. This folder is **agent-facing documentation** — read and
|
`.box/` folder. This folder is **agent-facing documentation** — read and
|
||||||
acted on by Claude Code (the reasoning agent) running inside the box. It is not
|
acted on by Claude Code (the reasoning agent) running inside the box. It is not
|
||||||
shell that the host executes.
|
shell that the host executes.
|
||||||
|
|
||||||
|
> The folder was named `.claudebox/` before the 0.5.0 rename. Repos that still
|
||||||
|
> ship `.claudebox/` keep working — the agent is told to read either — but new
|
||||||
|
> projects should use `.box/`.
|
||||||
|
|
||||||
## What it is / what it is not
|
## What it is / what it is not
|
||||||
|
|
||||||
- **Optional.** No `.claudebox/` is a perfectly valid state.
|
- **Optional.** No `.box/` is a perfectly valid state.
|
||||||
- **Agent-facing.** You are writing instructions to a reasoning agent, not a
|
- **Agent-facing.** You are writing instructions to a reasoning agent, not a
|
||||||
machine. Prose is fine; the agent adapts.
|
machine. Prose is fine; the agent adapts.
|
||||||
- **Not a host contract.** The host never parses, sources, or runs anything in
|
- **Not a host contract.** The host never parses, sources, or runs anything in
|
||||||
|
|
@ -23,34 +27,34 @@ shell that the host executes.
|
||||||
## How it's consumed
|
## How it's consumed
|
||||||
|
|
||||||
Every box ships a global `~/.claude/CLAUDE.md` telling Claude it is inside a
|
Every box ships a global `~/.claude/CLAUDE.md` telling Claude it is inside a
|
||||||
box and to treat a repo's `.claudebox/` folder as its bootstrap runbook.
|
box and to treat a repo's `.box/` folder as its bootstrap runbook.
|
||||||
So the whole flow is:
|
So the whole flow is:
|
||||||
|
|
||||||
```
|
```
|
||||||
box new # get a box
|
box new # get a box
|
||||||
box shell # get in
|
box shell # get in
|
||||||
git clone <repo> && cd <repo>
|
git clone <repo> && cd <repo>
|
||||||
claude # Claude reads .claudebox/ and brings the project up
|
claude # Claude reads .box/ and brings the project up
|
||||||
```
|
```
|
||||||
|
|
||||||
The operator can also just say: *"set this project up per .claudebox"*.
|
The operator can also just say: *"set this project up per .box"*.
|
||||||
|
|
||||||
## Suggested contents (all optional)
|
## Suggested contents (all optional)
|
||||||
|
|
||||||
Author everything here for a reasoning agent.
|
Author everything here for a reasoning agent.
|
||||||
|
|
||||||
- **`.claudebox/SETUP.md`** — the prose runbook. Prerequisites, how to install
|
- **`.box/SETUP.md`** — the prose runbook. Prerequisites, how to install
|
||||||
deps, how to start services, how to template the env, how to seed data, and
|
deps, how to start services, how to template the env, how to seed data, and
|
||||||
how to smoke-test. Written as instructions to Claude.
|
how to smoke-test. Written as instructions to Claude.
|
||||||
- **Helper scripts** (e.g. `.claudebox/dev-up.sh`) that the runbook tells Claude
|
- **Helper scripts** (e.g. `.box/dev-up.sh`) that the runbook tells Claude
|
||||||
to run. Claude decides to run them; the host never does.
|
to run. Claude decides to run them; the host never does.
|
||||||
- **`.claudebox/env.template`** — example env the runbook explains how to fill.
|
- **`.box/env.template`** — example env the runbook explains how to fill.
|
||||||
Staging values the operator pastes in. **Never commit real secrets.**
|
Staging values the operator pastes in. **Never commit real secrets.**
|
||||||
- **`.claudebox/compose.yml`** — optional services the runbook starts.
|
- **`.box/compose.yml`** — optional services the runbook starts.
|
||||||
|
|
||||||
## Worked example
|
## Worked example
|
||||||
|
|
||||||
A minimal `.claudebox/SETUP.md` for a Node + Postgres app:
|
A minimal `.box/SETUP.md` for a Node + Postgres app:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Setup
|
# Setup
|
||||||
|
|
@ -58,8 +62,8 @@ A minimal `.claudebox/SETUP.md` for a Node + Postgres app:
|
||||||
This is a Node service backed by Postgres.
|
This is a Node service backed by Postgres.
|
||||||
|
|
||||||
1. Install deps: `npm ci`
|
1. Install deps: `npm ci`
|
||||||
2. Start Postgres: `docker compose -f .claudebox/compose.yml up -d`
|
2. Start Postgres: `docker compose -f .box/compose.yml up -d`
|
||||||
3. Create the env file: copy `.claudebox/env.template` to `.env` and ask the
|
3. Create the env file: copy `.box/env.template` to `.env` and ask the
|
||||||
operator to fill in `DATABASE_URL` and `API_KEY` (staging values).
|
operator to fill in `DATABASE_URL` and `API_KEY` (staging values).
|
||||||
4. Run migrations: `npm run migrate`
|
4. Run migrations: `npm run migrate`
|
||||||
5. Start the app: `npm run dev`
|
5. Start the app: `npm run dev`
|
||||||
|
|
@ -72,7 +76,7 @@ That's it — Claude reads it top to bottom and adapts if reality differs.
|
||||||
|
|
||||||
- **Keep it declarative and resilient.** State intent and steps; let the agent
|
- **Keep it declarative and resilient.** State intent and steps; let the agent
|
||||||
adapt when the repo has drifted. Don't hard-code brittle assumptions.
|
adapt when the repo has drifted. Don't hard-code brittle assumptions.
|
||||||
- **Never put real credentials in `.claudebox/`.** Templates and staging
|
- **Never put real credentials in `.box/`.** Templates and staging
|
||||||
placeholders only. The operator pastes real values at runtime.
|
placeholders only. The operator pastes real values at runtime.
|
||||||
- **No `.claudebox/` is fine.** The operator can stand the project up by hand,
|
- **No `.box/` is fine.** The operator can stand the project up by hand,
|
||||||
or let Claude infer the steps from the repo's `README` / `CLAUDE.md`.
|
or let Claude infer the steps from the repo's `README` / `CLAUDE.md`.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ if incus network show boxnet >/dev/null 2>&1; then
|
||||||
ok "dns.mode = none — a box cannot enumerate its siblings by name"
|
ok "dns.mode = none — a box cannot enumerate its siblings by name"
|
||||||
else
|
else
|
||||||
no "dns.mode = ${dns:-<unset>} — a box can RESOLVE its siblings' names and addresses"
|
no "dns.mode = ${dns:-<unset>} — a box can RESOLVE its siblings' names and addresses"
|
||||||
inf "fix: re-run ~/.local/share/box/host/setup-host.sh"
|
inf "fix: re-run: box setup-host"
|
||||||
[ "$FIX" = 1 ] && { incus network set boxnet dns.mode=none && inf "set: dns.mode=none"; }
|
[ "$FIX" = 1 ] && { incus network set boxnet dns.mode=none && inf "set: dns.mode=none"; }
|
||||||
fi
|
fi
|
||||||
inf "ipv4.address = $(incus network get boxnet ipv4.address 2>/dev/null)"
|
inf "ipv4.address = $(incus network get boxnet ipv4.address 2>/dev/null)"
|
||||||
|
|
@ -110,7 +110,7 @@ if [ -n "$PROFILES" ]; then
|
||||||
no "$p: security.port_isolation is NOT set — BOXES CAN REACH EACH OTHER"
|
no "$p: security.port_isolation is NOT set — BOXES CAN REACH EACH OTHER"
|
||||||
inf "an L3 ACL cannot do this: two boxes on one bridge are on the same L2"
|
inf "an L3 ACL cannot do this: two boxes on one bridge are on the same L2"
|
||||||
inf "segment, so their frames are switched, never routed past the ACL."
|
inf "segment, so their frames are switched, never routed past the ACL."
|
||||||
inf "fix: re-run ~/.local/share/box/host/setup-host.sh"
|
inf "fix: re-run: box setup-host"
|
||||||
fi
|
fi
|
||||||
for k in security.mac_filtering security.ipv4_filtering; do
|
for k in security.mac_filtering security.ipv4_filtering; do
|
||||||
v="$(incus profile device get "$p" eth0 "$k" 2>/dev/null)"
|
v="$(incus profile device get "$p" eth0 "$k" 2>/dev/null)"
|
||||||
|
|
@ -192,7 +192,7 @@ else
|
||||||
no "the host resolves via a CGNAT/Tailscale resolver ($hostns), and boxes INHERIT it — see issue #33"
|
no "the host resolves via a CGNAT/Tailscale resolver ($hostns), and boxes INHERIT it — see issue #33"
|
||||||
inf "· box DNS breaks whenever the tailnet's resolver does (this is what kills cold mints)"
|
inf "· box DNS breaks whenever the tailnet's resolver does (this is what kills cold mints)"
|
||||||
inf "· and tailnet names RESOLVE from inside a box, though its ACL blocks connecting to them"
|
inf "· and tailnet names RESOLVE from inside a box, though its ACL blocks connecting to them"
|
||||||
inf "fix: re-run ~/.local/share/box/host/setup-host.sh (it pins the resolver)"
|
inf "fix: re-run: box setup-host (it pins the resolver)"
|
||||||
inf " or quick-test the pin alone: bash drill/doctor.sh --pin-dns"
|
inf " or quick-test the pin alone: bash drill/doctor.sh --pin-dns"
|
||||||
else
|
else
|
||||||
inf "boxes inherit the host's resolver (unpinned — setup-host.sh pins this now; re-run it)"
|
inf "boxes inherit the host's resolver (unpinned — setup-host.sh pins this now; re-run it)"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ write_files:
|
||||||
- **Disposable.** Nothing here is backed up. State is discarded when the box is
|
- **Disposable.** Nothing here is backed up. State is discarded when the box is
|
||||||
removed; the operator persists work via git push and via `box snapshot`.
|
removed; the operator persists work via git push and via `box snapshot`.
|
||||||
- **Bootstrap runbook.** If the repository you are working in contains a
|
- **Bootstrap runbook.** If the repository you are working in contains a
|
||||||
`.claudebox/` folder, read it as your setup runbook — how to install
|
`.box/` folder (older repos may use `.claudebox/`), read it as your setup runbook — how to install
|
||||||
dependencies, start services, template environment files, seed data, and
|
dependencies, start services, template environment files, seed data, and
|
||||||
smoke-test — and follow it. It is documentation for you, not a script the
|
smoke-test — and follow it. It is documentation for you, not a script the
|
||||||
host runs.
|
host runs.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ write_files:
|
||||||
- **Disposable.** Nothing here is backed up. State is discarded when the box
|
- **Disposable.** Nothing here is backed up. State is discarded when the box
|
||||||
is removed; the operator persists work via git push and via `box snapshot`.
|
is removed; the operator persists work via git push and via `box snapshot`.
|
||||||
- **Bootstrap runbook.** If the repository you are working in contains a
|
- **Bootstrap runbook.** If the repository you are working in contains a
|
||||||
`.claudebox/` folder, read it as your setup runbook — how to install
|
`.box/` folder (older repos may use `.claudebox/`), read it as your setup runbook — how to install
|
||||||
dependencies, start services, template environment files, seed data, and
|
dependencies, start services, template environment files, seed data, and
|
||||||
smoke-test — and follow it. It is documentation for you, not a script the
|
smoke-test — and follow it. It is documentation for you, not a script the
|
||||||
host runs.
|
host runs.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ write_files:
|
||||||
- **Disposable.** Nothing here is backed up. State is discarded when the box
|
- **Disposable.** Nothing here is backed up. State is discarded when the box
|
||||||
is removed; the operator persists work via git push and via `box snapshot`.
|
is removed; the operator persists work via git push and via `box snapshot`.
|
||||||
- **Bootstrap runbook.** If the repository you are working in contains a
|
- **Bootstrap runbook.** If the repository you are working in contains a
|
||||||
`.claudebox/` folder, read it as your setup runbook — how to install
|
`.box/` folder (older repos may use `.claudebox/`), read it as your setup runbook — how to install
|
||||||
dependencies, start services, template environment files, seed data, and
|
dependencies, start services, template environment files, seed data, and
|
||||||
smoke-test — and follow it. It is documentation for you, not a script the
|
smoke-test — and follow it. It is documentation for you, not a script the
|
||||||
host runs.
|
host runs.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue