MQTT Trigger
Subscribe to a topic and start an Execution Path when a message arrives. Reference →
The MQTT Broker extension lets a Task react to MQTT messages and publish MQTT messages. It exposes two operations — MQTT Trigger subscribes to a topic and starts an Execution Path on each received message; MQTT Action publishes one message per run.
This page covers everything you need to know to build Tasks with those two operations: how devices authenticate, which topics they are allowed to touch, what the broker enforces, the per-user rate limits, and what happens when a device crosses them. The Configuration reference names the host setting behind each behaviour described here.
MQTT Trigger
Subscribe to a topic and start an Execution Path when a message arrives. Reference →
MQTT Action
Publish one message per run. Terminal action, no outputs. Reference →
A slot owns a fixed set of MQTT devices. Each device has a user_name and a credential, provisioned through the slot admin interface (see Recovering a blocked device for the CLI).
A device’s credential is either a secret the box generates or a certificate. Neither can be guessed. The form that adds a device (and Set credential on an existing one) offers a Credential picker with three choices:
CONNECT verifies in about a millisecond. That speed decides how fast a fleet comes back after a restart.openssl x509 -fingerprint -sha256.CN=<slot>:<user_name>, valid for the number of days chosen on the form, ten years by default). It hands them back once as one PEM file, <slot>-<user_name>.pem, holding the private key and the certificate, and keeps only the fingerprint. Give the file to the device as both its certificate and its key.A certificate device presents its certificate in the TLS handshake on port 8883 and sends no password. The broker never runs the password verifier for it, so a fleet of certificate devices reconnects as fast as the edge can shake hands. The box does not check the certificate’s dates: a pinned certificate is admitted until the device is deleted or given another credential, which is how a certificate is revoked or rotated.
A secret device presents its secret as the MQTT CONNECT password. A device holds one credential; the kind is recorded on the device and shown in its list.
A device never gets a password a person chose. A chosen password is one a person could guess. The only defence against guessing is to lock the device out, and locking out an unattended device takes it offline. A credential nobody can guess needs no lockout.
When a device connects, its MQTT clientId is formed as:
<slot>:<user_name>The slot portion identifies which slot owns the device; the user portion is the device’s own user_name. The broker derives the slot and user from the clientId — it does not trust what the MQTT CONNECT packet declares in the username field alone.
If you see a device flap — connect, disconnect, connect, disconnect — the most common cause is two processes using the same credentials. Each new connection evicts the previous one. Give each physical device its own user_name.
All topics that a device publishes or subscribes to must start with the slot prefix:
<slot>/...The broker’s ACL rejects subscribes and publishes to any topic whose first level does not match the slot the device logged in to. This is enforced on every packet — you cannot leak messages between slots even if a device tries.
The slot publishes operational events on one well-known topic:
| Topic | Direction | Purpose |
|---|---|---|
<slot>/$status/events | server → clients | JSON events when a user is throttled, blocked, admin-disabled, or unblocked. See Lifecycle. |
Any topic starting with $ is reserved. Devices can subscribe to <slot>/$status/events (for example, to have a supervisor Task react to block events) but cannot publish to it.
The MQTT broker enforces a set of constraints at parse time. These are not per-user — they apply to every packet from every client. The specific values for your plan are listed in the Acceptable Use Policy.
| Constraint | Effect |
|---|---|
| Maximum QoS | The broker accepts publishes up to the QoS level allowed by your plan. Messages at a higher QoS are refused. |
| Maximum packet size | Any single MQTT packet larger than the plan limit is rejected at parse time and the connection is dropped. This covers the whole packet including headers and properties, so practical payloads are a few hundred bytes smaller. |
| Last Will and Testament | Availability depends on your plan. When disabled, CONNECT packets with a Will are rejected. Devices must signal liveness some other way (heartbeat message, etc.). |
| Retained messages | Availability depends on your plan. When not available, the broker accepts the retain flag on PUBLISH but does not store the message. Subscribers receive live messages only. |
| Anonymous connections | Every connection must authenticate with a valid slot, user and credential: the device’s generated secret as the CONNECT password, or its pinned certificate. |
Several policies constrain each individual MQTT device. The specific values for your plan are listed in the Acceptable Use Policy.
| Policy | What it means |
|---|---|
| Publishes per minute | Maximum PUBLISH packets the device may send within any wall-clock minute. Excess publishes are silently dropped. |
| Connect attempts per minute | Maximum CONNECT attempts within any wall-clock minute. Applies whether the attempt succeeds or fails. |
| Breach threshold | How many breached minutes within the last hour before the device is hard-blocked. A “breached minute” is any minute in which the device exceeded either the publish or connect rate. |
| Subscriptions per session | Maximum active topic-filter subscriptions a single MQTT session may hold. The counter resets on each fresh login. |
| MQTT triggers per task | Maximum number of MQTT Trigger operations allowed in a single Task. |
| MQTT actions per task | Maximum number of MQTT Action operations allowed in a single Task. |
The rate limits use a rolling wall-clock minute and a rolling hour. Once enough breached minutes pile up within an hour, the device is disabled in the database and force-disconnected.
A separate quota, connected devices, caps how many MQTT devices a slot can provision in total.
Every rate decision that changes a user’s state publishes a JSON event on <slot>/$status/events:
{ "type": "<event-type>", "user": "<user_name>", "until": <unix-seconds>, "ts": <unix-seconds> }ts is when the event was emitted. until is the unix-seconds deadline the block expires at, or 0 when there is no auto-expiry (admin disable or unblock). The payload is a single JSON object, published at QoS 0, not retained.
type | When it fires | Device effect |
|---|---|---|
publish_throttle | First publish over the per-minute limit within a wall-minute | Warning only. The current PUBLISH is dropped; the session stays open. |
connect_throttle | First connect over the per-minute limit within a wall-minute | Warning only. The current CONNECT is refused; earlier session unaffected. |
publish_block | Breach threshold reached due to publish-rate violations | Hard block. Device is disabled in the DB, session is force-disconnected, further connects are refused. |
connect_block | Breach threshold reached due to connect-rate violations | Hard block. Same as above. |
admin_block | Operator disabled the device manually | Hard block. Same as above, but triggered manually. |
unblock | Operator re-enabled a blocked device | Block cleared. Device can reconnect on next CONNECT. |
Throttle events are observational — subsequent soft-denies in the same wall-minute do not re-emit. You get one *_throttle per minute per user, which lets you build a supervisor that reacts to “this device is starting to misbehave” without drowning in events.
A device with user_name=pump-3 starts publishing faster than its plan allows. The event timeline looks like this:
First breached minute — pump-3 exceeds its per-minute publish limit. The first excess publish is dropped and a throttle event is broadcast:
{ "type": "publish_throttle", "user": "pump-3", "until": 0, "ts": 1744810860 }Further excess publishes in the same minute are silently dropped; no additional events.
Subsequent breached minutes — the same thing happens in each following minute. One publish_throttle event fires per breached minute:
{ "type": "publish_throttle", "user": "pump-3", "until": 0, "ts": 1744810920 }Hard breach — once enough breached minutes accumulate within an hour (the breach threshold for the plan), the device is disabled in the database, force-disconnected, and a block event is broadcast:
{ "type": "publish_block", "user": "pump-3", "until": 0, "ts": 1744811040 }From this point, any CONNECT attempt from pump-3 is refused — the credentials are valid but the device is disabled.
Operator recovers the device — a human investigates, fixes the device, and re-enables it (see next section). The block state is cleared and an unblock event is broadcast:
{ "type": "unblock", "user": "pump-3", "until": 0, "ts": 1744811700 }pump-3 can now reconnect.
A blocked device cannot unblock itself. An operator re-enables it through the slot admin interface. The reference tool is the Python CLI mqtt_admin.py shipped in nonbl/tools/nonbl-client/:
# List MQTT devices on the slot and their enabled state.python mqtt_admin.py --slot <slot> list
# Re-enable a blocked device.python mqtt_admin.py --slot <slot> enable --user-name pump-3
# Disable a device manually — fires an admin_block event.python mqtt_admin.py --slot <slot> disable --user-name pump-3enable works against any user whose enabled flag is currently false, regardless of whether the block came from publish_block, connect_block, or admin_block. It fires a single unblock event.
Some behaviours that look like they should be per-user limits are actually handled elsewhere:
CONNECT with the same <slot>:<user_name> clientId evicts the first session. There is no separate “max connections per device” knob.Key facts
<slot>:<user_name>. Topics must start with <slot>/.*_throttle, *_block, admin_block, unblock) fires a JSON event on <slot>/$status/events.