Hierarchy
How projects, apps, rooms and topics fit together to organize your realtime traffic.
Overview
Every message in NoLag is addressed by a three-part path, app/room/topic, and every actor belongs to a project. Each level exists for a specific reason; skip one and you lose a boundary.
project / app / room / topic
-------------------------------------------------
logistics / chat / dispatch-team / messages
logistics / tracking / driver-42 / locations
logistics / dashboard / overview / metrics
The project is not part of the address. It is the container that the actor's token belongs to, so a client only ever writes app/room/topic.
Where the hierarchy is defined
kraken itself stores no configuration. It asks its auth backend, once per connection, which topics an actor may reach, and caches the answer. So the hierarchy lives wherever your auth backend keeps it:
@nolag/core: projects, apps, rooms, lobbies, access scopes and actors are real records, written from a project document. Core turns them into the exact list of topics each actor may reach.- kraken's static
auth.jsonfile: there are no app or room records. Each token lists the topic patterns it may use, with labels (projectId,app_id,room_slug,room_id) that kraken reads from the rules. See Static auth.
The concepts below hold for both. Where they differ, the pages in this section say so.
Project: your outermost boundary
A project is the top-level container. It represents your product, or one environment of it. In @nolag/core, apps, actors, access scopes and signing keys all belong to a project, and an actor can only ever reach apps in its own project.
- Actor home: actors (your users, devices and services) are created within a project and granted access to its apps
- Environment separation: run separate projects (or separate deployments) for production and staging so test traffic never touches real data
- Connection limit: when a
maxConnectionslimit is set, kraken counts it perorganizationId, across every project (or token) that carries the same organization id. With no limit set, connections are unlimited
In the static file, projectId and organizationId are labels on each token. kraken uses the project id to keep load-balancing groups apart and the organization id to count connections, so give every token a projectId.
App: a distinct realtime feature
Inside a project, each app represents one realtime capability. In @nolag/core an app owns its topic list, its rooms and lobbies, whether actors need an explicit grant to reach it (accessMode), and its webhooks.
Why separate apps instead of one big app? Because different features have different requirements:
- A chat app wants presence, a hydration webhook that loads recent messages, and rooms per conversation
- A GPS tracking app wants a room per vehicle and tight access rules, so one driver cannot read another's stream
- A dashboard app wants a few broadcast rooms that every operator can subscribe to but only a backend service can publish to
Splitting these into apps gives each its own topics, rooms and access mode without compromise. Adding a notifications feature later means adding an app, not touching the others.
A single actor can be given access to several apps and use all of them over one connection.
Room: scoped data isolation
Rooms give each conversation, team, device or session its own instance of an app's topics. Without rooms, every subscriber to messages would see every message from every conversation.
- In a chat app: each room is a conversation:
dispatch-team,drivers-east,support - In a tracking app: each room is a tracked entity:
driver-42,vehicle-108 - In a dashboard app: each room is a view:
overview,region-north
A room has to be known to the auth backend before anyone can use it: kraken never creates rooms on the data path. See Rooms for how rooms come into being with each backend.
Public vs private rooms
In @nolag/core, rooms are public by default: any actor with access to the app can use them. Attaching a grant to a room makes it private, and from then on only grant holders can use it. See Access Control.
Topic: the message channel
Topics are the pub/sub channels within a room. Each topic carries one kind of data, and subscribers choose which topics to listen to, so a client in the dispatch-team room might subscribe to messages but not _typing.
A topic name is a single path segment. Use letters, digits, _, : and -, and never /, + or #: / separates the parts of the address, and + and # are pattern wildcards.
messages: chat messages_typing: ephemeral typing indicatorslocations: GPS coordinatesmetrics: aggregated dashboard data points
In @nolag/core, topics are defined on the app and every room inherits them, so all rooms in a chat app have the same topic structure. You configure topics once and they apply to every room.
Access scopes: an optional tenant segment
An actor can belong to an access scope. For a scoped actor, kraken inserts the scope slug into the address, so chat/general/messages becomes chat/acme/general/messages, and actors in different scopes never share a topic. The client code does not change.
Putting it all together
| Level | Example | Purpose |
|---|---|---|
| Project | logistics-prod | Actor home, environment boundary, connection limit |
| Apps | chat, tracking, dashboard | Separate features with independent topics, rooms and access |
| Rooms | dispatch-team, driver-42, overview | Per-conversation, per-device or per-view isolation |
| Topics | messages, locations, metrics | Individual message channels within a room |
A dispatcher with access to all three apps can, over one connection:
- Send and receive messages on
chat/dispatch-team/messages - Watch live GPS updates from
tracking/driver-42/locations - Monitor fleet health on
dashboard/overview/metrics