Skip to content

Overview

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:

  • Generated secret: the box mints 32 random bytes, shows them once on the reply as 64 hexadecimal characters, and keeps only their hash. Paste the secret into the device as its MQTT password. A secret of this size cannot be guessed at any key-derivation cost, so the box hashes it cheaply and a CONNECT verifies in about a millisecond. That speed decides how fast a fleet comes back after a restart.
  • Certificate: paste the device’s certificate (PEM) or its SHA-256 fingerprint. The box pins the fingerprint on the device and admits a connection whose TLS client certificate has it. Any certificate will do, a self-signed one included: the fingerprint is trusted, not the chain. The reply names the fingerprint so it can be checked against openssl x509 -fingerprint -sha256.
  • Generated certificate: the box makes a P-256 key and a self-signed certificate (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:

TopicDirectionPurpose
<slot>/$status/eventsserver → clientsJSON 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.

ConstraintEffect
Maximum QoSThe broker accepts publishes up to the QoS level allowed by your plan. Messages at a higher QoS are refused.
Maximum packet sizeAny 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 TestamentAvailability depends on your plan. When disabled, CONNECT packets with a Will are rejected. Devices must signal liveness some other way (heartbeat message, etc.).
Retained messagesAvailability 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 connectionsEvery 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.

PolicyWhat it means
Publishes per minuteMaximum PUBLISH packets the device may send within any wall-clock minute. Excess publishes are silently dropped.
Connect attempts per minuteMaximum CONNECT attempts within any wall-clock minute. Applies whether the attempt succeeds or fails.
Breach thresholdHow 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 sessionMaximum active topic-filter subscriptions a single MQTT session may hold. The counter resets on each fresh login.
MQTT triggers per taskMaximum number of MQTT Trigger operations allowed in a single Task.
MQTT actions per taskMaximum 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.

typeWhen it firesDevice effect
publish_throttleFirst publish over the per-minute limit within a wall-minuteWarning only. The current PUBLISH is dropped; the session stays open.
connect_throttleFirst connect over the per-minute limit within a wall-minuteWarning only. The current CONNECT is refused; earlier session unaffected.
publish_blockBreach threshold reached due to publish-rate violationsHard block. Device is disabled in the DB, session is force-disconnected, further connects are refused.
connect_blockBreach threshold reached due to connect-rate violationsHard block. Same as above.
admin_blockOperator disabled the device manuallyHard block. Same as above, but triggered manually.
unblockOperator re-enabled a blocked deviceBlock 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:

  1. 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.

  2. 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 }
  3. 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.

  4. 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/:

Terminal window
# 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-3

enable 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:

  • Concurrent connections per device. The MQTT spec’s one-session-per-clientId rule handles this. A second CONNECT with the same <slot>:<user_name> clientId evicts the first session. There is no separate “max connections per device” knob.
  • Maximum packet size. The broker-wide packet size limit applies equally to every device. There is no per-device payload tier.
  • Per-topic rate limits. Rate limits are per device, not per topic. A device using one topic and a device fanning out across ten topics are treated identically.

Key facts

  • MQTT clientId is <slot>:<user_name>. Topics must start with <slot>/.
  • Broker constraints (QoS level, packet size, will/retain availability) and per-device rate limits are defined by your plan — see the Acceptable Use Policy. The Configuration reference names the setting behind each one.
  • Every state transition (*_throttle, *_block, admin_block, unblock) fires a JSON event on <slot>/$status/events.
  • A blocked device is recovered through the slot admin interface.