Rooms

Rooms are the middle segment of every address, app/room/topic. They give each conversation, game, device or user its own copy of an app's topics.

What are Rooms?

A room groups related topics and isolates them from every other room in the app. A subscriber to messages in game-lobby never sees messages published in game-42.

import { NoLag } from "@nolag/js-sdk";

const client = NoLag(token, { url: "ws://localhost:8080/ws" });
const room = client.setApp("games").setRoom("game-lobby");

client.on("connect", () => {
  // Subscribe to topics within this room
  room.subscribe("chat");
  room.subscribe("player-updates");
});

await client.connect();

// Messages are scoped to this room
room.emit("chat", { text: "Hello lobby!" });

Rooms Never Exist Implicitly

kraken does not create a room the first time someone names it. A subscribe or publish to an address that none of the actor's grants cover is refused with unknown_topic (42940) on the client's error event, and nothing is delivered. This is deliberate: a typo in a room slug fails loudly instead of creating a second, empty room that the other side never joins.

What makes a room "known" depends on your auth backend.

With @nolag/core

Rooms are records belonging to an app. You declare them in the app's rooms list in the project document:

{
  "slug": "games",
  "name": "Games",
  "accessMode": "open",
  "topics": ["chat", "player-updates", "moves"],
  "rooms": [
    { "slug": "game-lobby", "name": "Game Lobby" },
    { "slug": "game-42", "name": "Game 42" }
  ]
}

When an actor connects, core lists every room it may reach in every app, and kraken caches that list for the life of the connection. Rooms created later reach a connected client in two ways:

  • On subscribe: when a client subscribes to a room that is not in its cached list, kraken asks core once whether the actor may reach it, and on a yes adds it to the connection. The client does not need to reconnect.
  • On revalidation: kraken refreshes every connection's grants from core about every 10 minutes.

kraken does not make that check for a publish. A client that only publishes to a room created after it connected is refused until it reconnects, waits for revalidation, or subscribes to the room first.

For rooms created at runtime (one per order, per match, per device), a host built on @nolag/core can create them with the library's room facade (RoomFacade.createRoom). The example host that ships with core only imports and exports whole project documents.

In the project document, room slugs are lowercase letters, digits and hyphens, at most 100 characters, and unique within the app.

With kraken's static auth file

There are no room records. A token's grant patterns decide which room addresses it may use:

"allowedTopics": [
  {
    "pattern": "games/game-lobby/#",
    "permission": "pubSub",
    "app_id": "games",
    "room_slug": "game-lobby",
    "room_id": "games-game-lobby"
  },
  { "pattern": "games/+/#", "permission": "pubSub", "app_id": "games" }
]

The first rule grants one room. The second uses + to match any room slug in the games app, which is how you get rooms on demand with the static file: any client holding this grant can use games/game-42/moves without anything being created first.

Presence and lobbies need more than an address: they only work in rooms that have a rule carrying both room_slug and room_id, like the first rule above. A + rule covers messages in any room but presence in none. See Static auth for the full format.

Public vs Private Rooms

This applies to @nolag/core. A room with no grants attached is public: every actor that can reach the app can use it, with the app-level permission. The moment any grant is attached to a room, the room becomes private, and only actors holding a grant for it can use it.

Grants come in two forms in the project document:

  • Type grants on the room (typeGrants): every actor of a given actor type, for example service
  • Actor grants on the actor (roomAccess): one named actor
{
  "slug": "vip",
  "name": "VIP",
  "typeGrants": [{ "actorType": "service", "permission": "pubSub" }]
}

Access control is built into room assignment rather than separate permission rules:

  • Notifications: grant a user their own room, and only they receive their notifications
  • Team chat: grant the team members a shared room, and outsiders cannot join
  • Device telemetry: grant a device and its owner a room, isolating the data stream

No grants = public. At least one grant = private. There is no toggle; the existence of a grant is what makes a room private, so the first grant you add locks every other actor out. Grant everything that needs the room, including your own backend services, in the same change. See Access Control for the full rules.

With the static file, privacy is simply which tokens carry a rule covering the room.

Topic Inheritance

In @nolag/core, every room inherits the topics defined on its app. A chat app with messages and _typing gives every room both topics, so you configure topics once and they apply to thousands of rooms. A room's own topics field in the project document is not used for authorization; a grant can set its own topics list instead.

Topic Resolution

Topics within a room are namespaced automatically. Subscribing to chat in room game-lobby of app games addresses games/game-lobby/chat. That three-part path is the only hierarchy: topic names are single segments with no /.

Use Cases

  • Game lobbies: each game session gets its own room
  • Chat channels: separate rooms for different conversations
  • User spaces: private rooms for user-specific notifications
  • Multi-tenant apps: isolate organizations from each other, or use access scopes to do it without duplicating rooms

Next Steps