From 040ea41dbc7d61d33c1de46bcc7a61980b217444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 12 Aug 2026 22:58:35 +0000 Subject: [PATCH] per-user linux accounts are not optional any more MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OFFICER_OS_USERS is gone. The platform behaves as it always would have with the flag on, and there is nothing to enable. Six conditionals, five of which were dead weight — provisionOsAccount, deprovisionOsAccount and the create/delete paths each opened with an early "not enabled on this server" return, and the API told the frontend whether to render the Linux controls at all. Those go, along with the 'disabled' DeprovisionResult stage, which nothing can produce now. The sixth is the one with teeth. assertSecretsClosed opened with `if (!OS_USERS_ENABLED) return`, described in its own comment as "a no-op when the feature is off, so an existing install is unaffected until the owner opts in". It is now unconditional: the server refuses to boot while any .env in the project root is group- or world-readable. A member's shell reading .env and printing JWT_SECRET was confirmed exploitable when this check was written, and a prerequisite that only holds when somebody remembers to set a variable is not a prerequisite. Nothing to remove on the environment side — the flag was never in .env.example or in the setup script. Not typechecked: node_modules is empty in this tree and installs are frozen, so tsgo could not run. All six files parse under `bun build --no-bundle`, and the changes are deletions of dead branches plus one removed early return. Formatted with prettier 3.9.6 via bunx rather than the pinned resolution, for the same reason; its one unrelated reformat was reverted by hand. Co-Authored-By: Claude Opus 5 (1M context) --- docs/per-user-linux-accounts.md | 7 ++++++- .../Settings/UserManagement/UsersSection.tsx | 4 +--- src/servers/api/users/create-user.ts | 5 +---- src/servers/api/users/manage-users.ts | 6 +----- src/servers/api/users/provision-os.ts | 8 ++------ src/servers/os-user-deprovision.ts | 13 ++----------- src/servers/os-user.ts | 11 ++++------- 7 files changed, 17 insertions(+), 37 deletions(-) diff --git a/docs/per-user-linux-accounts.md b/docs/per-user-linux-accounts.md index 4fcef7f3..016376be 100644 --- a/docs/per-user-linux-accounts.md +++ b/docs/per-user-linux-accounts.md @@ -279,7 +279,12 @@ Three bugs surfaced only by running it: also *unreachable*. 3. **`platform/.env` was readable, and printing `JWT_SECRET` from a member's shell was confirmed.** This is the prerequisite above, demonstrated. It is now a boot check (`assertSecretsClosed`) that refuses to - start with `OFFICER_OS_USERS` on while any `.env` in the project root is group- or world-readable. + start while any `.env` in the project root is group- or world-readable. + + That check was itself conditional on `OFFICER_OS_USERS` until 2026-08-12, which meant the guarantee was + opt-in. The flag is gone and the check is unconditional: a security prerequisite that only holds when + somebody remembers to set a variable is not a prerequisite. Per-user Linux accounts are now simply what + the platform does, so there is nothing to enable and nothing to forget. **`cd $HOME/..` succeeding is correct and worth being precise about.** `711` grants traversal, so `cd` works while `ls` does not — they can stand in the directory and see nothing in it. Beyond that, a real diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/UsersSection.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/UsersSection.tsx index 9bc9bcd9..1f142494 100644 --- a/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/UsersSection.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/UsersSection.tsx @@ -32,8 +32,6 @@ type ManagedUser = { type UsersResponse = { users: ManagedUser[]; - /** False on a host without per-user Linux accounts, where those controls would only ever refuse. */ - osUsersEnabled: boolean; /** Every role, for displaying the owner's own value. */ roles: string[]; /** Roles the server will accept in a write. Excludes the owner role — both write paths refuse it. */ @@ -183,7 +181,7 @@ export const UsersSection = () => { {/* Create or repair the Linux side. Offered for anyone lacking an account (backfill) and for anyone who has one (retry after fixing a host problem, or replace their key) — the underlying operation is idempotent, so there is no state where pressing it is wrong. */} - {data.osUsersEnabled && !user.isOwner && ( + {!user.isOwner && (