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 -
allowordeny - Priority - Rules evaluate in priority order; the first match wins
A basic rule set might look like:
| Priority | Permission | Action | Topic pattern |
|---|---|---|---|
| 10 | ALLOW | publish | agents/agent-01/state |
| 20 | ALLOW | subscribe | agents/agent-01/inbox/# |
| 100 | DENY | publish | # |
| 100 | DENY | subscribe | # |
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}/stateWhen 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
| Variable | Resolves To | Example Pattern |
|---|---|---|
{username} | MQTT username | users/{username}/inbox |
{client_id} | MQTT client ID | clients/{client_id}/status |
{email} | User's email address | notifications/{email}/# |
{user_id} | Unique user identifier | private/{user_id}/# |
{agent_id} | Agent or bot identifier | agents/{agent_id}/commands |
{session_id} | Current session ID | sessions/{session_id}/events |
{channel} | Channel designation | channels/{channel}/messages |
Variable resolution flow
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:
| Wildcard | Meaning | Example |
|---|---|---|
+ | Matches exactly one topic level | agents/+/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/+/stateThis 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 set | Applies to |
|---|---|
users | End-user clients |
admins | Dashboard & admin tools |
agents | Autonomous 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
Authentication
How CloudSignal integrates with your existing auth provider - or uses token-based auth - so MQTT fits into your stack without a separate identity layer.
Pub/Sub & topics
How CloudSignal uses MQTT's topic-based publish/subscribe model - with wildcards, shared subscriptions, and ACL enforcement - to route messages exactly where they need to go.