Lobbies
Observe presence across multiple rooms from one subscription. A lobby is how a dashboard, dispatcher or monitor sees who is present in many rooms without joining each one.
Read the limits before you build on lobbies. In kraken v0.9.0 the set of rooms a lobby covers comes from the subscriber's own grants rather than from your lobby definition, and kraken only remembers that set for five minutes on the node where the subscribe happened. Lobbies work for a single-node dashboard that re-subscribes regularly; they are not yet a dependable cross-room view.
What is a Lobby?
A lobby groups rooms for presence observation. Actors in a room only see each other; a lobby subscriber sees presence from the lobby's rooms, with each event tagged by the room it came from.
Room trip-123 -> driver + rider
Room trip-456 -> driver + rider
Room trip-789 -> driver + rider
Lobby active-trips -> a dispatch dashboard sees everyone present in those rooms
Lobbies are read-only. Nobody publishes to a lobby or sets presence on one; actors set presence on their room, and kraken forwards it to the lobby's subscribers.
Use Cases
- Ride-sharing dashboard: monitor every active trip and its driver's position
- Support center: see which support conversations have someone in them
- Game matchmaking: show player counts across game rooms
- IoT monitoring: track which devices are connected across sites
Setting Presence on a Room
Participants set presence on their own room with room.setPresence(), exactly as in Presence. kraken forwards it to the lobby. Presence set without a room is never broadcast and never reaches a lobby.
import { NoLag } from "@nolag/js-sdk";
const client = NoLag(driverToken, { url: "ws://localhost:8080/ws" });
const trip = client.setApp("rides").setRoom("trip-123");
client.on("connect", () => {
trip.setPresence({ role: "driver", name: "John Smith", location: { lat: 37.77, lng: -122.41 } });
});
await client.connect();
// Update location while driving
setInterval(() => {
trip.setPresence({ role: "driver", name: "John Smith", location: getCurrentLocation() });
}, 5000);Subscribing to a Lobby
Subscribe to receive a snapshot of current presence, followed by live events. The lobby is addressed by its slug.
import { NoLag } from "@nolag/js-sdk";
const client = NoLag(dashboardToken, { url: "ws://localhost:8080/ws" });
await client.connect();
const lobby = client.setApp("rides").setLobby("active-trips");
// Live events carry the room they came from
lobby.on("presence:join", (event) => addToMap(event.roomId, event.actorId, event.data));
lobby.on("presence:update", (event) => updateOnMap(event.roomId, event.actorId, event.data));
lobby.on("presence:leave", (event) => removeFromMap(event.roomId, event.actorId));
// subscribe() resolves with a snapshot. The first snapshot can come back
// empty (see the limits below), so read the current state again.
await lobby.subscribe();
const snapshot = await lobby.fetchPresence();
// snapshot: { roomSlug: { actorId: { actorTokenId, presence, joinedAt } } }
for (const [roomSlug, actors] of Object.entries(snapshot)) {
for (const [actorId, entry] of Object.entries(actors)) {
addToMap(roomSlug, actorId, entry.presence);
}
}To stop observing, call lobby.unsubscribe() (Unsubscribe() in Go).
Lobby Presence Events
Live events include the room, so a dashboard can group actors by room:
interface LobbyPresenceEvent {
lobbyId: string; // The lobby slug
roomId: string; // The room the actor is in, as a slug
actorId: string; // The actor's id
data: PresenceData; // The actor's presence payload (empty on leave)
}
The snapshot is shaped differently from the events: each actor maps to an entry holding actorTokenId, presence and joinedAt, not to the presence payload itself. Read entry.presence for the payload.
Defining a Lobby and Granting Access
There is no separate lobby permission. An actor may subscribe to the lobbies its grants list, and a refused subscribe arrives as not_authorized on the client's error event (the JavaScript lobby.subscribe() then rejects after its 10-second timeout).
With @nolag/core
Lobbies belong to an app in the project document, and list room slugs from the same app:
{
"slug": "rides",
"name": "Rides",
"topics": ["status"],
"rooms": [
{ "slug": "trip-123", "name": "Trip 123" },
{ "slug": "trip-456", "name": "Trip 456" }
],
"lobbies": [
{ "slug": "active-trips", "name": "Active trips", "rooms": ["trip-123", "trip-456"] }
]
}
A lobby is visible to any actor that can reach at least one of its rooms. A rider who can reach trip-123 can therefore subscribe to active-trips too.
With kraken's static auth file
List the lobby on the token, with a slug and an id:
"dev-token-dispatch": {
"actorTokenId": "dispatch",
"projectId": "rides",
"actorType": "user",
"allowedTopics": [
{ "pattern": "rides/trip-123/#", "permission": "subscribe", "app_id": "rides", "room_slug": "trip-123", "room_id": "rides-trip-123" },
{ "pattern": "rides/trip-456/#", "permission": "subscribe", "app_id": "rides", "room_slug": "trip-456", "room_id": "rides-trip-456" }
],
"allowedLobbies": [{ "lobby_slug": "active-trips", "lobby_id": "rides-active-trips" }]
}
Current Limits
These follow from how kraken v0.9.0 maps lobbies to rooms, and apply with either auth backend:
- The subscriber's grants decide the rooms. When an actor subscribes to a lobby, kraken treats every room in that actor's own grants that carries a room id as part of the lobby, across all of its apps. With
@nolag/core, the lobby'sroomslist decides who may subscribe, not which rooms are watched. With the static file, the dispatcher above watches exactly the rooms its rules name. - Five-minute memory. kraken remembers that room mapping for five minutes after the most recent subscribe to the lobby. After that, live lobby events stop and the presence list comes back empty, until someone subscribes to the lobby again. A dashboard that must stay live can call
lobby.subscribe()again on a timer shorter than five minutes. - The first snapshot can be empty. The snapshot returned by
subscribe()can be read before the mapping is stored. CallfetchPresence()after subscribing, as in the examples above. - Shared mapping. The mapping is shared by every subscriber of the lobby on that node, so subscribers whose grants differ can see presence from each other's rooms, and a room maps to the lobby most recently subscribed over it.
- Single node. The mapping lives on the kraken node that handled the subscribe. In a cluster, presence set by actors connected to other nodes does not reach the lobby.
- Not isolated by access scope. Like room presence, lobby presence is not separated by scope.
Keep presence payloads free of anything one participant should not see about another, and keep sensitive rooms out of any lobby. If you need a dependable cross-room view today, have participants also publish their status to a topic the dashboard subscribes to, for example rides/dispatch/status.
Room vs Lobby Presence
| Feature | Room Presence | Lobby Presence |
|---|---|---|
| Scope | Single room | Rooms in the subscriber's grants |
| Can set presence | Yes | No (read-only) |
| Events include the room | No (implicit) | Yes |
| Use case | Participants in a session | Dashboards, monitoring |
| Who can observe | Actors with presence in the room | Actors granted the lobby |