---
title: Static Auth File
description: "kraken's default auth backend: tokens and their grants in a JSON file that kraken re-reads when it changes. The format, how grants resolve, reloading and revocation, and the insecure AUTH_ALLOW_ALL switch."
---

# 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](#allow-all-mode). |

## Format

```json
{
  "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](/docs/authentication#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](#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](#one-app-per-token). |
| `scopeSlug` | | An access scope slug. See [Access Scopes](/docs/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:

1. If a grant's `pattern` equals the address exactly and has a `topic`, that `topic` is the internal topic.
2. Otherwise, if a grant matches the address (exactly, or with `+` and `#`), the internal topic is `<app_id>/<address>`.
3. 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`:

```json
"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](/docs/getting-started#reconnects-what-comes-back-and-what-does-not).

## 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 `4001` and reason `token_revoked` at 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:

```yaml
services:
  kraken:
    environment:
      - AUTH_FILE=/app/auth/auth.json
    volumes:
      - ./auth:/app/auth:ro
```

## Rotating a token

1. Add the new token as a second entry with the same `actorTokenId`.
2. Move the client over to the new token.
3. 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

```bash
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](/docs/self-hosting/http-auth) or the [full stack](/docs/self-hosting/full-stack) when you need tokens per user, short-lived browser tokens, changes without editing a file, or webhooks.
