secrets: rotate dashboard session token + auth signing secret #352

Merged
hermes merged 1 commit from task/dash-cred-rot into main 2026-09-17 18:55:55 -06:00
Collaborator

Rotates the two dashboard credentials the operator generates itself. Deliberately not rotated: dashboard.basic_auth's scrypt password_hash — it encodes a human login, and the new plaintext would need an out-of-band handoff, so it stays until the owner asks for it.

  • hermes-dashboard-token — same 64-char shape. The backend's launch wrapper (hermes-backend-launch) reads /run/secrets/hermes-dashboard-token, exports it as HERMES_DASHBOARD_SESSION_TOKEN, and deliberately never puts it on a command line — so a rotated token reaches the running server only through a restart. restartUnits = [ "hermes-backend.service" ] declared on both declaring hosts (vpn: owner hermes; public: owner hermes-bot).
  • hermes-env's HERMES_DASHBOARD_BASIC_AUTH_SECRET — 43-char urlsafe, read once at start by hermes-backend. public's hermes-env gains the same restartUnits pair vpn's already carries (vpn's came with #351).

Verification: round-trip asserts before the move (new values exact, password_hash line unchanged, only 2 of 43 top-level keys differ, recipients byte-identical). Post-deploy: the rendered secrets on each host contain the new values and none of the old, the password hash line is byte-identical to before, hermes-backend restarted on vpn and public, and the running process's HERMES_DASHBOARD_SESSION_TOKEN (via /proc/<pid>/environ) hashes to the new token — the value actually in effect, not just the value on disk.

Impact: clients that present the old session token (desktop/phone) need to re-pair; the dashboard password is unchanged, so basic-auth logins keep working.

Rotates the two dashboard credentials the operator generates itself. Deliberately **not** rotated: `dashboard.basic_auth`'s scrypt `password_hash` — it encodes a human login, and the new plaintext would need an out-of-band handoff, so it stays until the owner asks for it. * `hermes-dashboard-token` — same 64-char shape. The backend's launch wrapper (`hermes-backend-launch`) reads `/run/secrets/hermes-dashboard-token`, exports it as `HERMES_DASHBOARD_SESSION_TOKEN`, and deliberately never puts it on a command line — so a rotated token reaches the running server only through a restart. `restartUnits = [ "hermes-backend.service" ]` declared on both declaring hosts (vpn: owner `hermes`; public: owner `hermes-bot`). * `hermes-env`'s `HERMES_DASHBOARD_BASIC_AUTH_SECRET` — 43-char urlsafe, read once at start by `hermes-backend`. public's `hermes-env` gains the same `restartUnits` pair vpn's already carries (vpn's came with #351). **Verification:** round-trip asserts before the move (new values exact, `password_hash` line unchanged, only 2 of 43 top-level keys differ, recipients byte-identical). Post-deploy: the rendered secrets on each host contain the new values and none of the old, the password hash line is byte-identical to before, `hermes-backend` restarted on vpn and public, and the running process's `HERMES_DASHBOARD_SESSION_TOKEN` (via `/proc/<pid>/environ`) hashes to the new token — the value actually in effect, not just the value on disk. **Impact:** clients that present the old session token (desktop/phone) need to re-pair; the dashboard password is unchanged, so basic-auth logins keep working.
secrets: rotate the dashboard session token and auth signing secret
All checks were successful
NixOS Build Check / Scope changed deployable hosts (pull_request) Successful in 2s
NixOS Build Check / Eval giga (scope + liveness + offline fallback) (pull_request) Successful in 2s
NixOS Build Check / Flake input regression guard (pull_request) Successful in 3s
PR base guard / Base drift / conflict check (pull_request) Successful in 3s
Secret Scan / gitleaks scan (pull_request) Successful in 7s
NixOS Build Check / Build mail closure (on mail) (pull_request) Successful in 13s
NixOS Build Check / Build vpn closure (on vpn) (pull_request) Successful in 22s
NixOS Build Check / Eval giga natively (authoritative, on giga) (pull_request) Successful in 36s
NixOS Build Check / Build public closure (on public) (pull_request) Successful in 1m15s
01512b3b87
Rotates the two dashboard credentials the operator generates itself, leaving
dashboard.basic_auth's scrypt password_hash untouched on purpose (that one is
a human login: rotating it would need an out-of-band handoff of the new
plaintext). The session token and the HMAC signing secret are machine
credentials, so they rotate silently.

  - hermes-dashboard-token (64 chars, same shape as before): the launch wrapper
    copies it out of the file into HERMES_DASHBOARD_SESSION_TOKEN at process
    start and never puts it on a command line, so the running server keeps the
    old value until it restarts. Declare restartUnits = hermes-backend.service
    on vpn and public, which is the only path a rotation has into the server.
  - hermes-env's HERMES_DASHBOARD_BASIC_AUTH_SECRET (43-char urlsafe): read
    once at start by hermes-backend, so public's hermes-env gains the same
    restartUnits pair vpn already carries.

All three changed keys verified byte-identical round-trip before the move;
2 of 43 top-level keys differ, the password_hash and every recipient are
untouched. Clients that present the old session token must re-pair.
hermes merged commit 0fe30ccbda into main 2026-09-17 18:55:55 -06:00
Sign in to join this conversation.
No reviewers
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
ymrtech/nix-config!352
No description provided.