users apply grants the incus socket but never the tier — it should call box grant #49

Closed
opened 2026-07-19 16:05:05 +00:00 by dan-claude-bot · 0 comments
dan-claude-bot commented 2026-07-19 16:05:05 +00:00 (Migrated from github.com)

users apply grants the socket but never the tier — it should call box grant

The gap

On a host=yes box, role box in the users file resolves to exactly one
action (commands/users-apply.sh:213):

case ",$roles," in *,box,*) if [ "$INCUS_OK" -eq 1 ]; then want="$want incus"; fi ;; esac

usermod -aG incus <user>. That is step 1 of the five box grant performs
(box/host/grant-user.sh, summarised at box/bin/box:530):

  1. put the user in the incus group ← the only step rig does
  2. create their user-<uid> project by touching incus-user as them
  3. unpin the private bridge (drop eth0 from the project's default profile)
    and set restricted.networks.access boxnetonly boxnet
  4. allow snapshots and backups (incus-user blocks both; clone rides
    snapshots, box export rides backups)
  5. install the shipped box-net profile into their project

rig never invokes box grant — the phrase appears only in prose
(README.md:763, users-apply.sh:171), never as a call.

What it costs

A manual second step, per user, per host, after every apply. The user's
first box new fails closed at box/bin/box:749:

your project has no box-net profile — the restricted tier is granted per user, by an admin: box grant <user>

Good message, but the promise of rig users apply — the users file is the
fleet's source of truth, apply converges to it — is not kept for this role.

A window where the grant is worse than absent. rig adds the user to
incus with no converged project. If they log in and touch incus before
an admin runs box grant, incus-user lazily creates their project pinned to
incusbr-<uid> — which box/docs/plans/2026-07-18-restricted-tier.md
measures as worse than unhardened: NAT on v4 and v6, no ACL, no
dns.mode=none, no port isolation, no resolver pin. box grant converges
an existing project, so the window closes when an admin arrives — but until
then that user holds a door box's contract explicitly forbids.

The README overstates what apply does. The role table at
README.md:749 reads:

| box | Incus restricted tier, no sudo | incus |

The group is the socket. The tier is the project convergence, which apply
does not touch.

Why calling box grant is the right shape

  • rig already hard-depends on box here. users-apply.sh:180 dies with
    "install the box CLI and run box setup-host first" on host=yes with no
    incus group. Calling box grant consumes an interface rig already
    requires.
  • The "rig never installs Incus" boundary is about installation, not
    invocation.
    Deferring to box's own grant path respects that boundary
    harder than reimplementing half of it does.
  • box grant is already script-callable. Documented idempotent (every
    step converges, safe after upgrades); root-or-sudo, which apply already
    is; every incus call </dev/null-pinned so it cannot wedge interactively;
    and step 2 does the run-as-user touch via runuser, so the user never has
    to log in first. It also fails closed — the backout trap strips a group
    membership this run added if a later step trips.

Open decisions

1. Failure granularity. box grant exits 1 on host-level facts (no
boxnet, incus-user.socket unavailable) and per-user facts (project
creation failed). Apply already has the precedent for the split: die on the
host-level precondition, as it does today at users-apply.sh:180; warn and
continue per user — "one box-role user somewhere in the fleet must not stop
apply everywhere VMs don't live."

2. incus-admin members must still be converged. box grant today
refuses them outright (grant-user.sh:69, "they already have the admin
tier; there is nothing tighter to grant"). That conflates permission with
provisioning: an incus-admin member resolves to tier=admin
(box/bin/box:30) and lands in the shared default project — they never
get their own user-<uid> project, their own box-net profile copy, or the
per-project narrowing. Being able to do anything is not the same as being
set up. This has bitten us in practice. Blocked on a box-side change —
heavy-duty/box#99.

3. Runtime. box grant per box-role user per apply run is several incus
calls plus a timeout 60 run-as-user touch. Convergent and bounded, but
apply gets measurably slower on a host with many box-role users, and there
is no cheap "already converged?" probe short of doing the work.

Out of scope

The mirror-image bug on the revoke side — apply's bare gpasswd -d bypassing
box revoke — is #50. Bootstrap taking the users file so this converges at
bring-up is #51.

# `users apply` grants the socket but never the tier — it should call `box grant` ## The gap On a `host=yes` box, role `box` in the users file resolves to exactly one action (`commands/users-apply.sh:213`): ```sh case ",$roles," in *,box,*) if [ "$INCUS_OK" -eq 1 ]; then want="$want incus"; fi ;; esac ``` `usermod -aG incus <user>`. That is step 1 of the five `box grant` performs (`box/host/grant-user.sh`, summarised at `box/bin/box:530`): 1. put the user in the `incus` group ← **the only step rig does** 2. create their `user-<uid>` project by touching incus-user as them 3. unpin the private bridge (drop `eth0` from the project's default profile) and set `restricted.networks.access boxnet` — **only** boxnet 4. allow snapshots and backups (incus-user blocks both; clone rides snapshots, `box export` rides backups) 5. install the shipped `box-net` profile into their project rig never invokes `box grant` — the phrase appears only in prose (`README.md:763`, `users-apply.sh:171`), never as a call. ## What it costs **A manual second step, per user, per host, after every apply.** The user's first `box new` fails closed at `box/bin/box:749`: > `your project has no box-net profile — the restricted tier is granted per user, by an admin: box grant <user>` Good message, but the promise of `rig users apply` — the users file is the fleet's source of truth, apply converges to it — is not kept for this role. **A window where the grant is worse than absent.** rig adds the user to `incus` with no converged project. If they log in and touch `incus` before an admin runs `box grant`, incus-user lazily creates their project pinned to `incusbr-<uid>` — which `box/docs/plans/2026-07-18-restricted-tier.md` measures as *worse than unhardened*: NAT on v4 **and** v6, no ACL, no `dns.mode=none`, no port isolation, no resolver pin. `box grant` converges an existing project, so the window closes when an admin arrives — but until then that user holds a door box's contract explicitly forbids. **The README overstates what apply does.** The role table at `README.md:749` reads: | `box` | Incus **restricted** tier, no sudo | `incus` | The group is the socket. The *tier* is the project convergence, which apply does not touch. ## Why calling `box grant` is the right shape - **rig already hard-depends on box here.** `users-apply.sh:180` dies with "install the box CLI and run `box setup-host` first" on `host=yes` with no `incus` group. Calling `box grant` consumes an interface rig already requires. - **The "rig never installs Incus" boundary is about installation, not invocation.** Deferring to box's own grant path respects that boundary harder than reimplementing half of it does. - **`box grant` is already script-callable.** Documented idempotent (every step converges, safe after upgrades); root-or-sudo, which apply already is; every incus call `</dev/null`-pinned so it cannot wedge interactively; and step 2 does the run-as-user touch via `runuser`, so the user never has to log in first. It also fails closed — the `backout` trap strips a group membership *this run* added if a later step trips. ## Open decisions **1. Failure granularity.** `box grant` exits 1 on host-level facts (no `boxnet`, `incus-user.socket` unavailable) and per-user facts (project creation failed). Apply already has the precedent for the split: die on the host-level precondition, as it does today at `users-apply.sh:180`; warn and continue per user — "one box-role user somewhere in the fleet must not stop apply everywhere VMs don't live." **2. `incus-admin` members must still be converged.** `box grant` today refuses them outright (`grant-user.sh:69`, "they already have the admin tier; there is nothing tighter to grant"). That conflates *permission* with *provisioning*: an `incus-admin` member resolves to `tier=admin` (`box/bin/box:30`) and lands in the **shared default project** — they never get their own `user-<uid>` project, their own `box-net` profile copy, or the per-project narrowing. Being able to do anything is not the same as being set up. This has bitten us in practice. Blocked on a box-side change — heavy-duty/box#99. **3. Runtime.** `box grant` per box-role user per apply run is several incus calls plus a `timeout 60` run-as-user touch. Convergent and bounded, but apply gets measurably slower on a host with many box-role users, and there is no cheap "already converged?" probe short of doing the work. ## Out of scope The mirror-image bug on the revoke side — apply's bare `gpasswd -d` bypassing `box revoke` — is #50. Bootstrap taking the users file so this converges at bring-up is #51.
Sign in to join this conversation.
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/rig#49
No description provided.