An empty users file converges a root-only box as silently as forgetting one #57

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

An empty users file converges a root-only box as silently as forgetting one

Found by claude-bot-andresmgsl reviewing #54.

The gap

#51 made --users required so that bootstrapping a box without deciding
about its people becomes impossible — the complete path is the default path,
and skipping it is a deliberate --no-users.

An empty or comments-only users file walks straight through that. It
passes pre-flight (the parser is happy — zero users is not a parse error),
apply converges nothing, and the box comes up root-only. Which is exactly
the outcome --no-users exists to make explicit, reached by the path that
was supposed to guarantee the opposite.

So the ambiguity the required flag was built to kill survives in a narrower
form: --users ./users with an empty file and --no-users produce the same
box, but only one of them says so.

The sharper case

A truncated users file against a box that already has operators does not
just converge nothing — it revokes every one of them. That is apply's
documented and correct drop-semantics (expire the account, rename
authorized_keys, keep the home), and it warns per user, so it is loud
rather than silent. But it is reached by a file that a moment's > in the
wrong place produces, on a command whose other failure modes were
deliberately made to fail before spending anything.

Proposed fix

Catch the zero-user parse in bootstrap's pre-flight — where the file is
already being parsed for validation, before apt, hostname, or key spend —
and refuse, naming --no-users as the way to say root-only out loud.

Deliberately narrow: this is about rig bootstrap's contract, not
rig users apply's. A standalone rig users apply against an emptied file
is a real de-provisioning operation and must stay possible.

Open question

Does the same refusal belong on rig users apply itself, at least as a
confirmation when the file is empty and the /etc/rig/users ledger is
not? Revoking every operator on a box is a big enough action that "the file
was empty" is worth distinguishing from "the file said to remove them."
Filed as a question, not a proposal.

Context

Not a regression — #54 introduced the required flag, and this is the corner
it does not cover. Raised at review as non-blocking, deferred here rather
than restarting an approved round.

# An empty users file converges a root-only box as silently as forgetting one Found by `claude-bot-andresmgsl` reviewing #54. ## The gap #51 made `--users` required so that bootstrapping a box without deciding about its people becomes impossible — the complete path is the default path, and skipping it is a deliberate `--no-users`. An **empty or comments-only** users file walks straight through that. It passes pre-flight (the parser is happy — zero users is not a parse error), apply converges nothing, and the box comes up root-only. Which is exactly the outcome `--no-users` exists to make explicit, reached by the path that was supposed to guarantee the opposite. So the ambiguity the required flag was built to kill survives in a narrower form: `--users ./users` with an empty file and `--no-users` produce the same box, but only one of them says so. ## The sharper case A **truncated** users file against a box that already has operators does not just converge nothing — it revokes every one of them. That is apply's documented and correct drop-semantics (expire the account, rename `authorized_keys`, keep the home), and it warns per user, so it is loud rather than silent. But it is reached by a file that a moment's `>` in the wrong place produces, on a command whose other failure modes were deliberately made to fail before spending anything. ## Proposed fix Catch the zero-user parse in bootstrap's pre-flight — where the file is already being parsed for validation, before apt, hostname, or key spend — and refuse, naming `--no-users` as the way to say root-only out loud. Deliberately narrow: this is about `rig bootstrap`'s contract, not `rig users apply`'s. A standalone `rig users apply` against an emptied file is a real de-provisioning operation and must stay possible. ## Open question Does the same refusal belong on `rig users apply` itself, at least as a confirmation when the file is empty *and* the `/etc/rig/users` ledger is not? Revoking every operator on a box is a big enough action that "the file was empty" is worth distinguishing from "the file said to remove them." Filed as a question, not a proposal. ## Context Not a regression — #54 introduced the required flag, and this is the corner it does not cover. Raised at review as non-blocking, deferred here rather than restarting an approved round.
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#57
No description provided.