Topic patterns
Learn to use wildcards and patterns in MQTT topic strings for ACL rules.
Topic patterns let you match many topics with a single ACL rule. Use this guide to understand + and # wildcards and write rules that scale across users without one rule per topic.
MQTT topic structure
Topics are hierarchical strings separated by /:
agents/team-alpha/agent-001/state
| | | |
| | | L Field type
| | L Agent ID
| L Group
L CategoryWildcards
MQTT defines two wildcards for topic matching.
Single-level wildcard: +
Matches exactly one level in the topic hierarchy.
Pattern: agents/+/state
Matches:
agents/agent-001/state
agents/agent-002/state
agents/abc123/state
Does NOT match:
agents/state (missing a level)
agents/team-a/agent-001/state (too many levels)Multi-level wildcard: #
Matches zero or more levels. Must be the last character.
Pattern: agents/#
Matches:
agents
agents/state
agents/agent-001/state
agents/team-a/agent-001/inbox/task-42
Does NOT match:
rooms/room-9 (different root)Pattern examples
Match all topics under a prefix
agents/#
Matches everything under "agents":
agents/agent-001/state
agents/agent-001/inbox
agents/team-a/agent-002/stateMatch specific level structure
teams/+/agents/+/inbox
Matches:
teams/alpha/agents/001/inbox
teams/beta/agents/lead/inbox
Does NOT match:
teams/alpha/inbox (wrong structure)Match all fields for any agent
teams/+/agents/+/#
Matches:
teams/alpha/agents/001/state
teams/alpha/agents/001/inbox
teams/alpha/agents/001/logs/debugCombine multiple + wildcards
+/+/state
Matches:
agents/agent-001/state
rooms/room-9/state
anything/anything/stateUsername substitution
CloudSignal supports special variables in topic patterns.
%u username substitution
Replaced with the connecting user's username:
User: agent-001
Pattern: agents/%u/inbox
Resolves to: agents/agent-001/inboxThis creates dynamic, per-user topic access:
ACL rule:
User: agent-%
Topic: agents/%u/#
Access: pubsub
Result:
agent-001 can access agents/agent-001/#
agent-002 can access agents/agent-002/#
(each agent isolated to its own namespace)%c client ID substitution
Replaced with the MQTT client ID:
Client ID: mobile-app-user-123
Pattern: clients/%c/inbox
Resolves to: clients/mobile-app-user-123/inboxUsername (%u) is generally more predictable since you control it in the dashboard. Client ID (%c) is set by the connecting client.
Wildcard rules
Placement rules
| Pattern | Valid? | Notes |
|---|---|---|
agents/+/state | Yes | + between levels |
agents/# | Yes | # at end |
agents/+/# | Yes | Combine both |
agents# | No | # must follow / |
agents/state+ | No | + must be entire level |
agents/#/state | No | Nothing after # |
Examples of invalid patterns
agents/state+ate # + must be entire level
agents/#/inbox # # must be last
agents+ # + must follow /
#agents # # must follow /Common patterns reference
Agent isolation
Each agent can only access its own topics:
User: agent-%
Topic: agents/%u/#Hub and spoke
Agents publish, central service subscribes:
# Agents
User: agent-%
Topic: agents/%u/state
Access: publish
# Backend
User: backend
Topic: agents/#
Access: subscribeBroadcast to all agents
Service publishes to all agents:
# Agents listen
User: agent-%
Topic: agents/broadcast
Access: subscribe
# Backend broadcasts
User: backend
Topic: agents/broadcast
Access: publishMulti-tenant isolation
Each customer in their own namespace:
User: customer-a-%
Topic: customers/customer-a/#
User: customer-b-%
Topic: customers/customer-b/#Debugging patterns
Test your patterns
Before creating rules, verify patterns match expected topics:
Pattern: agents/+/state/#
Test topics:
agents/agent-001/state/load -> MATCH
agents/agent-001/state -> MATCH
agents/agent-001/inbox -> NO MATCH (state vs inbox)
agents/state -> NO MATCH (missing level)Check rule order
CloudSignal evaluates rules until a match is found. More specific rules should come first:
1. agent-001 -> agents/agent-001/config -> subscribe (specific)
2. agent-% -> agents/%u/state -> publish (general)Next steps
- Common ACL patterns - Ready-to-use examples
- Create your first rule - Apply what you learned