chore: bump version for cleanness
This commit is contained in:
parent
014d662bd1
commit
77599ab3ee
3 changed files with 28 additions and 24 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -5,6 +5,8 @@ which records not just what changed but what each drill run proved.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 0.6.0 — 2026-07-18
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **The restricted tier: multi-user hosts** (#74, redesigning #72) — an admin
|
- **The restricted tier: multi-user hosts** (#74, redesigning #72) — an admin
|
||||||
|
|
@ -12,7 +14,7 @@ which records not just what changed but what each drill run proved.
|
||||||
hardened `boxnet`, seeing nobody else's; `box revoke <user>` takes it back
|
hardened `boxnet`, seeing nobody else's; `box revoke <user>` takes it back
|
||||||
(`--purge` deletes their world, and asserts the absence). The tier rides
|
(`--purge` deletes their world, and asserts the absence). The tier rides
|
||||||
incus-user, whose defaults miss box's contract three measured ways (Debian
|
incus-user, whose defaults miss box's contract three measured ways (Debian
|
||||||
13 / Incus 6.0.4): a private *unhardened* NAT bridge per user, snapshots
|
13 / Incus 6.0.4): a private _unhardened_ NAT bridge per user, snapshots
|
||||||
blocked, the `box-net` profile invisible — so grant is an idempotent
|
blocked, the `box-net` profile invisible — so grant is an idempotent
|
||||||
convergence: project narrowed to `boxnet` **and only boxnet** (listing the
|
convergence: project narrowed to `boxnet` **and only boxnet** (listing the
|
||||||
private bridge too, the obvious fix, would keep an unhardened network one
|
private bridge too, the obvious fix, would keep an unhardened network one
|
||||||
|
|
@ -29,7 +31,7 @@ which records not just what changed but what each drill run proved.
|
||||||
`multiuser.sh --container`), so every PR proves the tier's semantics
|
`multiuser.sh --container`), so every PR proves the tier's semantics
|
||||||
against a live daemon, not a mock. The VM trust boundary itself remains a
|
against a live daemon, not a mock. The VM trust boundary itself remains a
|
||||||
real-hardware ritual, like the full drill.
|
real-hardware ritual, like the full drill.
|
||||||
- **Global / root install** (#71) — run as root, box installs *once* to
|
- **Global / root install** (#71) — run as root, box installs _once_ to
|
||||||
`/opt/box` (world-readable) with the `box` symlink on `/usr/local/bin`, so
|
`/opt/box` (world-readable) with the `box` symlink on `/usr/local/bin`, so
|
||||||
every operator on a shared host runs the same tree. Per-user installs are
|
every operator on a shared host runs the same tree. Per-user installs are
|
||||||
unchanged (`$HOME/.local`); `BOX_HOME`/`BOX_BIN` still override. A per-user
|
unchanged (`$HOME/.local`); `BOX_HOME`/`BOX_BIN` still override. A per-user
|
||||||
|
|
@ -53,7 +55,7 @@ which records not just what changed but what each drill run proved.
|
||||||
restricted user; fixed for every tier, and the rehearsal + a grep-guard in
|
restricted user; fixed for every tier, and the rehearsal + a grep-guard in
|
||||||
`test/cli.sh` now hold it.
|
`test/cli.sh` now hold it.
|
||||||
- **`box tmux` works on every template** (#65) — `box tmux` runs
|
- **`box tmux` works on every template** (#65) — `box tmux` runs
|
||||||
`tmux new-session` *inside* the box, but the templates did not install tmux, so
|
`tmux new-session` _inside_ the box, but the templates did not install tmux, so
|
||||||
it failed with `tmux: command not found`. `tmux` is now in each template's
|
it failed with `tmux: command not found`. `tmux` is now in each template's
|
||||||
cloud-init package list (`blank`/`claude`/`codex`/`grok`).
|
cloud-init package list (`blank`/`claude`/`codex`/`grok`).
|
||||||
|
|
||||||
|
|
@ -103,11 +105,11 @@ which records not just what changed but what each drill run proved.
|
||||||
It now asserts the post-install stack in-group before touching the host, and
|
It now asserts the post-install stack in-group before touching the host, and
|
||||||
runs `setup-host` exactly once more — after the clean, which deliberately
|
runs `setup-host` exactly once more — after the clean, which deliberately
|
||||||
unsets `dns.mode` and so has to be converged back. `DRILL_OWNS_SETUP=1`
|
unsets `dns.mode` and so has to be converged back. `DRILL_OWNS_SETUP=1`
|
||||||
hands sequencing back to the drill. Pre-setup tripwires now read *before*
|
hands sequencing back to the drill. Pre-setup tripwires now read _before_
|
||||||
`install.sh`, since that is what triggers setup now.
|
`install.sh`, since that is what triggers setup now.
|
||||||
- **`install.sh` asks, sets up the host, and no-ops on re-run** (#64) — it now
|
- **`install.sh` asks, sets up the host, and no-ops on re-run** (#64) — it now
|
||||||
prompts *"Install box?"*, then on a fresh host installs the tree and asks a
|
prompts _"Install box?"_, then on a fresh host installs the tree and asks a
|
||||||
second question, *"Set up this machine as a box host now?"*, running the whole
|
second question, _"Set up this machine as a box host now?"_, running the whole
|
||||||
isolation stack if you say yes (previously it only printed a warning and left
|
isolation stack if you say yes (previously it only printed a warning and left
|
||||||
you a command, so the install reported success and `box new` died on a host
|
you a command, so the install reported success and `box new` died on a host
|
||||||
with no Incus). Prompts read `/dev/tty`, since under `curl | bash` the script
|
with no Incus). Prompts read `/dev/tty`, since under `curl | bash` the script
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -8,7 +8,7 @@ destroying it loses nothing you didn't push.
|
||||||
|
|
||||||
**Strictly creds-free.** A box ships with everything installed and **no**
|
**Strictly creds-free.** A box ships with everything installed and **no**
|
||||||
credentials — no agent token, no git PAT, nothing. You authenticate
|
credentials — no agent token, no git PAT, nothing. You authenticate
|
||||||
interactively *inside* the box. The tool never stores or injects a secret. That
|
interactively _inside_ the box. The tool never stores or injects a secret. That
|
||||||
means there's nothing shared or committed, so it's safe for multiple operators
|
means there's nothing shared or committed, so it's safe for multiple operators
|
||||||
out of the box.
|
out of the box.
|
||||||
|
|
||||||
|
|
@ -23,6 +23,8 @@ runbook that the box's coding agent 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
|
step and no host-run setup. See [docs/box-design.md](docs/box-design.md) for the
|
||||||
design rationale.
|
design rationale.
|
||||||
|
|
||||||
|
> **0.6.0**: multi-user support.
|
||||||
|
|
||||||
> **0.5.0**: two new templates (`codex`, `grok`), `box expose` — a
|
> **0.5.0**: two new templates (`codex`, `grok`), `box expose` — a
|
||||||
> loopback-only door to a box port, for seeing a dev server — and the host
|
> loopback-only door to a box port, for seeing a dev server — and the host
|
||||||
> lifecycle as first-class verbs: `box setup-host`, `box teardown-host`, and
|
> lifecycle as first-class verbs: `box setup-host`, `box teardown-host`, and
|
||||||
|
|
@ -52,7 +54,7 @@ rebuild the stack under your boxes. Upgrading is therefore explicit: uninstall
|
||||||
what you have and install fresh. Preserve any boxes first — `box down <box>`,
|
what you have and install fresh. Preserve any boxes first — `box down <box>`,
|
||||||
copy out anything you need (a portable `box export` is
|
copy out anything you need (a portable `box export` is
|
||||||
[#70](https://github.com/heavy-duty/box/issues/70)), then `box rm <box>`
|
[#70](https://github.com/heavy-duty/box/issues/70)), then `box rm <box>`
|
||||||
(which deletes the box *and* its snapshots) — then:
|
(which deletes the box _and_ its snapshots) — then:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf ~/.local/share/box ~/.local/bin/box # uninstall
|
rm -rf ~/.local/share/box ~/.local/bin/box # uninstall
|
||||||
|
|
@ -67,7 +69,7 @@ declines the host-setup step.
|
||||||
### Global vs per-user install
|
### Global vs per-user install
|
||||||
|
|
||||||
Where box lands depends on **who runs the installer**, because on a shared host
|
Where box lands depends on **who runs the installer**, because on a shared host
|
||||||
box's tree is *executed by other users* — so it cannot hide in one user's home:
|
box's tree is _executed by other users_ — so it cannot hide in one user's home:
|
||||||
|
|
||||||
- **As root → global.** The tree goes to `/opt/box` (world-readable) and the
|
- **As root → global.** The tree goes to `/opt/box` (world-readable) and the
|
||||||
`box` symlink to `/usr/local/bin` (already on every login `PATH`). One
|
`box` symlink to `/usr/local/bin` (already on every login `PATH`). One
|
||||||
|
|
@ -114,7 +116,7 @@ the machine — so box layers a second tier on
|
||||||
[incus-user](https://linuxcontainers.org/incus/docs/main/projects/):
|
[incus-user](https://linuxcontainers.org/incus/docs/main/projects/):
|
||||||
|
|
||||||
| tier | who | what they hold |
|
| tier | who | what they hold |
|
||||||
|---|---|---|
|
| -------------- | -------------------------------- | ----------------------------------------------------------------- |
|
||||||
| **admin** | root, or the `incus-admin` group | everything: all boxes, the stack, `setup-host`, `expose`, `grant` |
|
| **admin** | root, or the `incus-admin` group | everything: all boxes, the stack, `setup-host`, `expose`, `grant` |
|
||||||
| **restricted** | the `incus` group | their **own** boxes only, on the same hardened network |
|
| **restricted** | the `incus` group | their **own** boxes only, on the same hardened network |
|
||||||
| none | everyone else | no socket, nothing |
|
| none | everyone else | no socket, nothing |
|
||||||
|
|
@ -132,7 +134,7 @@ box revoke dev1 --purge # ...or end their sessions and delete everything the
|
||||||
`grant` is an idempotent convergence, not a flag flip, because incus-user's
|
`grant` is an idempotent convergence, not a flag flip, because incus-user's
|
||||||
defaults miss box's contract three ways (measured on Debian 13 / Incus 6.0.4,
|
defaults miss box's contract three ways (measured on Debian 13 / Incus 6.0.4,
|
||||||
see [the plan doc](docs/plans/2026-07-18-restricted-tier.md)): it pins each
|
see [the plan doc](docs/plans/2026-07-18-restricted-tier.md)): it pins each
|
||||||
user to a private *unhardened* NAT bridge, it blocks snapshots, and it cannot
|
user to a private _unhardened_ NAT bridge, it blocks snapshots, and it cannot
|
||||||
see the `box-net` profile. Granting rewires all three: the user's project is
|
see the `box-net` profile. Granting rewires all three: the user's project is
|
||||||
restricted to `boxnet` **and only boxnet** — the hardened network is not their
|
restricted to `boxnet` **and only boxnet** — the hardened network is not their
|
||||||
default placement but the only one their certificate can express — snapshots
|
default placement but the only one their certificate can express — snapshots
|
||||||
|
|
@ -176,7 +178,7 @@ No coding agent is special — each is one template among several, and adding
|
||||||
another is just another directory. What ships today:
|
another is just another directory. What ships today:
|
||||||
|
|
||||||
| Template | What's in it |
|
| Template | What's in it |
|
||||||
| --- | --- |
|
| -------- | --------------------------------------------------------- |
|
||||||
| `blank` | Bare Debian 13 — same isolation, no tooling. The default. |
|
| `blank` | Bare Debian 13 — same isolation, no tooling. The default. |
|
||||||
| `claude` | Claude Code, creds-free — where this project started |
|
| `claude` | Claude Code, creds-free — where this project started |
|
||||||
| `codex` | OpenAI Codex CLI, creds-free |
|
| `codex` | OpenAI Codex CLI, creds-free |
|
||||||
|
|
@ -303,7 +305,7 @@ command surface is a table.
|
||||||
## Isolation
|
## Isolation
|
||||||
|
|
||||||
The contract: **a box reaches the public internet and nothing else.** Not the
|
The contract: **a box reaches the public internet and nothing else.** Not the
|
||||||
host, not your LAN, not another box, not even another box's *name*. What
|
host, not your LAN, not another box, not even another box's _name_. What
|
||||||
enforces it, layer by layer:
|
enforces it, layer by layer:
|
||||||
|
|
||||||
- **Dedicated NAT bridge** `boxnet`, IPv6 off. Every rule below is
|
- **Dedicated NAT bridge** `boxnet`, IPv6 off. Every rule below is
|
||||||
|
|
@ -311,7 +313,7 @@ enforces it, layer by layer:
|
||||||
contract, not a default.
|
contract, not a default.
|
||||||
- **`box-isolate` ACL** — drops all egress to private space (RFC1918,
|
- **`box-isolate` ACL** — drops all egress to private space (RFC1918,
|
||||||
CGNAT, link-local), with a single carve-out to the gateway so DNS works.
|
CGNAT, link-local), with a single carve-out to the gateway so DNS works.
|
||||||
- **Sibling isolation, at L2** — two boxes on one bridge are *switched*,
|
- **Sibling isolation, at L2** — two boxes on one bridge are _switched_,
|
||||||
never routed, so no L3 rule can separate them (learned the hard way; see
|
never routed, so no L3 rule can separate them (learned the hard way; see
|
||||||
below). `security.port_isolation` on every box NIC plus an nft
|
below). `security.port_isolation` on every box NIC plus an nft
|
||||||
bridge-family drop mean box A cannot exchange frames with box B at all.
|
bridge-family drop mean box A cannot exchange frames with box B at all.
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.5.0
|
0.6.0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue