Message retention
Use MQTT retained messages to store last-known-state and deliver immediate updates to new subscribers on CloudSignal.
Retained messages let CloudSignal store the last message published to a topic. When a new client subscribes, it immediately receives the retained message, useful for last-known-state scenarios like agent status, configuration, or current values.
How it works
| Step | What happens |
|---|---|
| Publisher sends retained message | Set retain=true when publishing |
| CloudSignal stores the message | Only the latest message per topic is kept |
| Subscriber connects later | Immediately receives the retained message |
| Updates replace old values | New retained messages overwrite previous ones |
Publishing retained messages
JavaScript (CloudSignal SDK)
// Publish with retain flag
client.transmit('agents/agent-01/state', { status: 'idle' }, { retain: true });
// Clear a retained message by publishing an empty payload
client.transmit('agents/agent-01/state', '', { retain: true });Python (paho-mqtt)
# Publish with retain flag
client.publish('agents/agent-01/state', '{"status":"idle"}', retain=True)
# Clear retained message
client.publish('agents/agent-01/state', '', retain=True)To clear a retained message, publish an empty payload with retain=true to the same topic.
Use cases
Agent presence
Show whether agents are online or offline:
// Agent publishes its status (retained)
client.transmit('agents/agent-01/state', { status: 'online' }, { retain: true });
// Dashboard subscribes - immediately knows agent status
await client.subscribe('agents/+/state');Current values
New subscribers immediately see the most recent value:
// Service publishes latest load (retained)
client.transmit('agents/agent-01/load', { load: 0.42 }, { retain: true });
// New dashboard component gets the value the moment it subscribesConfiguration distribution
Publish configuration that clients pick up when they connect:
// Admin publishes config (retained)
client.transmit('config/agent-type-a/settings', {
reportInterval: 60,
alertThreshold: 100,
}, { retain: true });
// Agents subscribe on boot and get the config immediatelyLast will and testament (LWT)
Combine retained messages with LWT for presence detection:
import CloudSignal from '@cloudsignal/mqtt-client';
const client = new CloudSignal({
preset: 'desktop',
will: {
topic: 'agents/agent-01/state',
payload: JSON.stringify({ status: 'offline' }),
retain: true,
},
});
// On connect, publish online status
await client.connectWithToken({ organizationId, externalToken });
client.transmit('agents/agent-01/state', { status: 'online' }, { retain: true });Retained messages vs. persistent sessions
These features complement each other.
| Feature | Purpose | Scope |
|---|---|---|
| Retained messages | Last-known-state for a topic | Per topic, visible to every subscriber |
| Persistent sessions | Queue messages for offline clients | Per client, private to that client |
- Retained messages answer "what's the current value?"
- Persistent sessions answer "what did I miss while offline?"
Use both together for resilient apps:
import CloudSignal from '@cloudsignal/mqtt-client';
// Agent uses persistent session so it doesn't miss commands
const client = new CloudSignal({
preset: 'server',
clean: false,
clientId: 'agent-01',
});
await client.connectWithToken({ organizationId, secretKey });
// Agent publishes retained status
client.transmit('agents/agent-01/state', { status: 'online' }, { retain: true });Limits
| Plan | Retained messages |
|---|---|
| Free | 100 topics |
| Pro | 1,000 topics |
| Max | 10,000 topics |
Each topic can hold one retained message (the latest one).
Retained messages persist until explicitly cleared or the topic limit is reached. Clean up unused retained messages to stay within limits.
Best practices
Use descriptive topics
# Good: clear hierarchy
agents/agent-01/state
agents/agent-01/load
chat/conv-7/last-message
# Avoid: flat structure
agent01-stateInclude timestamps
client.transmit('agents/agent-01/load', {
load: 0.42,
timestamp: Date.now(),
}, { retain: true });Clean up on shutdown
process.on('SIGTERM', () => {
// Clear retained status before exit
client.transmit('agents/agent-01/state', '', { retain: true });
client.destroy();
});Related
- Persistent sessions - Offline message queueing
- How CloudSignal works - Core concepts