docs: box help matches what grant/revoke now mutate (#101 review)

Round 2 changed the incus-admin path from a skipped group step to a real
`usermod -aG incus`, but `bin/box` help still described the superseded
design — telling operators the group step is a no-op that will not happen
(it does) and that a bare revoke has no membership to drop (it drops one).

The help is the pre-run contract: it is what an operator reads to decide
whether to run the command at all, so prose that denies a mutation the
command performs is the same class of defect the rest of this PR exists to
remove. Both sections now describe the current behavior, and `help revoke`
carries the consequence the operator would otherwise discover: once `incus`
is gone, a later `gpasswd -d <user> incus-admin` lands them in NEITHER
group, so grant's "no re-grant needed" holds only while they hold `incus`.

Pinned in both directions — the current sentence must be present and the
superseded one absent — so the prose cannot drift from the scripts again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dan-claude-bot 2026-07-19 17:13:24 +00:00
parent 8598cf96fe
commit 7067fb03b0
2 changed files with 30 additions and 6 deletions

20
bin/box
View file

@ -542,9 +542,12 @@ What it converges, idempotently (safe to re-run, and re-run after upgrades):
rides snapshots, 'box export' rides backups — #70) rides snapshots, 'box export' rides backups — #70)
· installs the box-net profile into their project · installs the box-net profile into their project
An incus-admin member is provisioned too, not refused (#99): the group step An incus-admin member is provisioned too, not refused (#99): they are added
is a reported no-op — they already hold more — and everything else converges, to 'incus' like anyone else — not a new privilege, since incus-admin already
so they finally have a project of their own. What it is not is a confinement: opens the daemon, but the key to a FILE, because incus-user's socket is group
'incus' mode 0660 and nothing below can provision them without it. Everything
else converges, so they finally have a project of their own. What it is not
is a confinement:
incus-admin wins at the socket, so the restrictions are a default placement incus-admin wins at the socket, so the restrictions are a default placement
they can step outside at will, and their own client keeps resolving to the they can step outside at will, and their own client keeps resolving to the
admin socket (and the default project) until incus-admin is taken away. The admin socket (and the default project) until incus-admin is taken away. The
@ -569,9 +572,14 @@ recreate the project, unhardened, afterwards — measured, not theoretical),
then their boxes, images, project, private bridge and trust-store then their boxes, images, project, private bridge and trust-store
certificate are removed — irreversible, so it asks first. certificate are removed — irreversible, so it asks first.
On an incus-admin member there is no membership to drop, so a bare revoke is On an incus-admin member a bare revoke takes back the 'incus' membership that
a named no-op and --purge unmakes the provisioning without ending any access: grant added — reported as 'partial:', because it ends no access: incus-admin
only 'gpasswd -d <user> incus-admin' does that, and revoke says so. still opens every project on this host. (One who was never granted is a named
no-op instead.) Mind what that leaves behind: with 'incus' gone, a later
'gpasswd -d <user> incus-admin' drops them into NEITHER group and their ready
project becomes unreachable — grant's "no re-grant needed" holds only while
they still hold 'incus'. --purge unmakes the provisioning the same way. Only
'gpasswd -d <user> incus-admin' ends their access, and revoke says so.
box revoke dev1 # take the tier; their boxes keep running box revoke dev1 # take the tier; their boxes keep running
box revoke dev1 --purge # ...or end their sessions and delete everything box revoke dev1 --purge # ...or end their sessions and delete everything

View file

@ -479,6 +479,22 @@ check "box revoke with no user exits 2 (via the CLI table)" 2 "usage: box revoke
check "help grant names the hardened network" 0 "boxnet" "$BOX" help grant check "help grant names the hardened network" 0 "boxnet" "$BOX" help grant
check "help revoke names --purge" 0 "purge" "$BOX" help revoke check "help revoke names --purge" 0 "purge" "$BOX" help revoke
# The help is the PRE-RUN CONTRACT: an operator reads it to decide whether to
# run the command at all, so it must not promise a mutation that will not
# happen (or deny one that will). Round 1 of #101 changed what grant/revoke
# mutate for an incus-admin member and left this prose describing the
# superseded design — these pins are why that cannot happen silently again.
# Both directions: the current sentence must be present, and the superseded
# one must be gone.
check "help grant: the admin member's group step is a real add, not a no-op" \
0 "like anyone else" "$BOX" help grant
check "help revoke: a bare revoke of a granted admin member is 'partial:'" \
0 "partial:" "$BOX" help revoke
check "help grant no longer calls the admin group step a no-op" 0 "" \
bash -c '! "'"$BOX"'" help grant | grep -q "reported no-op"'
check "help revoke no longer claims there is no membership to drop" 0 "" \
bash -c '! "'"$BOX"'" help revoke | grep -q "no membership to drop"'
# Load-bearing lines a daemon-free run cannot exercise — grepped so a deleted # Load-bearing lines a daemon-free run cannot exercise — grepped so a deleted
# guard cannot ship green (the house test discipline). # guard cannot ship green (the house test discipline).
# The expose guard must fire before ANY incus call in cmd_expose: line order. # The expose guard must fire before ANY incus call in cmd_expose: line order.