Goal
A browser that was logged in should stay logged in after the daemon restarts or
is re-launched. Today every restart sends every viewer back to the password
prompt.
Why it is not simply a bug
The cookie is fine — it is issued with Max-Age=86400 and the browser still
holds it. What is gone is the server's memory of it:
// src/web/common/auth.rs
pub struct SessionStore {
tokens: Mutex<HashSet<String>>,
}
and that is documented as deliberate:
Tokens are opaque 256-bit random strings and stay valid until the process
exits (a fresh launch invalidates all sessions, which is the desired
behaviour for a dev tool).
So this issue asks to change a recorded decision, not to fix a defect. Moving
the credential to localStorage is not the answer either: the cookie is
HttpOnly on purpose, docs/architecture/web.md confines localStorage to
first-paint caches, and the server would still not recognise the token after a
restart.
What the change has to carry with it
Process exit is currently the backstop that makes two other accepted risks
tolerable. Removing it means picking up both:
- No absolute TTL (
docs/architecture/web.md, listed under accepted residual risks). Sessions
live until the process ends. If a restart no longer clears them and nothing
expires them, tokens become valid indefinitely. A persistent session needs an
expiry.
- Logout must stay a revocation, not a suggestion —
SessionStore::revoke
exists precisely so that clearing the cookie is not the only defence. A purely
stateless signed cookie (HMAC over an expiry, no server state) cannot revoke,
so it does not satisfy this on its own; it needs a deny list, which puts the
state back.
Two more constraints, if tokens land on disk:
- They are credentials, so the file wants owner-only permissions.
PermissionsExt
is Unix-only, so per .agents/rules/guardrails.md the mode belongs behind the
src/platform/ seam, with whatever Windows cannot enforce written down.
docs/web-viewer.md already steers operators away from a plaintext secret on
disk (hashed_password over password). Long-lived tokens in a file push the
other way and should be a conscious trade, not a side effect.
Worth noting for anyone weighing the exposure: the viewer has no TLS and the
cookie carries no Secure flag, both recorded. On a non-loopback bind the
token already crosses the network in plaintext; persisting it widens the window
from "until this process exits" to "until it expires".
Not proposing an implementation here
Ways this could go — persisted store with a TTL, signed cookie plus a deny list,
or leaving the model alone and letting a reverse proxy / SSH tunnel own
authentication — differ in what they give up. Since the current behaviour is a
documented decision, the decision should be settled (and
docs/architecture/web.md updated) before code moves.
Goal
A browser that was logged in should stay logged in after the daemon restarts or
is re-launched. Today every restart sends every viewer back to the password
prompt.
Why it is not simply a bug
The cookie is fine — it is issued with
Max-Age=86400and the browser stillholds it. What is gone is the server's memory of it:
and that is documented as deliberate:
So this issue asks to change a recorded decision, not to fix a defect. Moving
the credential to
localStorageis not the answer either: the cookie isHttpOnlyon purpose,docs/architecture/web.mdconfineslocalStoragetofirst-paint caches, and the server would still not recognise the token after a
restart.
What the change has to carry with it
Process exit is currently the backstop that makes two other accepted risks
tolerable. Removing it means picking up both:
docs/architecture/web.md, listed under accepted residual risks). Sessionslive until the process ends. If a restart no longer clears them and nothing
expires them, tokens become valid indefinitely. A persistent session needs an
expiry.
SessionStore::revokeexists precisely so that clearing the cookie is not the only defence. A purely
stateless signed cookie (HMAC over an expiry, no server state) cannot revoke,
so it does not satisfy this on its own; it needs a deny list, which puts the
state back.
Two more constraints, if tokens land on disk:
PermissionsExtis Unix-only, so per
.agents/rules/guardrails.mdthe mode belongs behind thesrc/platform/seam, with whatever Windows cannot enforce written down.docs/web-viewer.mdalready steers operators away from a plaintext secret ondisk (
hashed_passwordoverpassword). Long-lived tokens in a file push theother way and should be a conscious trade, not a side effect.
Worth noting for anyone weighing the exposure: the viewer has no TLS and the
cookie carries no
Secureflag, both recorded. On a non-loopbackbindthetoken already crosses the network in plaintext; persisting it widens the window
from "until this process exits" to "until it expires".
Not proposing an implementation here
Ways this could go — persisted store with a TTL, signed cookie plus a deny list,
or leaving the model alone and letting a reverse proxy / SSH tunnel own
authentication — differ in what they give up. Since the current behaviour is a
documented decision, the decision should be settled (and
docs/architecture/web.mdupdated) before code moves.