Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tokens

Clients authenticate with access tokens. Backends authenticate token-admin calls with a master token.

Formats

  • Master token: MT_... (keep on the server). Policy fields (name, description, tenant_prefix, allow_ip_masks) are stored with the token in Cockpit, not inside the secret string.
  • Access token: AT_{token_id}_{secret}

token_id and secret are hex. Helpers parseAccessToken / parse_access_token split them. Never log the full string.

Lifetime

  • expires_at is ISO-8601 UTC.
  • Maximum TTL is 24 hours from server time. Longer values are rejected.
  • After expiry, new connections fail.
  • Existing sockets are not a forever lease: mint a new AT or refresh before expiry.

Refresh (backend, master token):

  • PUT /v1/refresh-token
  • Body uses token_id (not the secret) and a new expires_at (still max 24 hours from now).
  • Already expired tokens cannot be refreshed.

Revoke (backend, master token):

  • DELETE /v1/revoke-token
  • Body is the full AT_... string.

SDKs wrap these as refreshAccessToken / refreshAccessTokenFromAt (JavaScript) and refresh_access_token / refresh_access_token_from_at (Rust). Revoke is revokeAccessToken / revoke_access_token.

Master token fields (Cockpit)

These sit next to the secret hash. They are not encoded in MT_....

FieldRole
nameRequired short label in Cockpit (max 64).
descriptionComment (max 512).
tenant_prefixNamespace for this app. Empty = whole overlay. Several master tokens may share one prefix. Cockpit adds a trailing . if the prefix ends with a letter or digit (paintpaint.), so app does not match apple. On POST /v1/get-token the stored prefix is prepended to each tenant_ids value that does not already start with it. The access token then carries the physical name (paint. + lobbypaint.lobby). Clients must use that physical name on the socket. After prepend, tenant length must stay ≤ 128.
allow_ip_masksCIDR or exact IP. Limits who may call get-token, refresh, revoke, and list with this master token. Empty = no IP limit. If masks are set and the caller IP is unknown, the call is refused. This is not the access-token socket IP list.

Do not copy channel grants, origin, protocol, or region lists onto the master token.

Permissions on the token

Each access token carries tenant_grants:

  • tenant_ids — exact tenant names (no wildcards)
  • allow_channels_pub — publish rules
  • allow_channels_sub — subscribe rules

Optional extra rights:

  • allow_ip_masks — CIDR / exact IP
  • allow_regions — currently US and EU if your overlay uses them
  • allowed_ws_origin — browser Origin allow-list
  • allow_protocolswebsocket / webtransport
  • ingress_kb_per_sec / egress_kb_per_sec — edge caps, typically 8–16384

Full pattern rules: Permissions model.

How the client sends the access token

WebSocket

  1. Authorization: Bearer AT_... (server-side)
  2. Sec-WebSocket-Protocol: llps.v1, at.AT_... (browser)

The handshake echoes only llps.v1.

WebTransport

Bearer access token on the HTTP/3 session (SDK default).

What not to do

  • Do not put master tokens in browsers, mobile apps, or source control.
  • Do not log MT_... or AT_....
  • Do not mint 24-hour tokens for public web pages when 15–60 minutes will do.
  • Do not grant public.# publish to untrusted clients if you can grant one room instead.