Session Lifecycle
Session states, the browser bind protocol, how to unlock a locked session, and how to end one.
Endpoint schemas and parameter details live in the API Reference. This page covers session behavior — what states a session can be in, how browser clients finalize sessions, how to unlock a locked session, and how to end one.
Session States
Every access token maps to a KV entry that records the session's current state. A session is either locked or unlocked, depending on whether owner_token and user_member_token are stored in that entry.
Think of a locked session as a library card that proves you're a member but doesn't tell the
librarian which books are yours. The access token gets you through the door. The owner_token
and user_member_token — derived from your personal key material on your device — tell the
server which records belong to you. Without them, the server has no way to locate your documents
or entity memberships.
| State | What it carries | What it can do |
|---|---|---|
| Unlocked | Access token + owner_token + user_member_token | Full API access — documents, entities, search, deliveries |
| Locked | Access token only | Auth operations and token refresh only |
Locked sessions arise in two situations:
- Account recovery —
POST /auth/recoveryreturns a locked session intentionally. Your device decrypts the recovered key material and callsPATCH /auth/recoveryto receive an unlocked token. - Browser login —
POST /auth/opaque/authenticate-finishissues a 60-second pending access token. Your device must complete the bind protocol before the session can act on data.
The Browser Bind Protocol
Your device cannot safely receive a refresh token in a login response — it would be exposed to the JavaScript environment before being stored securely. Instead, your device derives the refresh token locally using OPRF evaluation, then binds the session server-side.
Step 1 — Receive the Pending Access Token
POST /auth/opaque/authenticate-finish returns a short-lived pending access token with a 60-second TTL. This token is accepted on only two endpoints — POST /auth/session/refresh-eval and POST /auth/session/bind — until it expires or is replaced by a full token at bind.
Step 2 — Evaluate the OPRF
POST /auth/session/refresh-eval accepts a blinded ristretto255 point. The server multiplies it by its OPRF key scalar and returns the evaluated result. The server never learns your device's refresh secret; your device never learns the OPRF key.
Your device unblinds the result to derive the refresh token locally.
Step 3 — Bind the Session
POST /auth/session/bind finalizes the session using the OPRF-derived refresh_token:
- Validates the pending KV entry (
pending: truemust be set) - Creates a DB session row keyed by SHA-256 of the refresh token
- Issues a new, non-pending access token (15-minute TTL)
- Deletes the pending KV entry
- Sets the
kyndex_rtHttpOnly cookie containing the refresh token
After this step the session is full and unlocked. The server has no record of what refresh token your device holds — only its hash.
Unlocking a Locked Session
Pass owner_token and user_member_token in the refresh body alongside the refresh token:
POST /auth/tokens/refresh
Content-Type: application/json
{
"refresh_token": "<base64-encoded 32 bytes>",
"owner_token": "<base64-encoded 32 bytes>",
"user_member_token": "<base64-encoded 32 bytes>"
}The server stores these values in the new access token's KV entry. All requests using the new token are treated as unlocked.
This is the standard path after account recovery: POST /auth/recovery returns a locked session, your device decrypts its recovered key material and derives the two tokens, then calls POST /auth/tokens/refresh to receive an unlocked access token.
Endpoints That Require an Unlocked Session
Requests to these endpoints with a locked session return 401 with error code SESSION_LOCKED. The message is: session is locked; provide owner_token and user_member_token via token refresh.
| Domain | Affected endpoints |
|---|---|
| Documents | Create, list, reservations, OCR result, consumer indexes |
| Entities | List, add member, manage members |
| Deliveries | Discover, received, reserve, create, lifecycle |
| Search | Document search |
| Jobs | Get job status |
All auth, token refresh, and public endpoints accept locked sessions.
Ending a Session
Both logout routes require a valid access token and return 204 No Content on success. The kyndex_rt cookie is cleared automatically for browser clients.
| Endpoint | Scope | Body |
|---|---|---|
DELETE /auth/sessions/current | Current session only | None |
DELETE /auth/sessions | All sessions | { "revocation_token": "<base64 32 bytes>" } |
The revocation_token is issued during login alongside the access and refresh tokens. It authorizes bulk invalidation of every session tied to the account. Store it alongside your refresh token — if it is lost, all-session logout is unavailable until the next login.