Data and Control Flow
Relevant source files
The following files were used as context for generating this wiki page:The Overlord Network Kill Switch system provides a simplified way to manage network access for devices and services within a home network. It integrates with Apple HomeKit, allowing non-technical users to perform actions like blocking streaming services or disabling internet for specific devices with a single tap. The control flow is designed to be decoupled, using an MQTT message broker to connect user actions from HomeKit to a backend server that executes commands on Pi-hole DNS servers and Ubiquiti network hardware.
This document outlines the end-to-end data and control flow, from a user's interaction with a HomeKit switch to the final action performed by the target service. The architecture relies on several distinct components: a user interface (HomeKit), a home automation hub (Homebridge), a message broker (MQTT), an orchestration layer (Node-RED), and a backend logic server (FastAPI) that communicates with the network services (Pi-hole, Ubiquiti).
Sources: README.md:4-10, AGENTS.md:5-9
High-Level Architecture
The system is composed of several layers that communicate primarily through MQTT messages and REST API calls. A user action in HomeKit triggers a chain of events that flow through the system to either a Pi-hole instance or a Ubiquiti controller.
The following diagram illustrates the main components and their interactions.
This diagram shows the two main flows: the command flow from user to service, and the status update flow from the service back to the user interface.
Sources: README.md:100-120, AGENTS.md:9
Control Flow Breakdown
1. User Interaction (HomeKit & Homebridge)
The flow begins when a user toggles a switch in the HomeKit app. These switches are configured in Homebridge using the homebridge-mqttthing plugin. Each switch is tied to specific MQTT topics for getting its state (getOn) and setting its state (setOn).
When a switch is toggled, homebridge-mqttthing publishes a message to the setOn topic on the MQTT broker. The payload is typically "true" or "false".
Example homebridge-mqttthing Configuration:
{
"type": "switch",
"name": "HBO",
"topics": {
"getOn": "stat/dns_controller/media/hbomax/status",
"setOn": "cmnd/dns_controller/media/hbomax/change"
},
"accessory": "mqttthing"
}
In this example, toggling the "HBO" switch publishes a message to cmnd/dns_controller/media/hbomax/change.
Sources: README.md:80-91
2. Orchestration (Node-RED)
Node-RED acts as the central orchestrator, listening for messages on the MQTT broker and translating them into API calls to the backend FastAPI server.
The provided dns.json flow defines the logic for handling different types of commands.
Node-RED Flow Logic:
- MQTT In Node: Subscribes to wildcard topics like
cmnd/dns_controller/media/+/changeandcmnd/router_controller/change_rule/+. - Parsing Function: A JavaScript function node extracts key information from the MQTT topic, such as the
target(e.g.,hbomax) and theverb(e.g.,change). - URL Construction: The function constructs the appropriate API endpoint URL for the FastAPI server based on the parsed information and the message payload. For example, a
changeverb with atruepayload is translated into anenableaction. - HTTP Request Node: Sends a
POSTorGETrequest to the constructed URL. - Response Handling: The response from the FastAPI server contains the new status. This payload is extracted.
- MQTT Out Node: The new status is published back to a
stat/...topic (e.g.,stat/dns_controller/media/hbomax/status), whichhomebridge-mqttthingis subscribed to, thus updating the switch's state in HomeKit.
The diagram below visualizes the logic for handling a Pi-hole domain block change.
This flow ensures that user commands are translated into the correct backend actions and that the UI reflects the true state of the system.
Sources: node_red_flows/dns.json:14-22, 54-82, 222-250
3. Backend Logic (FastAPI Server)
The FastAPI server, defined in cgi-bin/controller.py, is the brain of the operation. It exposes a REST API that Node-RED calls.
Application Lifecycle
On startup, the lifespan manager in controller.py performs several key actions:
- Configuration Loading: Reads
etc/config.inito get settings for Pi-hole, Ubiquiti, and MQTT. - Module Initialization: If enabled in the config, it initializes the
pihole_routerandubiquitymodules, creating singleton instances ofPiHoleOverlord,MasterEnabler, andUbiquitiOverlord. - Router Inclusion: It includes the API routers from the Pi-hole and Ubiquiti modules.
- MQTT LWT: It publishes a "Last Will and Testament" message to announce that the service is online.
On shutdown, it gracefully closes sessions to Pi-hole and Ubiquiti and publishes an offline LWT message.
Sources: cgi-bin/controller.py:192-221
API Endpoints
The server exposes endpoints grouped by functionality. The main routers are defined in lib/pihole/pihole_router.py and lib/ubiquity/ubiquity.py.
| Method | Path | Controller | Description |
|---|---|---|---|
GET | /pihole/status/{domain_block} | PiHoleOverlord | Check if a domain block is enabled. |
POST | /pihole/enable/{domain_block} | PiHoleOverlord | Enable a domain block list. |
POST | /pihole/disable/{domain_block} | PiHoleOverlord | Disable a domain block list. |
GET | /alldns/ | MasterEnabler | Get global DNS blocking status. |
POST | /alldns/ | MasterEnabler | Disable global DNS blocking (with timer). |
DELETE | /alldns/ | MasterEnabler | Enable global DNS blocking. |
GET | /ubiquiti/status_rule/{target} | UbiquitiOverlord | Get firewall rule status. |
GET | /ubiquiti/enable_rule/{target} | UbiquitiOverlord | Enable a firewall rule. |
GET | /ubiquiti/disable_rule/{target} | UbiquitiOverlord | Disable a firewall rule. |
GET | /ubiquiti/status_device/{target} | UbiquitiOverlord | Get device block status. |
GET | /ubiquiti/enable_device/{target} | UbiquitiOverlord | Unblock a device/group. |
GET | /ubiquiti/disable_device/{target} | UbiquitiOverlord | Block a device/group. |
Sources: AGENTS.md:40-61, lib/pihole/pihole_router.py:40-85, lib/ubiquity/ubiquity.py:461-497
4. Service Integration
The FastAPI server delegates the actual work to specialized controller classes.
Class Structure
The core logic is encapsulated in handler classes that inherit from a Pydantic BaseModel. The BaseHTTPHandler for Pi-hole manages connections to multiple Pi-hole instances.
Pi-hole Control Flow
- Connection: The
BaseHTTPHandler.first_connect()method iterates through the Pi-hole IPs defined in the configuration and establishes a session with each one using thepihole6apilibrary. - Domain Blocking (
PiHoleOverlord): When a request to enable or disable a domain block (e.g.,hbomax) is received, thepost()method iterates through the associated regex domains from the config file. For each domain, it callsadd_domainordelete_domainon each configured Pi-hole instance. - Global DNS (
MasterEnabler): Theflip_mode()method callsset_blocking_status()on each Pi-hole instance to globally enable or disable DNS filtering, optionally for a limited time.
Sources: lib/pihole/base.py:64-81, lib/pihole/pihole.py:112-137, lib/pihole/alldns.py:73-90
Ubiquiti Control Flow
- Connection: The
UbiquitiOverlord.first_connect()method establishes arequestssession using the API key provided in the configuration. - Firewall Rules: To change a firewall rule, the
change_rule()method first ensures its internal cache of rules is fresh by callingparse_firewall_rules(). It then constructs a payload with the rule's_idand the desired state (enabled: true/false) and sends aPUTrequest to the UniFi controller's/firewall-policies/batchendpoint. - Device Blocking: To block a device group,
change_device()iterates through the MAC addresses in that group. For each MAC, it sends aPOSTrequest to the/cmd/stamgrendpoint with a command of eitherblock-staorunblock-sta.
Sources: lib/ubiquity/ubiquity.py:192-200, 362-401, 429-458
End-to-End Sequence Diagram
This sequence diagram illustrates the complete flow for a user disabling the "HBO" domain block from their iPhone.
This diagram shows how a simple user action is propagated through multiple systems to achieve the desired outcome, with status feedback closing the loop.
Sources: README.md:80-91, node_red_flows/dns.json, lib/pihole/pihole_router.py:56-60, lib/pihole/pihole.py:112-137