Static Auth File
With its default auth backend, static, kraken reads tokens and their grants from a JSON file. There is no database and no other service: change the file, and kraken picks the change up without a restart.
| Setting | Default | Meaning |
|---|---|---|
AUTH_BACKEND | static | Selects this backend |
AUTH_FILE | /app/examples/auth.json | Path of the file inside the container. The repository's docker-compose.yml mounts ./examples/auth.json there, read-only. |
AUTH_ALLOW_ALL | false | Accept any token with access to everything. Insecure; see allow-all mode. |
Format
{
"tokens": {
"dev-token-alice": {
"actorTokenId": "alice",
"projectId": "demo-project",
"organizationId": "demo-org",
"actorType": "user",
"allowedTopics": [
{
"pattern": "demo/general/#",
"permission": "pubSub",
"room_id": "room-general",
"room_slug": "general",
"app_id": "demo-app",
"app_name": "demo"
}
]
}
}
}
Each key under tokens is a token a client may connect with. Use long random strings: anyone who has one can connect as that actor. Keys outside tokens are ignored.
Token fields
| Field | Required | Meaning |
|---|---|---|
actorTokenId | yes | The actor's id. The client sees it as actorTokenId in the auth reply and in presence; kraken uses it to revalidate and as the default load-balance group. |
allowedTopics | The grants. Without any, the token can connect but reach nothing. | |
projectId | Reported back in the auth reply, and part of every load-balance group name. | |
organizationId | The group kraken counts connections in for maxConnections. | |
actorType | A label, user by default. See actor types. | |
maxConnections | Connection limit for the token's organizationId, counted across every token that shares it; it has no effect without an organizationId. Absent means unlimited. A connect beyond the limit is refused with connection_limit_reached. | |
activeSubscriptions | Subscriptions kraken restores when this token reconnects. See restoring subscriptions. | |
allowedLobbies | Lobbies the token may subscribe to, as [{ "lobby_slug": "...", "lobby_id": "..." }]. | |
appId, appName | The app every grant of this token belongs to. Default: the app_id of the first grant. See one app per token. | |
scopeSlug | An access scope slug. See Access Scopes. |
The repository's example file also carries rateLimit. kraken ignores it: the limit is 50 publishes per second per connection for everyone. It ignores maxMessageSizeBytes too; the payload ceiling is a fixed 921,600 bytes. There is no way to configure webhooks in the static file.
Grant fields
Each entry in allowedTopics:
| Field | Meaning |
|---|---|
pattern | Required. The addresses the grant covers, as app/room/topic. + matches exactly one segment and # matches everything after it, so demo/general/# covers every topic in room general of app demo, and demo/+/messages covers messages in every room. |
permission | Required. subscribe, publish or pubSub. |
app_id | The app the grant belongs to. kraken uses it to name its internal topics, so tokens that should reach each other must agree on it. |
app_name | Descriptive |
room_id, room_slug | Needed for presence and lobbies. A client sends presence for a room by its slug; kraken finds the room through these two fields. Presence for a slug no grant names is silently ignored. |
topic | An internal topic name for an exact (wildcard-free) pattern. Leave it out unless you know you need it; see below. |
How grants turn into topics
kraken never uses your address as its internal topic directly. It resolves the address through the connection's grants, the same way for a subscribe and a publish:
- If a grant's
patternequals the address exactly and has atopic, thattopicis the internal topic. - Otherwise, if a grant matches the address (exactly, or with
+and#), the internal topic is<app_id>/<address>. - If no grant matches, the request fails with
unknown_topic(42940).
So two tokens reach each other when they resolve the same address to the same internal topic. The simplest way to guarantee that is to leave topic out and use one app_id per app across the whole file. Then every token resolves demo/general/messages to demo-app/demo/general/messages, whether its grant is demo/general/#, demo/+/messages or the exact address. A token whose grant sets topic resolves that address somewhere else, and does not reach tokens whose grants do not.
One app per token
kraken puts all of a token's grants into one app, and gives every grant that app's id: the token's appId if it has one, otherwise the app_id of its first grant. A token whose grants span two apps therefore resolves the second app's addresses under the first app's id, and does not reach tokens that hold only the second app. Give such a token its own appId shared with the tokens it talks to, or use one app_id value for the whole file.
Restoring subscriptions
kraken does not remember what a client subscribed to. When a client reconnects (the SDKs send reconnect: true after an unexpected drop), kraken restores the subscriptions its auth backend lists for the actor, and for the static file that is activeSubscriptions:
"dev-token-carol": {
"actorTokenId": "carol",
"allowedTopics": [
{ "pattern": "demo/general/#", "permission": "pubSub", "app_id": "demo-app", "room_id": "room-general", "room_slug": "general" }
],
"activeSubscriptions": ["demo/general/messages"]
}
We tested this against kraken v0.9.0: after a restart, a client using this token received messages on demo/general/messages again without subscribing a second time, while the demo tokens, which list no activeSubscriptions, received nothing until they subscribed again. Entries are addresses, or objects with pattern, loadBalance, loadBalanceGroup and filters. The list is fixed per token, whatever the client subscribed to; for anything else, subscribe in the client's connect handler, as in the Quick Start.
Reloading
kraken checks the file's modification time whenever it looks a token up, and re-reads the file when it has changed. No restart is needed:
- A new token works on its first connection.
- A changed grant reaches connections made with that token at their next revalidation, within about ten minutes. New connections get it at once, except that kraken may answer a connect from its 30 second cache.
- A removed token is refused for new connections once kraken's 30 second cache of that token expires. Connections already open with it are closed with code
4001and reasontoken_revokedat their next revalidation, within about ten minutes.
If the file stops being valid JSON, kraken logs Failed to parse auth file and refuses every token until the file is fixed. Check it with python3 -m json.tool auth.json or jq . auth.json before you save it into place.
On Docker Desktop we found that edits made on the host to a single-file bind mount (the repository's default, ./examples/auth.json:/app/examples/auth.json:ro) did not reach the container, so kraken kept serving the old tokens. Mounting the directory worked:
services:
kraken:
environment:
- AUTH_FILE=/app/auth/auth.json
volumes:
- ./auth:/app/auth:ro
Rotating a token
- Add the new token as a second entry with the same
actorTokenId. - Move the client over to the new token.
- Delete the old entry.
While two entries share an actorTokenId, revalidation finds the actor through either of them, so connections made with the old token stay up until both entries are gone.
Allow-all mode
AUTH_ALLOW_ALL=true docker compose up -d
With AUTH_ALLOW_ALL=true, kraken accepts any token. The token string itself becomes the actor id, every actor gets pubSub on every topic (#), and the file is not read. kraken logs a line saying so for each token it accepts. It exists to try things out on your own machine and must never be set anywhere else: a broker started this way lets anyone who can reach it read and write everything.
When to move on
The static file suits a fixed set of services and devices. Move to an HTTP auth service or the full stack when you need tokens per user, short-lived browser tokens, changes without editing a file, or webhooks.