Users, with their own settings, and a list of where each is signed in
The login is a user name and a password. The old single password becomes the account named after the OS user on first boot, along with its sessions and the machine-wide look. Settings gains Users (add, remove, reset a password — your own password confirms) and Sessions (device, IP, last seen; revoke one or sign out everywhere else). Theme, accents and the dashboard layout follow the account; the refresh rate, graph history and alerts stay the machine's. Pairing a machine takes a user on it. Log lines name the actor. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
db214ebc2b
commit
2b54a06821
@@ -295,8 +295,33 @@ something long in one, switch away and back, refresh — it's all still there.
|
||||
3.18/3.20 (OpenRC + doas, non-root), plus the four lab hosts. Not done: an OpenRC
|
||||
backend for the Services tab; runit/s6/sysvinit service files; public hosting for
|
||||
`install.sh` (the Gitea repo is private, so the one-liner needs `HAMLAB_TOKEN`).
|
||||
- **Auth hardening** — WebAuthn passkeys (phone Face ID), TOTP fallback, session
|
||||
list + revoke, optional NPM access list restricting to WireGuard/LAN.
|
||||
- **Users, sessions and settings that follow the account** — **shipped 2026-09-22**
|
||||
(`internal/api/users.go`, `config.User`, `store.Session`/`UserState`, `Login.tsx`,
|
||||
`Settings.tsx`). The login is a user name and a password. `config.json` holds `users`
|
||||
(name, argon2id hash, created, last seen); the old single `password_hash` becomes the
|
||||
account named after the OS user the console runs as on first boot, its signed-in
|
||||
sessions become that account's, and the machine-wide theme/accents/dashboard become
|
||||
theirs — nothing to redo after the upgrade. A session carries its user, an ID, when it
|
||||
was made and last seen, the IP and the browser; **Settings → Sessions** lists yours
|
||||
("Safari on iPhone · 10.0.0.5 · seen 2 min ago"), revokes one, or signs out everywhere
|
||||
else. **Settings → Users** lists accounts with their session counts, adds one, removes
|
||||
one (their sessions and settings go too; never yourself, never the last), and resets
|
||||
another's password (they are signed out); removing and resetting take your own
|
||||
password first. Everyone signed in can do everything — a household console, not a
|
||||
tenancy — and each machine keeps its own accounts: a hub reaches a node with its token,
|
||||
so pairing (Machines → Add) now takes a user and password on the node, the user
|
||||
optional while it has one account. Per account: `ui` (theme, accents) and `dashboard`
|
||||
in `state.json` under `users.<name>`. Still the machine's: refresh rate (it is how
|
||||
often every machine samples — it rides in the UI prefs object for the client but is
|
||||
stored once, machine-wide), graph history, alert rules, terminals (the shells run as
|
||||
one OS user anyway). A login attempt for a missing user takes as long as a wrong
|
||||
password, so the form can't list accounts. Every write's log line now names the
|
||||
account or token (`core@10.0.0.5 via …`), and Access tokens say who issued them. CLI:
|
||||
`hamlab passwd [user]` (makes the account if new; reads stdin when piped), `hamlab
|
||||
users`, `hamlab user-rm <user>`; `hamlab install` names the first account after the
|
||||
service user. Not done: a read-only viewer role.
|
||||
- **Auth hardening** — WebAuthn passkeys (phone Face ID), TOTP fallback, optional NPM
|
||||
access list restricting to WireGuard/LAN.
|
||||
- **Files tab** — **shipped 2026-09-18** (`internal/files`, `Files.tsx`): browse from
|
||||
`/` with breadcrumbs, an editable path box, sort, hidden-file toggle and a filter;
|
||||
rows show type icon, size, modified, perms and owner, link targets and broken links.
|
||||
@@ -350,31 +375,6 @@ something long in one, switch away and back, refresh — it's all still there.
|
||||
Downgrades are already refused by the installer's `vnum()` comparison; the updater
|
||||
should use the same rule.
|
||||
|
||||
- **Users, with their own settings** — asked for 2026-09-22, not started. Today Ham Lab is
|
||||
one shared password: `config.json` holds a single `password_hash`, a login mints a session
|
||||
token in `state.json`, and everything a person can set — theme, accent colours, refresh
|
||||
rate, dashboard layout and its widgets — is stored per *machine*, not per person
|
||||
(`store.State.UI`, `.Dashboard`). So two people share one look, one layout, and one
|
||||
audit trail. Wanted: a username and password field, an account each, and settings that
|
||||
follow the account.
|
||||
- **The login.** A `users` list in `config.json` (name, hash, created, last seen) beside
|
||||
the existing hash, which becomes the first user on upgrade so nothing breaks. The
|
||||
session already exists and is already a token → just carry which user it belongs to.
|
||||
Argon2id or bcrypt as now; a login attempt must take the same time whether or not the
|
||||
user exists, or the form becomes a way to enumerate accounts.
|
||||
- **The settings.** `UI` and `Dashboard` move from one blob to one per user. Machine
|
||||
settings — the sampling rate, the history window, alert rules — stay machine-wide:
|
||||
they are facts about the box, not a preference, and the fleet is held to them.
|
||||
- **Decisions to make when it is built:** whether anyone can do anything (simplest, and
|
||||
honest for a household) or whether there is a read-only account — a viewer who can see
|
||||
graphs but not open a terminal, remove a container or shut a machine down; who may add
|
||||
and remove users; what happens to a user's terminals when their account is removed; and
|
||||
whether the hub's users are the fleet's, pushed like the sampling rate, or each machine
|
||||
keeps its own (each machine keeps its own is less surprising and less to go wrong —
|
||||
a hub already authenticates to a node with a token, not a password).
|
||||
- **Worth doing at the same time:** the log line every write already carries an IP should
|
||||
carry the user, and Settings → Access tokens should say which account issued each one.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Idle-session reaping policy (never / after N days) — leaning never; it's a
|
||||
|
||||
Reference in New Issue
Block a user