MQTT Topic ACL Linter
August 10, 2026 · View on GitHub
A deterministic, local-only TypeScript library for reviewing MQTT topic-filter ACL documents. It flags invalid filters, dangerously broad wildcard grants, duplicate or subsuming rules, literal placeholder text, and optional RunMQTT policy-boundary findings.
Try the canonical online demo at https://runmqtt.com/mqtt-acl-linter.
Trust boundary
profile mqttcontains generic MQTT syntax and structural checks. These findings are based on MQTT topic-filter rules or conservative least-privilege review guidance.profile runmqttadds current RunMQTT-specific Policy Template constraints, tenant and device boundaries, and directional naming heuristics. These findings are not MQTT protocol requirements and may not apply to another broker.- Analysis runs entirely in the calling process. The library does not connect to a broker, call an API, persist input, or send telemetry.
- A clear result is not proof that a policy is secure. This is not a security audit and cannot evaluate broker-side precedence, inherited roles, provider transformations, or effective enforcement. Test allowed and denied operations against the real broker before deployment.
Requirements
- Node.js 22 or newer
- pnpm 10
pnpm install --frozen-lockfile
pnpm test
Input syntax
Input is a line-based document. Blank lines are ignored, and comments start with //.
profile mqtt|runmqtt
scope <literal-topic-prefix>
principal device|service <literal-id>
publish|subscribe|both <mqtt-topic-filter>
profile defaults to mqtt. A document needs at least one ACL rule. scope and principal provide context for RunMQTT-specific checks; they are not sent to RunMQTT or an MQTT broker. Publish and subscribe directions remain separate.
Generic MQTT example:
profile mqtt
publish factory/line-1/telemetry
subscribe factory/line-1/commands/#
RunMQTT-specific example:
profile runmqtt
scope tenants/acme
principal device sensor-42
publish tenants/acme/devices/sensor-42/telemetry
subscribe tenants/acme/devices/sensor-42/commands/#
Library usage
import { lintMqttAcl } from "./src/acl-linter";
const result = lintMqttAcl(`profile mqtt
subscribe factory/#`);
console.log(result.diagnostics);
The engine returns stable diagnostic codes, line numbers, MQTT or RunMQTT scope, and error, warning, or info severity. See the rule-set method for the complete input contract, diagnostic table, algorithm, fixtures, and limitations.
Development
pnpm format:check
pnpm typecheck
pnpm test
Changes to parsing or diagnostic behavior must update the fixtures, tests, and method document together. See CONTRIBUTING.md.
License
MIT © 2026 UllrAI.