An MQTT retained message answers “what is the last value published on this topic?” Message replay answers “what happened after my last processed position?” They look similar only when a topic has received one message.
Choosing the correct model avoids both lost history and unnecessary storage.
Four different delivery questions
| Mechanism | Question it answers | History kept |
|---|---|---|
| Live broadcast | Who is connected right now? | None for ordinary live delivery |
| Retained message | What value should a new subscriber see for this topic? | One retained value per topic |
| Latest-value channel | What is the current stored value and age for each matching topic? | One current value per topic |
| Append channel | What records has this consumer not acknowledged? | A bounded ordered log plus consumer positions |
MQTT persistent sessions add another mechanism: the broker preserves a session's subscriptions and eligible QoS deliveries while that client is away. This is session continuity, not a general-purpose event log that readers can independently seek.
Use retained messages for a published current value
A publisher sets the MQTT RETAIN flag when a message should become the retained value for its topic. A new matching subscriber receives that value immediately. A later retained publish replaces it.
Good examples include:
- the desired mode of a device;
- the last status deliberately published as retained;
- a small configuration value;
- a presence or discovery record with a clear lifecycle.
Retained messages are part of MQTT and need no Sagüin channel. They are not a historical sequence.
Use latest-value state for a managed topic region
Sagüin's latest channel keeps the current value for each topic claimed by its filter, together with channel position and age. It is useful when the topic hierarchy is a state tree and subscribers care about what is true now.
channels:
device-state:
type: latest
filter: fleet/+/state/#
A subscriber can receive current values on subscribe without replaying every transition that produced them. This is typically a better fit for dashboards, device twins, last-known sensor state, or configuration snapshots.
Use append when the transitions matter
An append channel stores an ordered, bounded history. Each durable consumer has its own position, so one consumer can catch up after an outage without changing what another consumer has read.
channels:
device-events:
type: append
filter: fleet/+/events/#
Choose append for audit trails, telemetry history, billing inputs, maintenance events, and integration feeds where losing intermediate records changes the meaning of the data.
A practical naming split
Many systems need both models. Make that visible in the topic design:
fleet/truck-17/state/location current value
fleet/truck-17/state/ignition current value
fleet/truck-17/events/position historical event
fleet/truck-17/events/engine-start historical event
fleet/truck-17/work/reindex one-worker job
Then assign non-overlapping or deliberately prioritised channel filters. The topic name tells an operator whether a value is state, history, or work before they inspect the broker configuration.
Decision checklist
Ask these questions in order:
- Is it acceptable for a disconnected consumer to miss the message? Use broadcast.
- Does a new subscriber need only one publisher-selected current value? Use a retained message.
- Do you need one managed current value per topic, with Sagüin's state semantics? Use latest.
- Must each consumer process every retained record in order? Use append.
- Must exactly one worker attempt each available job? Use queue.
The detailed comparison and reconnect rules are in RFC 0003: Delivery semantics. Channel routing and overlap rules are in RFC 0002: Channels and configuration.