CloudSignal Docs
Core Concepts

Access lists

Dynamic topic-level permissions with identity variables - control exactly what each user, device, or agent can publish and subscribe to.

Access control in most messaging systems is static: you define fixed topic patterns and assign them to users. CloudSignal's access lists go further - they support dynamic variable substitution that resolves topic permissions based on the connecting user's identity, agent ID, session, and other claims at connection time.

This means you write rules once, and they automatically scope to each user without creating per-user entries.

How access lists work

Each rule in an access list specifies:

  • Topic pattern - Which topics the rule governs (supports MQTT wildcards + and #)
  • Action - Whether it applies to publish, subscribe, or both
  • Permission - allow or deny
  • Priority - Rules evaluate in priority order; the first match wins
ACL Evaluation
Client publishes
Topic request
Broker evaluates ACL
First match
Allow or Deny

A basic rule set might look like:

PriorityPermissionActionTopic pattern
10ALLOWpublishagents/agent-01/state
20ALLOWsubscribeagents/agent-01/inbox/#
100DENYpublish#
100DENYsubscribe#

This lets agent-01 publish its state and receive inbox messages, while blocking everything else. The deny-all rules at lower priority act as a safety net.

Dynamic variables - the key advantage

Static rules work for a handful of devices. But what happens when you have thousands of users, each needing access to their own topics? Creating individual rules per user does not scale.

CloudSignal solves this with identity-bound variables that resolve at connection time. Instead of hardcoding a username into a topic pattern, you use a placeholder:

agents/{username}/state

When user agent-01 connects, this evaluates to agents/agent-01/state. When user agent-02 connects, it becomes agents/agent-02/state. One rule, every user scoped correctly.

Available variables

VariableResolves ToExample Pattern
{username}MQTT usernameusers/{username}/inbox
{client_id}MQTT client IDclients/{client_id}/status
{email}User's email addressnotifications/{email}/#
{user_id}Unique user identifierprivate/{user_id}/#
{agent_id}Agent or bot identifieragents/{agent_id}/commands
{session_id}Current session IDsessions/{session_id}/events
{channel}Channel designationchannels/{channel}/messages

Variable resolution flow

Variable Resolution
Rule Pattern
agents/{agent_id}/commands
Client connects
agent_id = assistant-v2
Resolved
agents/assistant-v2/commands

Variables are populated from the claims attached to the user's authentication - whether those come from an auth provider JWT, the Token Service, or the API. If a variable cannot be resolved (the claim is missing), the rule does not match, which means access is denied by default. No accidental wildcard grants.

Dynamic variables let you write one rule set for your entire organization. A single pattern like users/{username}/# automatically isolates every user to their own topic namespace - no per-user rule management required.

Practical patterns

Per-user isolation

Every user gets their own topic space. No user can read or write another user's topics.

ALLOW  publish    users/{username}/outbox/#
ALLOW  subscribe  users/{username}/inbox/#
DENY   publish    #
DENY   subscribe  #

AI agent scoping

Each AI agent is confined to its own command and response topics:

ALLOW  publish    agents/{agent_id}/responses/#
ALLOW  subscribe  agents/{agent_id}/tasks/#
DENY   publish    #
DENY   subscribe  #

Shared channels with identity

Users can publish to shared channels but only read their own notifications:

ALLOW  publish    channels/{channel}/messages
ALLOW  subscribe  channels/{channel}/messages
ALLOW  subscribe  notifications/{user_id}/#
DENY   publish    notifications/#
DENY   subscribe  #

Agent fleet coordination

Each agent publishes its own state and receives work items, plus a shared broadcast topic for fleet-wide signals:

ALLOW  publish    fleet/{client_id}/state
ALLOW  subscribe  fleet/{client_id}/inbox/#
ALLOW  subscribe  fleet/broadcast/#
DENY   publish    #
DENY   subscribe  #

The broadcast topic lets you push configuration changes or coordination signals to the entire fleet, while each agent's state and inbox remain isolated.

MQTT wildcards in rules

Access list patterns support standard MQTT wildcards:

WildcardMeaningExample
+Matches exactly one topic levelagents/+/state matches agents/agent-01/state but not agents/agent-01/sub/state
#Matches zero or more levels (must be last)chat/# matches chat/, chat/a, chat/a/b/c

Wildcards and variables can be combined:

ALLOW  subscribe  {username}/agents/+/state

This lets each user subscribe to the state topic of any agent they own, one level deep.

Rule set organization

Each organization can maintain multiple rule sets - for example, one for end users, one for admin tools, and one for autonomous agents. Users are assigned to a rule set at creation time or updated later via the API.

An organization with three rule sets might map them like this:

Rule setApplies to
usersEnd-user clients
adminsDashboard & admin tools
agentsAutonomous agent fleet

Rule sets from one organization never affect another. Each organization's access control is fully independent.

Why this matters

Most messaging platforms offer only static ACLs - you define fixed topic strings and assign them manually. As your application grows, this becomes a maintenance burden: every new user or device type requires new rules.

CloudSignal's dynamic variables eliminate this scaling problem. You define patterns that express your access policy in terms of identity, and the broker resolves them for every connection. The result is access control that scales to thousands of users with a handful of rules.


Next steps

  • ACL guide - Step-by-step instructions for creating and managing rules
  • Topic patterns - Deep dive into wildcards and variable substitution
  • Authentication - How users get the identity claims that variables resolve from

On this page