Skip to content

Realtime

Realtime

Move messages from your backend to open browser tabs, and know who is connected and what they missed.

Lessly Realtime moves messages between your backend and your users’ open browser tabs. Your backend publishes to a named channel; every client currently subscribed to that channel receives the message within the same request. On top of that it keeps a roster of who is on a channel, and a replayable history of what was sent there.

You work with your own concepts: namespaces, channels, capability tokens and grants. Holding the connections open, delivering to every subscriber and reconnecting a client that drops off are Realtime’s job.

When to reach for it

Reach for Realtime when something that happens on your server has to show up in a browser without the browser asking for it: a chat message, a document another person is editing, an order changing state, a long job reporting progress, a dashboard that must stay current. Reach for presence when you need to show who else is here, and for history when a client that reconnects must not miss what it was away for.

It is not the right tool for work that has no live audience. If nothing is watching, publish nothing.

The four ideas

  • Channel — a named stream of messages, such as chat:room-1. Its name is 3 to 128 characters of a-z, A-Z, 0-9, ., _, - and :, and the first colon is required. You never create a channel: publishing to a name or subscribing to it is enough.
  • Namespace — the segment before the first colon. chat:room-1 and chat:room-2 both live in chat. It is 1 to 64 characters of a-z, A-Z, 0-9, ., _ and -, and cannot contain a colon. It carries the policy for every channel under it.
  • Capability — one channel name paired with a set of operations. There are exactly four: subscribe receives messages, publish sends one from a connected client, presence enters and reads the roster, history replays what was sent earlier. A token carries a list of capabilities, and that list is the entire authority of the connection holding it.
  • Grant — the durable form of a capability, tying a subject to a channel pattern and a set of operations until you revoke it.

A namespace you have not registered allows nothing. Channels in it are silently dropped wherever capabilities are worked out, so subscribing to chat:room-1 before chat exists never succeeds. Register the namespaces your channels use before you send anything through them.

What a namespace decides

A namespace is the unit of policy. Registering it decides what any channel under it is allowed to do.

SettingValuesDefaultWhat it decides
visibilitypublic, authorizedauthorizedpublic gives every authenticated identity subscribe on namespace:* without a grant
presencebooleanfalseWhether a channel keeps a roster
clientEventsbooleanfalseWhether connected clients may publish directly
historynone, last-message, windownoneWhat is retained and replayable
historyWindowSeconds1 … 2592000Retention window; required when history is window
encryptionRequiredbooleanfalseWhether payloads must be end-to-end encrypted
identifiedOnlybooleanfalseWhether anonymous callers get any capability at all
subscribeProxyUrlHTTPS URL or nullnullA callback of yours consulted per channel while a token is minted
archiveoff, { days: N }offDurable retention of archived history entries, up to 3650 days

Namespaces are registered from the platform, not from the public HTTP surface. Over MCP that is realtime_namespace_create and realtime_namespace_update; over REST it is on the Realtime API reference.

How policy narrows a capability

Nothing that reaches a token is ever wider than the namespace allows. The same rule applies whether the capability comes from a grant or from what your backend declares when it mints a token:

  • presence survives only if the namespace has presence enabled.
  • publish survives only if the namespace has clientEvents enabled.
  • history survives only if the namespace’s history is not none.
  • subscribe is not gated by these flags. Who may subscribe is decided by the caller side: a grant, or the channel your backend names when it mints.

Operations that do not survive are dropped, not refused. A request for ['subscribe', 'presence'] on a namespace without presence yields a capability of ['subscribe']. If everything asked for is stripped, the mint fails with 422 rather than handing back a token that would connect and then be refused on every action.

Two more settings act before that narrowing. identifiedOnly yields nothing at all to an anonymous caller. subscribeProxyUrl, if set, is consulted once per concrete channel while the token is minted, and it is fail-closed: a channel your callback does not allow produces no capability.

Grants

A grant ties three things together, and stands until you revoke it.

FieldMeaning
subjectThe identity the grant applies to, or * for every identity in the product
patternA channel pattern such as chat:*
opsThe operations granted on every channel the pattern matches

In a pattern, * stands for exactly one segment, and the number of segments must match: chat:* matches chat:room-1 but not chat:room-1:typing, and orders:*:* matches orders:acme:42. The namespace segment is always concrete — you cannot write *:room-1 — and a pattern carries at most eight segments in total.

Where grants apply, and where they do not, is the practical distinction:

CallerNeeds a grantHow its capabilities are decided
A platform identityYesGrants are resolved when a token is minted for that identity, together with the public visibility rule
One of your own end usersNoYour backend states which concrete channels and operations the token should carry, and namespace policy narrows that declaration. No wildcards

Grants are managed from the platform: over MCP, realtime_grant_create, realtime_grant_list and realtime_grant_revoke.

What you build against

SurfaceUse it for
@lessly/realtime — the server SDKYour backend: publish, read history, mint tokens for your users
@lessly/realtime-client — the browser clientThe tab: connect, subscribe, take part in presence, reconnect
The public HTTP routesA backend that does not use the SDK, reached at https://public.lessly.com/{productId}/realtime with your API key in the X-Api-Key header

Managing namespaces, grants, API keys and webhooks is not part of that public surface — you do those from the platform.

Next steps

Was this page helpful?
Esc

Start typing to search the docs.

navigateselect