0008. Operator authentication¶
- Status: accepted
- Date: 2026-06-04
Context and Problem Statement¶
The operator reaches kenny-server two ways: Claude over the MCP Streamable-HTTP
endpoint (/mcp) and the web dashboard (/ + /api). Until now neither was
authenticated — anyone who could reach the server could drive every tool and read
the fleet dashboard. Agents already authenticate with a per-agent token on
/agent/ws ([ADR-0002]/registry), but the operator side was an open gap noted in
ADR-0006.
Considered Options¶
- Single shared operator bearer token (env
KENNY_OPERATOR_TOKEN), enforced by one ASGI middleware over/mcp,/api, and the UI; cookie login for the browser. - Per-operator identities with a user store / OAuth.
- Delegate entirely to a network layer (Cloudflare Access / Tailscale) and keep the app open behind it.
Decision Outcome¶
Chosen option: "Single shared operator bearer token". For a family-scale, single (or few) operator deployment this is the smallest change that closes the gap and is testable end-to-end:
- A pure-ASGI
OperatorAuthMiddlewarerequires a valid bearer token (header) or cookie on every HTTP request to/mcpand/api, and gates the UI (redirect to/login). It passes WebSocket (/agent/ws) and lifespan scopes through untouched, and passes authorized requests through unbuffered so MCP streaming/SSE is preserved. - Claude sends
Authorization: Bearer <token>to the MCP endpoint. The browser logs in at/login, which sets anHttpOnlycookie carrying the token. - The token comes from
KENNY_OPERATOR_TOKEN; an insecure dev fallback is used with a loud warning when unset, consistent with the agent-token dev fallback.
Consequences¶
- Good, because all three operator surfaces are covered by one mechanism and one secret, with tests for each.
- Good, because it composes with a network layer later (defense in depth).
- Bad, because it is a single shared secret (no per-operator identity, no rotation story yet) and the login cookie carries the token. Acceptable for the current scale; see hardening below.
More Information¶
- Transport security is separate and assumed: serve over
wss/httpsin production so the token is not sent in clear (the dev fallback andws://are for local use only). Set the cookiesecureflag behind TLS. - Hardening backlog: per-operator identities, token rotation, and/or fronting with Cloudflare Access / Tailscale (ADR-0003 backlog).
- Implementation:
kenny-server/kenny_server/auth.py; wired inmain.py.