Agents Explained
Relevant source files
The following files were used as context for generating this wiki page:Introduction
In the Overlord Network Kill Switch project, "Agents" are specialized Python modules that act as controllers for external network services. The primary agents are designed to interface with Pi-hole for DNS-level filtering and Ubiquiti UniFi Controllers for firewall rule and device-level network management. These agents are encapsulated within a FastAPI application, which exposes their functionality through a consistent REST API. This architecture simplifies complex network control tasks, making them accessible to higher-level automation systems like HomeKit (via Homebridge and Node-RED).
The main goal is to provide a simple, one-touch method for non-technical users to manage network access, such as implementing parental controls or creating a "focus time" environment by blocking distracting websites. The agents are the core components that translate simple API calls into specific commands for the Pi-hole and Ubiquiti systems.
Sources: README.md:4-10, AGENTS.md:5-7, cgi-bin/controller.py:110-115
High-Level Architecture
The agents are a central part of the application's logic layer, sitting between the message broker (MQTT) and the target network services. A user action, like toggling a switch in the HomeKit app, initiates a chain of events that ultimately invokes an agent's function.
The following diagram illustrates the overall system architecture and the agent's position within it.
This diagram shows the flow from a user's action in HomeKit to the execution of a command by an agent on a target service like Pi-hole or Ubiquiti.
Sources: README.md:100-119, AGENTS.md:9
Application Core
The FastAPI application defined in controller.py serves as the host for all agents. It manages their lifecycle, configuration, and routing.
Sources: cgi-bin/controller.py
Application Lifecycle
The application uses a lifespan asynchronous context manager to initialize and shut down resources. When the server starts, it reads the config.ini file, initializes the Pi-hole and Ubiquiti agents (if enabled), includes their respective API routers, and publishes a "true" status to an MQTT Last Will and Testament (LWT) topic. On shutdown, it gracefully closes agent sessions and publishes a "false" LWT status.
This flowchart shows the application's startup and shutdown sequence managed by the lifespan function.
Sources: cgi-bin/controller.py:155-180
Configuration
Agent behavior is controlled by ../etc/config.ini and environment variables. The init_config function parses this file to enable/disable agents and load their specific settings, such as Pi-hole server IPs, Ubiquiti controller details, and domain/MAC address targets.
| Section | Key | Description |
|---|---|---|
[general] | pihole_control_enabled | Enables or disables the Pi-hole agent. |
[general] | ubiquiti_control_enabled | Enables or disables the Ubiquiti agent. |
[general] | mqtt_announce_enabled | Enables or disables MQTT LWT status publishing. |
[block_domains] | (e.g., hbomax) | A list of regex patterns to block for a given group. |
[allow_domains] | (e.g., school_work) | A list of regex patterns to allow for a given group. |
[ubiquiti_targets] | (e.g., kids_devices) | A list of MAC addresses for a device group. |
[ubiquiti_rules] | (e.g., block_streaming) | A list of firewall rule names to manage. |
Sources: cgi-bin/controller.py:65-153, AGENTS.md:83-91
Pi-hole Agent
The Pi-hole agent is responsible for all interactions with Pi-hole instances. It uses the pihole6api library to communicate with Pi-hole v6. It is composed of a base handler and two specialized classes for different control scopes.
Sources: lib/pihole/base.py, lib/pihole/pihole.py, lib/pihole/alldns.py
Class Structure
The agent's logic is organized into a class hierarchy, with BaseHTTPHandler providing common connection logic for its subclasses.
This diagram shows the inheritance structure of the Pi-hole agent classes.
Sources: lib/pihole/base.py:11, lib/pihole/pihole.py:23, lib/pihole/alldns.py:20
BaseHTTPHandler
This base class in lib/pihole/base.py handles the core task of connecting to one or more Pi-hole instances.
__init__(): Initializes with configuration, including the list of Pi-hole IPs (remote_pi_list) and the API password.first_connect(): Iterates through thepiList, creating aPiHole6Clientsession for each and storing it in thesessionsdictionary. This method is called automatically when a command is issued to an uninitialized agent.shutdown(): Closes all activePiHole6Clientsessions.
Sources: lib/pihole/base.py:11-70
PiHoleOverlord
This class in lib/pihole/pihole.py manages domain-level blocking for specific groups defined in config.ini.
get(domain_block): Checks if a given domain block (e.g., "hbomax") is active. It queries each domain regex within the group and returns"false"if any domain is found to be disabled. Otherwise, it returns"true".post(direction, domain_block): Enables or disables a domain block.direction="enable": Adds all domains in the group to the Pi-hole blacklist (or whitelist, if configured).direction="disable": Removes all domains in the group.
Sources: lib/pihole/pihole.py:23-145
MasterEnabler
This class in lib/pihole/alldns.py manages the global DNS blocking status for all connected Pi-holes.
disable_dns_blocking(timer): Disables Pi-hole blocking across all instances for an optional duration in seconds.enable_dns_blocking(): Re-enables Pi-hole blocking on all instances.get(): Retrieves the current global blocking status. It checks for a consistent state across all Pi-holes and returns"unknown"if there is a mismatch.
Sources: lib/pihole/alldns.py:20-94
Pi-hole API Endpoints
The functionality of these classes is exposed via API endpoints defined in lib/pihole/pihole_router.py.
| Method | Endpoint | Agent Class | Description |
|---|---|---|---|
GET | /pihole/status/{domain_block} | PiHoleOverlord | Check if a domain block is enabled. |
POST | /pihole/enable/{domain_block} | PiHoleOverlord | Enable a domain block. |
POST | /pihole/disable/{domain_block} | PiHoleOverlord | Disable a domain block. |
GET | /alldns/ | MasterEnabler | Get global DNS blocking status. |
POST | /alldns/ | MasterEnabler | Disable global DNS blocking (with optional timer). |
DELETE | /alldns/ | MasterEnabler | Enable global DNS blocking. |
Sources: lib/pihole/pihole_router.py:38-79, AGENTS.md:52-66
Ubiquiti Agent
The Ubiquiti agent, defined in lib/ubiquity/ubiquity.py, interfaces with a Ubiquiti UniFi Network Controller to manage firewall rules and client device connectivity.
Sources: lib/ubiquity/ubiquity.py
UbiquitiOverlord Class
This is the sole class for Ubiquiti management. It uses the UniFi API key for authentication.
first_connect(): Initializes arequests.sessionand sets theX-API-KEYheader for all subsequent API calls.parse_firewall_rules(): Fetches all firewall policies from the controller, filters them against the rule names defined inconfig.ini, and caches the rule objects (including their_idandenabledstatus).check_rules_freshness(): Ensures the firewall rule cache is not stale (default TTL is 60 seconds) before performing an action. If stale, it triggersparse_firewall_rules().change_rule(requested_status, rule): Changes a firewall rule's state toenabledordisabled. It sends aPUTrequest to the.../firewall-policies/batchendpoint.change_device(requested_status, target): Blocks or unblocks a group of client devices. It iterates through the MAC addresses defined for the target group inconfig.iniand sends aPOSTrequest to the.../cmd/stamgrendpoint with theblock-staorunblock-stacommand.
Sources: lib/ubiquity/ubiquity.py:50-409
Ubiquiti API Endpoints
The UbiquitiOverlord methods are exposed via the /ubiquiti API router.
| Method | Endpoint | Description |
|---|---|---|
GET | /ubiquiti/status_rule/{target} | Get the status (true/false) of a firewall rule. |
GET | /ubiquiti/enable_rule/{target} | Enable a firewall rule. |
GET | /ubiquiti/disable_rule/{target} | Disable a firewall rule. |
GET | /ubiquiti/status_device/{target} | Get the status of a device group. |
GET | /ubiquiti/enable_device/{target} | Unblock a device group (sets status to "online"). |
GET | /ubiquiti/disable_device/{target} | Block a device group (sets status to "offline"). |
GET | /ubiquiti/refresh | Force a refresh of the firewall rule cache. |
Sources: lib/ubiquity/ubiquity.py:417-451, AGENTS.md:69-79
Interaction and Data Flow
The node_red_flows/dns.json file provides a clear example of how external systems interact with the agents via the API. An MQTT message triggers a Node-RED flow that constructs and sends an HTTP request to the FastAPI server.
The following sequence diagram illustrates the process of disabling the "hbomax" domain block.
This diagram shows the sequence of events from an MQTT command to the execution by the PiHoleOverlord agent and the final status update.
Sources: node_red_flows/dns.json, lib/pihole/pihole.py:113-145, lib/pihole/pihole_router.py:50-52
Conclusion
The agents in the Overlord Network Kill Switch project are the essential bridge between a high-level automation-friendly API and the low-level details of specific network hardware and software. By abstracting the complexities of the Pi-hole and Ubiquiti APIs into modular Python classes, the system provides a powerful, unified, and easily maintainable control plane for home network management. This design allows for straightforward integration with platforms like HomeKit and enables simple, user-friendly control over complex network rules.
Sources: README.md:4-10, AGENTS.md:5-7