rig/commands
dan-claude-bot a950569832 feat: users apply grants the box tier, not just the socket
Role `box` resolved to exactly one action, `usermod -aG incus`. That is
the socket — step 1 of the five `box grant` performs. Without the other
four (the user-<uid> project, its narrowing to boxnet and only boxnet,
the snapshot and backup allowances clone and `box export` ride, and the
shipped box-net profile installed into that project) the user's first
`box new` refuses for want of a box-net profile, so apply's promise —
the users file is the fleet's source of truth — was not kept for this
role. Worse, until an admin arrived by hand the user held an `incus`
membership with no converged project, and incus-user would lazily hand
them a stock unhardened NAT bridge: a state box's own contract forbids.

On host=yes apply now calls `box grant <user>` per box-role user. rig
calls box's grant rather than reimplementing four fifths of it — the
"rig never installs Incus" boundary is about installation, not
invocation, and grant is already script-callable: idempotent,
root-or-sudo, stdin-pinned, with its own run-as-the-user touch.

Three decisions the code carries in comment form:

- Ordering. The call sits after `useradd` (grant opens with a getent
  passwd and refuses an unknown account) and after the other groups, so
  a user whose grant fails still lands with everything rig owns outright.

- Failure granularity, split the way the host= guard beside it already
  splits. A missing box CLI on host=yes dies, like the missing incus
  group: a broken VM host, not a per-user accident. A per-user grant
  failure warns and continues — one box-role user somewhere in the fleet
  must not stop apply everywhere VMs don't live. host=no and marker-less
  boxes keep their existing skip-with-warning untouched.

- The group ADD is deferred to grant, while `incus` stays in the wanted
  set so the exact-convergence loop never strips a box-role user's
  socket. Grant's rollback only reaches a membership that run added, so
  rig opening the socket first would leave a failed grant unable to
  close it. And grant is the authority on whether the group belongs at
  all: for an incus-admin member it deliberately does not add `incus`.

An incus-admin member is warned, never fatal: box grant refuses them
today, which heavy-duty/box#99 fixes box-side with no rig change needed.

Closes #49

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 19:10:29 +00:00
..
lib fix(users): the host= marker gates the box role, not the incus group 2026-07-19 17:29:20 +00:00
bootstrap-tenant.sh review r1: staging tolerates only the workload guest; dockerd must answer; one CLI capture 2026-07-18 20:18:15 +00:00
bootstrap.sh fix: headless credential prompts refuse loudly, naming their variable (#42) 2026-07-19 12:15:08 +00:00
coolify-backup-install.sh fix(bootstrap): gate the host-set-up claim on 'box doctor', not on PATH 2026-07-18 16:59:43 +00:00
coolify-install.sh fix(bootstrap): gate the host-set-up claim on 'box doctor', not on PATH 2026-07-18 16:59:43 +00:00
db.sh feat(db): bring ad-hoc dump/restore on-box as rig db 2026-07-17 15:16:35 +00:00
runner-install.sh fix: headless credential prompts refuse loudly, naming their variable (#42) 2026-07-19 12:15:08 +00:00
runner-remove.sh fix: headless credential prompts refuse loudly, naming their variable (#42) 2026-07-19 12:15:08 +00:00
runner-repoint.sh fix: headless credential prompts refuse loudly, naming their variable (#42) 2026-07-19 12:15:08 +00:00
runner-status.sh fix(runner): install refuses a box registered to another repo 2026-07-13 14:57:28 +00:00
users-apply.sh feat: users apply grants the box tier, not just the socket 2026-07-19 19:10:29 +00:00
users-close-root.sh fix(close-root): the gate judges AllowGroups/DenyGroups too — same door, other hinge 2026-07-18 17:17:39 +00:00
users-status.sh fix(users): review findings — invoker gate, real SSH revocation, StrictModes-shaped close-root gate, trait-aware box role 2026-07-17 20:01:19 +00:00