MQTT Topic Structure
Relevant source files
The following files were used as context for generating this wiki page:The Overlord Network Kill Switch system uses MQTT as a message broker to facilitate communication between the user interface (HomeKit via homebridge-mqttthing) and the backend services. Node-RED acts as the orchestration layer, subscribing to MQTT topics, processing the messages, and forwarding them as API requests to the Gunicorn/FastAPI server. This structure decouples the frontend from the backend logic.
The topic structure is designed to be hierarchical and follows a command/status pattern. Command topics, prefixed with cmnd/, are used to trigger actions. Status topics, prefixed with stat/, are used to report the current state of a device or service. This allows for a responsive user interface that reflects the real-time status of the network controls.
Sources: README.md:10-14, README.md:101-112, node_red_flows/dns.json, AGENTS.md:9
Topic Namespace and Hierarchy
The MQTT topics are organized under two main controllers: dns_controller for Pi-hole related actions and router_controller for Ubiquiti firewall and device actions.
The general topic structure is:
<prefix>/<controller>/<scope>/<target>/<action>
<prefix>:cmnd(command) orstat(status).<controller>:dns_controllerorrouter_controller.<scope>: A logical grouping, such asmedia,master,change_rule, orstatus_rule.<target>: The specific service, rule, or device being controlled (e.g.,hbomax,school_work).<action>: The verb describing the operation, likechangeorstatus.
This hierarchy is parsed within Node-RED to determine the appropriate backend API endpoint to call.
The following diagram illustrates the topic hierarchy.
DNS Control (dns_controller)
Topics under dns_controller are used to manage Pi-hole domain blocking. This is split into controlling specific domain groups (e.g., for a media service) and master control for all DNS blocking.
Per-Service Domain Blocking
These topics control domain blocklists defined in the [block_domains] or [allow_domains] sections of the config.ini file.
| Topic Pattern | Purpose | Payload |
|---|---|---|
cmnd/dns_controller/media/{service}/change | Sent by HomeKit to enable or disable a domain block. | true (enable), false (disable) |
stat/dns_controller/media/{service}/status | Published by the system to report the current status of the block. | true (enabled), false (disabled) |
Sources: README.md:47-55, node_red_flows/dns.json:15-26, node_red_flows/dns.json:84-95
The Node-RED flow subscribes to cmnd/dns_controller/media/+/change. When a message is received, it parses the {service} from the topic and the boolean payload to construct a POST request to the backend API.
// From node_red_flows/dns.json
// This function translates the MQTT message to an HTTP request for the API.
let webserver = flow.get('endpoint');
// ... topic parsing ...
msg.topic = `stat/dns_controller/media/${msg.target}/status`
msg.url = `${webserver}/pihole/${msg.verb}/${msg.target}`
if (msg.verb == "change")
{
msg.method = "POST"
if (msg.payload == "true") msg.verb = "enable"
if (msg.payload == "false") msg.verb = "disable"
msg.url = `${webserver}/pihole/${msg.verb}/${msg.target}`
}
Sources: node_red_flows/dns.json:71-82
Master DNS Control
These topics are for globally enabling or disabling Pi-hole DNS blocking.
| Topic Pattern | Purpose | Payload |
|---|---|---|
cmnd/dns_controller/master/perm_change | Sent by HomeKit to permanently enable/disable all DNS blocking. | true (disable), false (enable) |
cmnd/dns_controller/master/short_change | Sent to temporarily disable DNS blocking (e.g., for 300 seconds). | true (disable) |
stat/dns_controller/master/status | Published to report the global DNS blocking status. | true (enabled), false (disabled) |
Sources: README.md:56-64, node_red_flows/dns.json:118-129, node_red_flows/dns.json:157-183
The following sequence diagram shows the flow for changing the master DNS status.
Service Availability (LWT)
A Last Will and Testament (LWT) topic is used to announce the online status of the backend service. This is used by homebridge-mqttthing to determine if the accessory is "online".
| Topic | Purpose | Payload |
|---|---|---|
stat/dns_controller/master/service_status | Reports if the backend API is running. Published on startup and as an LWT message. | true (online), false (offline) |
This is configured in homebridge-mqttthing using the getOnline key. The FastAPI application publishes true to this topic on startup and false on shutdown.
Sources: README.md:49, cgi-bin/controller.py:165-172, cgi-bin/controller.py:214-224
Ubiquiti Router Control (router_controller)
Topics under router_controller are used to manage Ubiquiti firewall rules.
| Topic Pattern | Purpose | Payload |
|---|---|---|
cmnd/router_controller/change_rule/{rule_name} | Sent to enable or disable a specific firewall rule. | true (enable), false (disable) |
stat/router_controller/status_rule/{rule_name} | Published to report the status of a firewall rule. | true (enabled), false (disabled) |
Sources: node_red_flows/dns.json:225-236, node_red_flows/dns.json:266-277
The Node-RED flow for router control is similar to the DNS control flow. It subscribes to cmnd/router_controller/change_rule/+, parses the {rule_name}, and constructs a GET request to the /ubiquiti/enable_rule/{target} or /ubiquiti/disable_rule/{target} endpoints.
// From node_red_flows/dns.json
// This function translates the MQTT message to a Ubiquiti API call.
let webserver = flow.get('endpoint');
// ...
else if (msg.verb == "change_rule") {
// OFF (Send to WebServer)
let intent = msg.payload
var method
if (intent == "false") {
router_path = "disable_rule";
}
if (intent == "true") {
router_path = "enable_rule";
}
}
// ...
msg.url = `${webserver}/ubiquiti/${router_path}/${msg.target}`
msg.topic = `stat/router_controller/status/${msg.target}`
Sources: node_red_flows/dns.json:289-315, lib/ubiquity/ubiquity.py:468-472
Summary
The MQTT topic structure is a critical component of the Overlord Network Kill Switch, providing a standardized and extensible way to issue commands and receive status updates. By using a hierarchical command/status pattern, the system maintains a clear separation of concerns between the user interface and the backend services, with Node-RED acting as the central message-to-API translator. This design allows for easy integration with MQTT-compatible systems like Homebridge and provides a clear convention for adding new controllable services in the future.
Sources: README.md, node_red_flows/dns.json