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:
Mitchell-Johnson
2026-09-22 18:20:30 -07:00
co-authored by Claude Fable 5.1
parent db214ebc2b
commit 2b54a06821
15 changed files with 1059 additions and 170 deletions
+2 -2
View File
@@ -198,7 +198,7 @@ func (m *Manager) List() []Status {
// Add pairs with a node: pin its certificate (trust on first use), trade the node's
// password for a token named after this hub, and store the node. The password is
// used once and never kept.
func (m *Manager) Add(ctx context.Context, name, rawURL, password string) (Status, error) {
func (m *Manager) Add(ctx context.Context, name, rawURL, username, password string) (Status, error) {
if !nameRe.MatchString(name) {
return Status{}, errors.New("name: lowercase letters, digits and dashes only")
}
@@ -221,7 +221,7 @@ func (m *Manager) Add(ctx context.Context, name, rawURL, password string) (Statu
}
n.Fingerprint = fp
}
body, _ := json.Marshal(map[string]string{"password": password, "name": "hub:" + m.hubName})
body, _ := json.Marshal(map[string]string{"username": username, "password": password, "name": "hub:" + m.hubName})
req, _ := http.NewRequestWithContext(ctx, "POST", n.URL+"/api/tokens", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, err := (&http.Client{Transport: transportFor(n), Timeout: 15 * time.Second}).Do(req)