/ how it works

Four levels. Zero confusion.

Every message in NoLag flows through a four-level hierarchy. Each level exists for a specific reason. Skip one, and you'd lose an important boundary.

project  / app   / room     / topic
―――――――――――――――――――――――――――――――――――――――――――
logistics  / chat   / dispatch-team / messages
logistics  / chat   / dispatch-team / _typing
logistics  / tracking / driver-42   / locations
logistics  / tracking / driver-42   / status
logistics  / dashboard / overview   / metrics
logistics  / notify  / user-1042   / alerts

A logistics platform with chat, GPS tracking, a live dashboard, and notifications, all within one project.

Project: Your Product Boundary

A project is the top-level container. It represents your product or a specific environment of your product. Everything inside (apps, actors, rooms, access scopes) belongs to that project.

Isolation and limits

Each project's rooms resolve to their own internal topics, so one project's traffic never reaches another. Connection and message-size limits come from your auth backend and are enforced by the broker.

Actor home

Actors (your end users) are created within a project and granted access to specific apps.

Environment separation

Most teams create separate projects for production and staging so test traffic never touches real data.

Think of a project the same way you'd think of a Firebase project or a Supabase project: one per product, per environment.

App: A Distinct Real-time Feature

Inside a project, each app represents a separate real-time capability. Apps have their own topic definitions, room configurations, and webhooks.

Why separate apps instead of one big app?

Because different features have fundamentally different requirements:

Chat app

Needs messages that people read and reply to, plus ephemeral typing indicators that nobody needs to keep.

GPS tracking app

Needs low-latency fire-and-forget delivery. Stale coordinates are useless.

Dashboard app

Needs retained snapshots so a new viewer sees the latest numbers at once, plus widget configuration updates.

Notifications app

Needs a private room per user so each person receives only their own alerts, with read state synced across tabs.

By splitting these into separate apps, each one gets its own topics, rooms, and webhooks without compromise. Chat's webhooks never fire for GPS pings, and tracking's per-driver rooms never clutter the chat room list. When you need a new feature later, you add a new app without touching the others.

Actors are granted access to specific apps. A single actor can be given access to chat, tracking, dashboard, and notifications, interacting with each through one WebSocket connection.

Room: Scoped Data Isolation

Rooms give each user, team, device, or session their own isolated instance of an app's topics. Without rooms, every subscriber to "messages" would see every message from every conversation. Rooms scope the data.

Chat app

Each room is a conversation: dispatch-team, drivers-east, support.

Tracking app

Each room is a tracked entity: driver-42, vehicle-108.

Dashboard app

Each room is a view: overview, region-north.

Static vs Dynamic Rooms

Rooms can be static (defined in your app configuration for permanent channels like general) or dynamic (created at runtime through @nolag/core or your own auth service when a new driver signs up or a new team is formed). With @nolag/core, rooms are never created implicitly: the broker refuses an unknown room rather than inventing one, so a typo is a visible error.

Public vs Private Rooms

With @nolag/core, rooms are public by default, meaning any actor with access to the app can connect. When you attach specific actors to a room, that room automatically becomes private. Only the attached actors can access it.

This is how you implement per-user or per-team data isolation without writing any authorization logic:

  • Attach a driver to their tracking room, and no other driver can see their GPS stream
  • Attach team members to a chat room, and outsiders can't join
  • Attach a user to their notification room, and only they receive their alerts
No actors attached = public. At least one actor attached = private. There is no toggle; the presence of actor assignments is what makes a room private.

Topic: The Message Channel

Topics are the actual pub/sub channels within a room. Each topic carries one type of data. Subscribers choose which topics to listen to, so a client in the dispatch-team chat room might subscribe to messages but not _typing.

messages

Chat messages people read and reply to

_typing

Ephemeral typing indicators, fire-and-forget

locations

GPS coordinates streamed in real-time

metrics

Aggregated dashboard data points

Topic Inheritance

Topics are defined at the app level and inherited by every room. All rooms in your chat app share the same topic structure (messages and _typing), so you configure once and scale to thousands of rooms.

Putting It All Together

LevelExamplePurpose
Projectlogistics-prodIsolation and connection limits, actor management, environment boundary
Appschat, tracking, dashboard, notifySeparate features with independent config
Roomsdispatch-team, driver-42, overviewPer-user/team/device data isolation
Topicsmessages, locations, metricsIndividual message channels within a room

A dispatcher with access to all four apps can simultaneously:

chat
Sends and receives messages in dispatch-team
tracking
Watches live GPS updates from driver-42
dashboard
Monitors fleet health on overview
notify
Receives their own alerts in dispatcher-7

All through one connection, with each app's data cleanly separated.

Ready to build?

Run the broker locally and start publishing messages in under 5 minutes.