Topic Permissions
Topic permissions
Topic permissions answer one question: may this account publish or subscribe to this topic? They are kept in the accounts database with the accounts and groups, so every node of a cluster applies the same rules, and they are edited on thePermissions page in the configuration interface.
Each subject has one policy: an ordered list of rules. A rule says whether it allows or denies, which direction it applies to (publish orsubscribe), and a topic filter. The list is checked from the top and the first matching rule decides, so an allow above a deny lets the deny's topic through; the order is part of the policy and can be edited. The subject is one of:
| Group | Applies to |
|---|---|
user:<name> | That one account, by its username. |
group:<name> | Every account that belongs to the group on the Users page. Thegroup: prefix is how the interface shows it, so a group cannot be mistaken for a username; the stored rule keeps the bare name, and so does an imported rule. |
anonymous | Clients that connect without a username. |
A client is resolved to user:<name>, then to the groups of its account, and a pattern rule from an import applies to every client; theanonymous subject applies only when there is no username. Rules from all of those subjects share one global order, which is why the list a subject shows is its part of that order rather than a private sequence. When a client has rules but none of them covers the topic, the answer is deny: its rules are an allow-list, exactly as in Mosquitto. The setting below decides only clients that have no rules at all.
An import keeps the order Mosquitto would check its rules in: the client's roles first, then its groups by priority, then the roles in each group, with each role's ACLs by their priority. Editing a subject's policy replaces its rules in one step, keeping the other subjects' relative order and renumbering the shared sequence.
Clients with no matching rule. Mosquitto denies them when an ACL file is present. XMQ has the same choice as a setting on this page, and an import that brings at least one rule turns it on. An import of an empty file leaves the setting as it was: opening or locking the whole broker on no evidence would both be worse than leaving it alone.
Subscribe is also permission to receive what is published on that filter. There is no separate delivery permission.
A rule added on the page names a subject that already exists: the picker is a combo box of the groups and accounts the server knows, so a name that does not exist cannot be entered at all. An imported rule may name an account that has not been created yet - a file can describe rules before the accounts they belong to - and such a rule is shown like any other.
The Test access panel answers a specific question without changing anything, using the same evaluation the broker applies, and names the rule that decided.
Importing rules
See Importing from Mosquitto for the file formats, password verifiers, conflict handling and known limitations in one place. This section describes how the imported rules are evaluated.
The Import from Mosquitto page takes the password file and the ACL file as file inputs, read by the browser and sent as content: the XMQ server does not open/etc/mosquitto with its own privileges, and cannot be used to read a file the administrator could not. Either file may be left empty. Accounts and rules are applied in one database transaction: either both arrive or neither does. The import reads a Mosquitto acl_file or adynamic-security.json. It is planned before it is applied: rules already present are recognised and not duplicated, a rule an existing rule already covers is not added, and an allow that overlaps a deny is a conflict. Nothing is stored while a conflict is unresolved; the screen asks whether to keep the existing rule, use the imported one, or skip the imported one, and an import that is refused leaves the rule set as it was. Importing the same file twice adds nothing.
| Mosquitto | XMQ rule |
|---|---|
topic read F | subscribe allow on F |
topic write F | publish allow on F |
topic readwrite F | publish and subscribe allow on F |
topic deny F | publish and subscribe deny on F |
pattern ... %u / %c | One rule for every client, with the username or client id substituted when the rule is checked |
The password file understands Mosquitto 2.0 $7$ verifiers and clear-text lines. A 1.6 $6$ verifier is checked when the platform'scrypt() is available, and a 2.1 argon2id verifier when OpenSSL is 3.2 or newer; the Windows build has no crypt(), so$6$ is reported there. A verifier the build cannot check is listed as not imported rather than stored as an account nobody can sign in to. An imported verifier is checked on both paths - the interface's own accounts and the user-database extension that MQTT clients authenticate against - and is replaced with this broker's own at the first successful login.
This affects only verifiers read from a Mosquitto password file; XMQ's own accounts, the $7$ import and every other authentication path are unchanged.$6$ is unavailable on Windows, which has no crypt(), andargon2id is not compiled on a source build against OpenSSL older than 3.2 - for example Ubuntu 24.04 or older, Debian 12, RHEL 9 and FreeBSD 14. There the verifier is reported as not imported and that account's password has to be set again; the packaged Linux and BSD versions listed under Install all have OpenSSL 3.2 or newer.
Dynamic Security roles are flattened onto accounts: the rules a client gets through its roles and groups become rules on user:<name>, in the order Mosquitto would check them. An allowing defaultACLAccess becomes a# rule checked after everything else; a denying one needs no rule, because no match is already a deny. Rules that cannot be represented are listed as not imported, with the reason, rather than dropped silently.
Many devices from a spreadsheet
An XLS or XLSX file is not read directly. Save the sheet asCSV UTF-8, then make one username:password line per device. With the username in column A and the password in column B, a third column with=A2&":"&B2 produces the line; a small script does the same for larger sheets. The result is the password file described above, so it is imported through Import from Mosquitto: clear-text lines are accepted there and are hashed with this broker's verifier.
That import puts every account in Default; it has no column for groups, so a fleet that needs a group per device type cannot be expressed this way yet. When a pilot needs a concrete layout - for example username,password,groups - a CSV import with that shape will be added rather than guessed at now.
The file holds clear passwords: send it over HTTPS, delete it after the import, use unique passwords rather than one shared secret, and look at the import plan for repeated usernames before applying it. The plan never shows a password. For very large files, import in batches; the broker hashes every password as it adds the account.
Known limitations
- Per-listener rules are not available. A rule applies to every MQTT and MQTT+SSL listener on the node. Separating what one listener permits from another has to be done with separate accounts or separate clusters for now.
- Read is subscribe. Mosquitto's
readand XMQ'ssubscribeare the same permission, and XMQ has no separate permission for receiving a message that only the delivery step would need. A Dynamic SecuritypublishClientReceiveallow is therefore redundant and is reported as not imported; a denial is carried as a subscribe deny. - A subscription is matched as a filter. Dynamic Security distinguishes
subscribeLiteralfromsubscribePattern; both are stored as a filter here. For ordinary clients the two are the same, but a rule written to permit only one literal subscription also permits the equivalent wildcard. %cis only as good as the client id. A client id is chosen by the client unless it is bound by authentication (a Dynamic Securityclientid,use_username_as_clientid, or unique credentials per device). A client id or username containing+,#or/matches no pattern rule, so it cannot widen one.- An empty topic level is refused. MQTT allows a topic such as
/finance, but a rule filter that names an empty level is treated as a typo by the control API and by the import alike.+and#must each be a whole level, and#the last one.
