0014. Auth hardening: token store, rotation, multi-operator, TLS cookie¶
- Status: accepted
- Date: 2026-06-04
Context and Problem Statement¶
ADR-0008 shipped deliberately dev-grade auth: agent tokens from an env map and a single
plaintext operator token. The backlog called for per-agent unique tokens with rotation, a
real store, hashed comparison, and a Secure login cookie under TLS — needed in particular
because the agent installer download (ADR-0012/WS5) must mint a per-agent token.
Considered Options¶
- SQLite-backed, hashed per-agent token store + rotation endpoint; multi-operator tokens;
Securecookie under TLS. (Incremental hardening, no new infra.) - Full IdP/OAuth for operators and mTLS for agents.
- Leave as-is and rely solely on a network layer (Tailscale/Cloudflare Access).
Decision Outcome¶
Chosen option: incremental hardening in the app, composable with a network layer later.
- Agent tokens:
AgentTokenStore(aiosqlite, sameKENNY_DB_PATH) stores onlysha256(token);verifyis constant-time;create_or_rotate(agent_id)mints asecrets.token_urlsafe(32)and returns the plaintext once. The env/dev map is seeded on first connect (INSERT OR IGNORE) so existing agents keep working. The/agent/wshandshake authenticates through the store (register_async), preserving the 4401 close. - Rotation endpoint:
POST /api/agents/{id}/token(operator-auth) returns a fresh token once — this is what the installer download calls to bind a per-agent token. - Operator tokens: accept a set —
KENNY_OPERATOR_TOKENplus optional comma-separatedKENNY_OPERATOR_TOKENS— compared constant-time across all candidates. - Cookie:
Secureis set whenKENNY_TLS=1; otherwise unchanged so local http works.
Consequences¶
- Good, because tokens are unique, rotatable, and never stored in clear; the installer has a clean mint path; multiple operators are possible.
- Good, because it layers under Tailscale/Cloudflare Access (documented as a Compose
tlsprofile / deployment variant) for defense in depth. - Bad, because hashing is
sha256(fast) not a slow KDF, and the login cookie still carries the operator token — acceptable for this scale; a slow KDF / session tokens are future work.
More Information¶
- Implementation:
kenny-server/kenny_server/tokenstore.py,registry.py,tunnel.py,auth.py,webui/__init__.py,main.py. Builds on ADR-0008.