Developer Guide
Relevant source files
The following files were used as context for generating this wiki page:This guide provides a technical overview of the Overlord Network Kill Switch project, intended for developers. It covers the system architecture, backend application structure, core logic modules, and configuration details. The project provides a web server that allows one-touch blocking of specific domains or entire network ranges on a per-device basis, integrating with HomeKit via MQTT.
The primary goal is to simplify network control for non-technical users through a user-friendly interface like HomeKit, while the backend handles the complexity of interacting with Pi-hole and Ubiquiti Network Controller APIs.
Sources: README.md:4-11
System Architecture
The system is composed of several loosely coupled components that communicate primarily through MQTT messages and HTTP requests. A user action in HomeKit triggers a chain of events that ultimately results in a change to Pi-hole DNS filtering or Ubiquiti firewall rules.
The following diagram illustrates the high-level data flow from the user to the target network services.
This diagram shows the overall architecture, from user interaction in HomeKit to the execution of commands on Pi-hole and Ubiquiti devices.
Sources: README.md:112-135, AGENTS.md:7-10
Project Structure
The repository is organized into distinct directories, each with a specific responsibility.
| Directory | Description |
|---|---|
cgi-bin/ | Contains the core FastAPI application, including the main controller, WSGI entry point, and Gunicorn configuration. |
lib/ | Houses the business logic for interacting with external services. It is subdivided into pihole/ and ubiquity/ modules. |
etc/ | Stores configuration files, such as config.ini and Python dependency lists. |
node_red_flows/ | Provides example Node-RED flows for orchestrating MQTT messages and API calls. |
Sources: AGENTS.md:16-32
Backend Application (FastAPI)
The core of the system is a FastAPI application responsible for exposing a REST API to control network services. It is served by Gunicorn, as defined in cgi-bin/gunicorn_config.py.
Sources: cgi-bin/controller.py, cgi-bin/gunicorn_config.py, cgi-bin/wsgi.py
Application Lifecycle
The application's startup and shutdown logic is managed by a lifespan async context manager.
This flowchart shows the sequence of operations during the application's startup and shutdown phases.
During startup, the lifespan manager:
- Initializes configuration from
etc/config.iniviainit_config. - Conditionally initializes and includes the
piholeandubiquityAPI routers based on the configuration. - If enabled, publishes a "true" (online) message to the configured MQTT Last Will and Testament (LWT) topics.
During shutdown, it gracefully closes sessions with Pi-hole and Ubiquiti and publishes a "false" (offline) message to the LWT topics.
Sources: cgi-bin/controller.py:192-218
Configuration
Application settings are loaded from ../etc/config.ini and can be overridden by environment variables. The init_config function in controller.py parses these settings.
| Section | Key | Environment Variable | Description |
|---|---|---|---|
[general] | pihole_control_enabled | - | Enables/disables the Pi-hole control module. |
[general] | ubiquiti_control_enabled | - | Enables/disables the Ubiquiti control module. |
[general] | mqtt_announce_enabled | - | Enables/disables MQTT LWT status publishing. |
[general] | remote_pi_list | REMOTE_PI_LIST | Space-separated list of Pi-hole IP addresses. |
[general] | remote_pi_password | REMOTE_PI_PASSWORD | Password for Pi-hole admin interface. |
[ubiquiti] | remote_ubiquiti_device | REMOTE_UBIQUITI_DEVICE | IP address of the UniFi controller. |
[ubiquiti] | remote_ubiquiti_api_key | REMOTE_UBIQUITI_API_KEY | API key for the UniFi controller. |
[mqtt] | broker | MQTT_BROKER | MQTT broker address. |
[mqtt] | port | MQTT_PORT | MQTT broker port. |
[mqtt] | lwt_topics | - | Newline-separated list of topics for LWT status messages. |
[block_domains] | (dynamic) | - | Groups of domain regexes to block. |
[allow_domains] | (dynamic) | - | Groups of domain regexes to allow. |
[ubiquiti_targets] | (dynamic) | - | Groups of MAC addresses for device blocking. |
[ubiquiti_rules] | (dynamic) | - | Names of firewall rules to manage. |
Sources: cgi-bin/controller.py:64-162, AGENTS.md:83-91
API Endpoints
The application exposes endpoints grouped by functionality.
Pi-hole Endpoints (/pihole, /alldns)
| Method | Path | Description |
|---|---|---|
GET | /pihole/status/{domain_block} | Checks if a specific domain block group is enabled. |
POST | /pihole/enable/{domain_block} | Enables a domain block group. |
POST | /pihole/disable/{domain_block} | Disables a domain block group. |
GET | /alldns/ | Gets the global DNS blocking status across all Pi-holes. |
POST | /alldns/ | Disables global DNS blocking, with an optional timer parameter. |
DELETE | /alldns/ | Enables global DNS blocking. |
Sources: lib/pihole/pihole_router.py:44-79, AGENTS.md:37-45
Ubiquiti Endpoints (/ubiquiti)
| Method | Path | Description |
|---|---|---|
GET | /ubiquiti/status_rule/{target} | Gets the status (enabled/disabled) of a firewall rule. |
GET | /ubiquiti/enable_rule/{target} | Enables a firewall rule. |
GET | /ubiquiti/disable_rule/{target} | Disables a firewall rule. |
GET | /ubiquiti/status_device/{target} | Gets the status (online/offline) of a device group. |
GET | /ubiquiti/enable_device/{target} | Unblocks a device group (sets to 'online'). |
GET | /ubiquiti/disable_device/{target} | Blocks a device group (sets to 'offline'). |
GET | /ubiquiti/refresh | Forces a refresh of the cached firewall rules from the controller. |
Sources: lib/ubiquity/ubiquity.py:503-535, AGENTS.md:48-58
Core Logic Modules
Ubiquiti Integration
The lib/ubiquity/ubiquity.py module handles all interactions with the Ubiquiti Network Controller.
UbiquitiOverlord Class
This Pydantic-based class encapsulates the logic for managing firewall rules and client devices.
This diagram shows the key properties and methods of the UbiquitiOverlord class.
Key Functionality:
- Authentication: Connects to the UniFi controller using an API key (
X-API-KEYheader). The previous username/password login method is deprecated. - Firewall Rule Management: It can enable or disable firewall rules by sending a
PUTrequest to the/proxy/network/v2/api/site/default/firewall-policies/batchendpoint. - Rule Caching: To improve performance, firewall rules are cached. The
check_rules_freshness()method ensures the cache is re-populated from the controller if it's older thanrule_cache_ttl(default 60 seconds). - Device Blocking: It can block and unblock client devices by MAC address by POSTing to the
/proxy/network/api/s/default/cmd/stamgrendpoint withblock-staorunblock-stacommands.
Sources: lib/ubiquity/ubiquity.py:58-535
The sequence diagram below shows the process of changing a firewall rule's state.
This diagram illustrates the flow for enabling a firewall rule, including the cache check and API calls.
Sources: lib/ubiquity/ubiquity.py:382-446
Pi-hole Integration
Interaction with Pi-hole is managed by classes in the lib/pihole/ directory, which all inherit from a common BaseHTTPHandler. The integration relies on the pihole6api library for communication with Pi-hole v6.
Sources: lib/pihole/base.py, etc/webserver_requirements.txt:19
This diagram shows the inheritance structure for the Pi-hole handler classes.
Sources: lib/pihole/base.py:11, lib/pihole/pihole.py:23, lib/pihole/alldns.py:21
BaseHTTPHandler: This base class inlib/pihole/base.pymanages the connection and session handling for multiple Pi-hole instances. Thefirst_connect()method iterates through theremote_pi_listfrom the config and creates aPiHole6Clientsession for each.PiHoleOverlord: Located inlib/pihole/pihole.py, this class handles domain-level blocking. It can enable or disable predefined groups of regex-based domain filters by adding or deleting them from the Pi-hole lists.MasterEnabler: Found inlib/pihole/alldns.py, this class provides global control over DNS blocking. It can enable or disable blocking on all configured Pi-hole instances, with an option to set a timer for temporary disabling.
Sources: lib/pihole/base.py:48-66, lib/pihole/pihole.py:89-148, lib/pihole/alldns.py:68-91
Orchestration with Node-RED
Node-RED acts as the bridge between the MQTT broker and the FastAPI backend. It subscribes to MQTT topics, parses the incoming messages, and translates them into the appropriate HTTP API calls to the server.
The example flow in node_red_flows/dns.json demonstrates this logic.
This flowchart depicts the Node-RED logic for handling a Pi-hole media domain block change request received via MQTT.
- An MQTT In node subscribes to topics like
cmnd/dns_controller/media/+/change. - A Change node uses JSONata to parse the topic string to extract the
target(e.g.,hbomax) andverb(e.g.,change). - A Function node constructs the full URL for the FastAPI endpoint (e.g.,
http://XXXX:19000/pihole/enable/hbomax). - An HTTP Request node makes the
POSTcall to the server. - The response from the server is processed, and the new status is published back to an MQTT status topic (e.g.,
stat/dns_controller/media/hbomax/status), which updates the switch state in HomeKit.
Similar flows exist for managing global DNS (/alldns) and Ubiquiti rules/devices (/ubiquiti).
Sources: node_red_flows/dns.json
Dependencies and Environment
- Python: The project requires Python
>=3.12. - Key Libraries: The application relies on a set of core Python libraries:
fastapi,gunicorn,uvicorn: For the web server and API framework.pihole6api: The client library for interacting with the Pi-hole v6 API.requests: Used by the Ubiquiti module for HTTP communication.aiomqtt: For asynchronous publishing of status messages to the MQTT broker.
- Type Checking:
pyrightis used for static type checking, with configuration defined inpyrightconfig.jsonto enforce basic type safety.
Sources: pyproject.toml:5, etc/webserver_requirements.txt, pyrightconfig.json:2