Authentication
Runne Pass has two independent authentication surfaces:
| Surface | Credential | Transport | Used by |
|---|---|---|---|
| Customer dashboard | JWT in an HttpOnly cookie | Cookie runne_session | Web cabinet, browser |
| Programmatic API | API key rn_live_… | Authorization: Bearer or x-api-key | Servers, scripts, SDKs |
They are not interchangeable: cookie sessions authorize dashboard routes, API keys authorize the external chat/balance surface.
Cookie session (dashboard)
Login and registration
POST /v1/auth/register—{email, password, name}, creates the customer, then auto-logs in and sets the cookie.POST /v1/auth/login—{email, password}, sets the cookie and returns{expires_at}.POST /v1/auth/logout— revokes the session server-side and clears the cookie.GET /v1/auth/me— returns the current customer profile.
Session cookie attributes
| Attribute | Value |
|---|---|
| Name | runne_session |
HttpOnly | yes |
SameSite | Lax |
Secure | only in production (NODE_ENV=production) |
Max-Age | 24h (matches the JWT lifetime) |
Path | / |
The JWT carries customerId, email, isAdmin, and a unique jti.
Server-side revocation
Logout stores the session jti in a Redis denylist keyed bl:sess:{jti} with a TTL equal to the token's remaining lifetime. Every authenticated request re-checks the denylist, so a logged-out session stops working immediately, even though the JWT itself is still cryptographically valid.
API keys (programmatic clients)
Format
rn_live_<keyId>_<secret>keyId— 12 characters, base64url, derived from 9 random bytes.secret— 32 random bytes, base64url.rn_live_prefix marks the key as a live (non-test) key.
Presenting a key
Two equivalent ways:
# Authorization header (preferred)
curl -H 'Authorization: Bearer rn_live_…' …
# x-api-key header
curl -H 'x-api-key: rn_live_…' …Verification
The server parses the key, looks up the row by keyId, and verifies the secret with HMAC-SHA256 against the stored hash. Only the hash is stored — the raw secret is never persisted.
Dual-secret rotation
Key verification supports two server secrets:
API_KEY_SERVER_SECRET— the current secret.API_KEY_SERVER_SECRET_PREV— the previous secret, for graceful rotation.
During a rotation window, keys hashed under the previous secret still verify, so you can rotate the server secret without invalidating live keys. Comparison is constant-time.
Scopes
API keys carry a list of scopes. See API Keys for the full scope matrix.
The raw key is shown once
On creation the server returns raw_key, then discards it. There is no recovery path — see API Keys.