User Guide
Welcome to the User Guide for Pharos Advanced Blocking (pab). This guide covers setup, credentials, the interactive TUI, and command-line operations.
Quickstart: From Zero to a Confirmed Block
1. Bootstrap Your Configuration (First Time Only)
If you don’t already have a dnsApp.config file, point pab at your running Technitium server — no copy-paste, no blank JSON file:
pab init
pab init resolves your Technitium credentials, fetches the server’s live Advanced Blocking configuration (or falls back to a starter template if the server has none yet), previews what it found, and asks you to confirm before writing anything to disk. See Installation & Credentials below for how to set up TECHNITIUM_URL/TECHNITIUM_TOKEN (one node) or secrets.json (two or more nodes) — or just run pab init with nothing set up yet and it will prompt you for them.
2. Run pab (Interactive Mode)
Open your terminal in the directory containing your Technitium dnsApp.config file and type:
pab
If you’re in a terminal (TTY), the interactive TUI dashboard opens immediately.
In non-TTY environments (CI/CD pipelines, container orchestration, cron jobs):
pab will exit with a helpful error message explaining that the TUI requires an interactive terminal. Use CLI commands instead:
pab map --ip <ip> --group <group>— Add or update a client IP mappingpab deploy --dry-run— Preview changes before applyingpab deploy -f— Apply changes to your Technitium instances in CI/CDpab verify --domain <domain>— Confirm a domain is actually being blocked
This is by design: the TUI is built for humans exploring interactively; CLI commands are designed for scripts, automation, and non-interactive environments. You get the full power of pab in either mode—just pick the right tool for your workflow.
3. Explore Your Configuration
The TUI shows:
- Your current client IP → group mappings
- Real-time search by IP or group name
- Available blocking groups and device counts
4. Use Slash Commands
Type /help to see all available commands, or try:
/view groups— List all groups with device counts/view group Kids— Show details for the “Kids” group/clear— Reset search and filters/exit— Close the TUI
5. Use Tab Completion for Command Names
Type /v[Tab] — it auto-completes to /view (with trailing space). Tab completion works for command names only (e.g., /e[Tab] → /exit ). Group name autocomplete in search mode is coming in a future release—for now, Tab navigates through filtered results.
6. Exit
Press Ctrl+C or type /exit to close the TUI.
7. Confirm It Worked
Once you’ve mapped devices and deployed with pab deploy, confirm blocking actually took effect by querying the live server directly:
pab verify --domain ads.example.com
See Mapping & Deploying below for the full map → dry-run → deploy → verify workflow.
Next Steps: Explore the Interactive Mode section to master slash commands, or jump to Mapping & Deploying to assign devices programmatically.
TUI Interface Preview
When you run pab, you’ll see a title bar, an empty content area, a search prompt, and a footer of keyboard hints — your command center for managing DNS blocking policies:
╭──────────────────────────────────────────────────────────────────╮
│ Pharos Advanced Blocking │
│ │
│ │
│ │
│ > Search or type /help for commands │
│ │
│ ctrl+c / esc: exit | /help: commands | /clear: reset │
╰──────────────────────────────────────────────────────────────────╯
There’s no welcome banner or first-run tips on the opening screen today — type /help to see the full command list. (A first-run welcome banner exists in the codebase but is currently disabled pending a rendering fix, so it isn’t part of the current release.)
Key Interface Elements
- Title Banner: “Pharos Advanced Blocking” with blue-tinted styling
- Content Area: Empty until you search or run a slash command like
/view groups - Search Prompt (
>): Type IPs or group names to filter your network - Footer Hints: Keyboard shortcuts for navigation and common commands
ctrl+c/esc: Exit the TUI/help: View all available commands/clear: Reset search filters
The interface is built with Bubbletea (Go TUI framework) and uses responsive sizing to fit your terminal. All interactions are keyboard-driven for speed and efficiency.
When to Use: Interactive Mode (TUI) vs. Command Line
Pharos Advanced Blocking offers two ways to manage your DNS configuration. Choose the right tool for your task:
Interactive Mode (TUI) — Use When…
- Exploring your configuration: Browse all clients, groups, and current assignments
- Debugging DNS issues: Search for a specific client IP, see what group it’s assigned to, check blocklists
- Making one-off changes: Quickly view a group’s settings or search results
- Learning the system: New to Pharos? The TUI guides you interactively with real-time search and
/helpcommands
Launch with: pab (no arguments)
Command Line (CLI) — Use When…
- Automating bulk operations: Map 100 client IPs at once with
pab mapcommands - Setting up CI/CD pipelines: Deploy changes automatically on schedule or triggered by events
- Integrating with other tools: Call
pab map,pab unmap, orpab deployfrom your scripts - Production deployments: Use
pab deploy --dry-runto preview changes before applying with-f - Non-interactive environments: Running in containers or cron jobs where a terminal isn’t available
Commands: pab init, pab map, pab unmap, pab deploy, pab verify, pab --help
Summary
| Task | Use TUI | Use CLI |
|---|---|---|
| Explore configuration | ✓ | |
| Debug a specific client | ✓ | |
| Assign one client | Either | |
| Bulk-assign 100+ clients | ✓ | |
| Deploy to production | ✓ (with –dry-run) | |
| Automate with CI/CD | ✓ |
First-time users: Start with the TUI (pab) to get familiar with your groups and clients. Once you understand your setup, you’re ready for CLI commands and automation.
Installation & Credentials
Install pab
Follow the Installation guide to install the CLI.
Set Up Credentials
pab does NOT require credentials to start up or use interactive mode (reads local dnsApp.config). Credentials are only needed when deploying to a Technitium server via the pab deploy command (or fetching one via pab init).
How you supply credentials depends on how many Technitium nodes you manage — that’s the whole decision:
| You manage… | Use | Works in |
|---|---|---|
| 1 node | Environment variables | Any environment, including CI/CD |
| 2+ nodes | secrets.json |
Any environment, including CI/CD |
The easiest way to set either one up is pab init — it prompts for credentials, asks whether you manage more than one node, and either prints the export lines for you to add to your shell profile (1 node) or writes secrets.json with chmod 600 for you (2+ nodes). The manual steps below are what pab init automates.
Here’s exactly what you’ll see when you run pab init with nothing set up yet. Answer “n” if you manage a single Technitium instance:
No Technitium credentials found via environment variables or secrets.json.
Technitium server URL (e.g. https://dns.example.com:5380): https://dns.example.com:5380
API token: your-api-token-here
Do you manage more than one Technitium node? [y/N]: n
Single-node setup. pab cannot persist environment variables for you;
add the following to your shell profile (e.g. ~/.bashrc, ~/.zshrc):
export TECHNITIUM_URL="https://dns.example.com:5380"
export TECHNITIUM_TOKEN="your-api-token-here"
Answer “y” if you manage two or more nodes — you’ll be asked to name and enter each one (the “Add another node?” prompt repeats until you decline), and pab init writes them straight to secrets.json at chmod 600:
No Technitium credentials found via environment variables or secrets.json.
Technitium server URL (e.g. https://dns.example.com:5380): https://dns1.example.com:5380
API token: token-for-dns1
Do you manage more than one Technitium node? [y/N]: y
Name for this node (e.g. dns1, prod): dns1
Add another node? [y/N]: y
Node URL: https://dns2.example.com:5380
API token: token-for-dns2
Node name: dns2
Add another node? [y/N]: n
Wrote 2 node(s) to /home/user/.config/pab/secrets.json (chmod 600).
Either way, the rest is automated — no manual file editing needed.
1 node: Environment Variables
export TECHNITIUM_URL="https://dns.example.com:5380"
export TECHNITIUM_TOKEN="your_api_token_here"
TECHNITIUM_URL/TECHNITIUM_TOKEN configure exactly one node (registered internally as "default"). There is no multi-node environment variable pattern — as soon as you’re managing a second node, move to secrets.json below.
pab deploy --dry-run # Validate first
pab deploy -f # Deploy without a confirmation prompt
2+ nodes: Secrets File
Create a secrets file at ~/.config/pab/secrets.json with mode chmod 600. Each node can optionally carry a name (falls back to node-0, node-1, … when omitted) so pab list-nodes and pab deploy --node <name> have something stable to target:
{
"nodes": [
{
"name": "dns1",
"url": "https://dns1.example.com:5380",
"token": "token_for_dns1"
},
{
"name": "dns2",
"url": "https://dns2.example.com:5380",
"token": "token_for_dns2"
}
]
}
Then deploy to all nodes with:
pab deploy --dry-run # Validate first
pab deploy -f # Deploy to all nodes simultaneously
Using secrets.json in CI/CD: since 2+ nodes always means secrets.json, write it from a CI secret to a temp path immediately before deploying, keeping it off disk the rest of the time:
export XDG_CONFIG_HOME=/tmp/pab-config
umask 077
mkdir -p "$XDG_CONFIG_HOME/pab"
printf '%s' "$SECRETS_JSON" > "$XDG_CONFIG_HOME/pab/secrets.json"
chmod 600 "$XDG_CONFIG_HOME/pab/secrets.json"
pab deploy -f
rm -rf "$XDG_CONFIG_HOME"
(Store the file’s JSON contents as a single CI secret, e.g. SECRETS_JSON — pab reads $XDG_CONFIG_HOME/pab/secrets.json on Linux, so pointing XDG_CONFIG_HOME at a scratch directory keeps the real credentials off disk outside the job.)
Security: Environment variables are simplest for a single node and never touch disk. secrets.json is required once you have two or more nodes, and pab refuses to run if its permissions are weaker than chmod 600.
Interactive Mode — Slash Commands
When you run pab (with no arguments), you enter interactive mode. The TUI accepts slash commands for powerful, terminal-native operations.
Command Reference
| Command | Aliases | Description | Example |
|---|---|---|---|
/help |
/?, /h |
Show all available slash commands | /help |
/exit |
/quit, /q |
Exit the TUI | /exit |
/clear |
/c |
Clear search input and reset all filters | /clear |
/view groups |
/v groups |
List all configured groups with device count per group | /view groups |
/view group <name> |
/v group <name> |
Show all details for a specific group (allowed domains, blocked domains, blocklists) | /view group Kids |
/view group <name> blocklists |
/v group <name> blocklists |
Show only blocked domains for a group | /view group Kids blocklists |
/view group <name> allowed |
/v group <name> allowed |
Show only allowed domains for a group | /view group Kids allowed |
/view networkGroupMap |
/v networkGroupMap |
Show all IP-to-group mappings (same as empty search) | /view networkGroupMap |
Usage Patterns
List all groups:
/view groups
Output:
Group Name Devices
Kids 3
IoT 5
Do-Not-Block 1
View Kids group details:
/view group Kids
See only blocklists for a group:
/view group IoT blocklists
View all IP → group assignments:
/view networkGroupMap
Search Typeahead (IP & Group Filtering)
While in search mode (before typing /), Pharos filters results in real-time as you type. Press Tab to navigate through filtered results.
What Works
- IP Filtering: Type
192.0and results filter to show only IPs containing “192.0” - Group Filtering: Type
kidand results filter to show only groups containing “kid” (case-insensitive) - Tab Navigation: Press Tab to cycle through matching IPs or groups
- Enter to Confirm: Press Enter to apply the selected filter
Example Workflow
Type: 192.0[Tab]
Result: Highlights first matching IP (e.g., 192.0.2.50)
Press Tab again: Cycles to next IP match (e.g., 192.0.2.51)
Press Enter: Filters table to show that IP's details
Group Filtering with Tab Navigation
Here’s what happens step-by-step when you search for a group:
You: Type 'kid' in search mode
Result: TUI filters to groups containing "kid":
- Kids
- Device-Kids
- Streaming-Kids
(First match "Kids" is automatically highlighted)
You: Press Tab
Result: Highlights cycle to the next match
Kids → Device-Kids (now highlighted)
You: Press Tab again
Result: Highlights cycle to the next match
Device-Kids → Streaming-Kids (now highlighted)
You: Press Tab again
Result: Wraps around to beginning
Streaming-Kids → Kids (now highlighted)
You: Press Enter on "Device-Kids"
Result: Shows details for the Device-Kids group
Key Point: This is navigation through filtered results, not autocomplete. Typing kid[Tab] does NOT auto-fill to “Kids”—it filters the list and lets you navigate through matches with Tab, then press Enter to confirm your choice.
What’s NOT Available (Yet)
- Full IP autocomplete: Typing
192.0[Tab]will NOT auto-complete to a full IP like192.0.2.50—it filters and navigates through matches - Full group name autocomplete: Typing
kid[Tab]will NOT auto-complete to “Kids”—it filters and navigates through matches - Auto-completion of group names is planned for a future release
Power-User Tips
- Case-insensitive: Type
kidsorKIDSto find the “Kids” group - Partial matching: Type
192to filter to all IPs containing 192 - Multiple matches: If multiple items match, use Tab to cycle through them, then Enter to confirm
How This Differs from Tab Completion
→ See Tab Completion below for IDE-style command name autocomplete (different from search navigation).
Tab Completion (Command Names Only)
→ See Search Typeahead above for search mode Tab navigation (different from command autocomplete).
Press Tab to auto-complete slash command names. This is true IDE-style completion—the command name fills in completely with a trailing space, ready for arguments.
What Works
Single Command Match:
Type a partial command that matches exactly one slash command, then press Tab. The command completes fully with a space:
/e[Tab] → /exit
/q[Tab] → /exit
/h[Tab] → /help
Multiple Command Matches:
Type a partial command that matches multiple commands, then press Tab. The TUI highlights the first match:
/v[Tab] → Highlights "/view" (multiple matches)
Press ↓ to select a match → Navigate to desired command
Press Tab → Completes highlighted command to "/view "
Entering Arguments After Completion:
After Tab-completing a command, start typing arguments immediately without pressing Tab again:
/v[Tab]
groups
[Enter] → Executes "/view groups"
What’s NOT Available (Yet)
- Group name Tab completion in search: Typing
kid[Tab]will NOT auto-complete to “Kids”—it filters and navigates search results instead - Subcommand Tab completion: Typing
/view gr[Tab]will NOT complete to “/view groups”—you must type “groups” manually - These features are planned for a future release
How It’s Different From Search
- Command Tab completion: TRUE autocomplete.
/v[Tab]→/view(fills in the space automatically) - Search Tab: Navigate through filtered results.
kid[Tab]→ Cycles through matching groups but does NOT complete the name
Power-User Tips
- Aliases work too:
/vand/viewboth match; Tab-complete either one - Case-insensitive:
/EXITand/exitare equivalent - After completion, you’re in argument mode: The space is already there—just start typing your arguments
Advanced Workflows
Command History Navigation
After executing commands, use ↑↓ arrow keys to scroll through command history. This is perfect for repeating common operations without retyping:
Execute: /view groups
Press ↑: Returns to /view groups in input
Press ↑: Returns to previous command
Useful for: Rapid multi-group inspection, repeated deployments, testing workflows.
Case-Insensitive Group Search
Search is case-insensitive, so any of these find the “Kids” group:
Type: kids[Enter]
Type: KIDS[Enter]
Type: KiDs[Enter]
All three filter identically to the Kids group.
Multi-Node Deployment Pattern
For active-active setups, deploy to all nodes with a single command:
pab deploy --dry-run # Preview all changes
pab deploy -f # Deploy to all configured nodes simultaneously
Or target a specific node:
pab deploy -f --node secondary
This pattern scales from 1 to N nodes without workflow changes.
Mapping & Deploying
Once your configuration is ready, manage updates using the CLI commands:
Workflow Overview
- Map Policies (
pab map): Assign client IPs to blocking groups - Preview Changes (
pab deploy --dry-run): Validate and inspect what will change - Deploy (
pab deploy): Sync to your Technitium instances - Verify (
pab verify): Confirm blocking actually took effect on the live server
Step 1: Map a Client IP
Add or update a device mapping:
pab map --ip 192.0.2.50 --group Kids
Validate succeeds instantly (local schema validation). If validation fails, the mapping is rejected and the config remains unchanged.
Step 2: Preview with Dry-Run
Before deploying, see what will change:
pab deploy --dry-run
Output shows:
- Structural diff between local and remote configuration
- Any validation warnings
- Summary of changes (new mappings, removed mappings, group updates)
Nothing is written to the server during dry-run.
Step 3: Deploy to Technitium
Once you’re confident, deploy:
pab deploy
By default, you’ll be prompted to confirm before deploying to all nodes. Use -f/--force to skip the confirmation prompt:
pab deploy -f
Or target a specific node:
pab deploy -f --node production
Step 4: Verify It Worked
Deploying successfully means the config was written to the API — it doesn’t guarantee Technitium is actually blocking the domain yet. Close the loop with a live DNS query:
pab verify --domain ads.example.com
pab verify queries the target node’s DNS resolver directly and checks the response against that domain’s group’s configured blocking behavior. It reports blocked, not-blocked, inconclusive (e.g. a sinkhole-style group with no blockingAddresses configured, so pab has nothing to compare the response against), or error, and exits non-zero unless the result is exactly blocked — safe to chain directly after deploy in a script or CI job:
pab deploy -f && pab verify --domain ads.example.com
If the domain is only covered by a remote block-list URL (not a literal entry in blockedDomains/blockedRegex), pass --group explicitly — pab verify doesn’t fetch and search remote lists to attribute a domain automatically:
pab verify --domain doubleclick.net --group Default
When you manage more than one node, pass --node to pick which one to query — pab verify checks exactly one node per run, unlike pab deploy’s fan-out to all configured nodes:
pab verify --domain ads.example.com --node production
Multi-Device Example
Map several devices, then deploy and verify once:
pab map --ip 192.0.2.50 --group Kids
pab map --ip 192.0.2.51 --group Kids
pab map --ip 203.0.113.10 --group IoT
pab deploy --dry-run
pab deploy -f
pab verify --domain ads.example.com
Safety Features
- Schema validation: Every
pab mapcall validates your IP and group name before writing. - Dry-run inspection:
pab deploy --dry-runshows changes without applying them. - Confirmation prompts: Deployment prompts once before sending to all nodes (use
-fto skip if you’ve already verified with--dry-run). - Live verification:
pab verify --domain <domain>confirms blocking actually took effect on the server — not just that the API accepted the config. - Rollback safety: One
pab deploy -fcan be undone by reverting your config file and re-deploying.
Configuration File Format
The fastest way to get a valid dnsApp.config is pab init (see Quickstart above) — it fetches your server’s live configuration, or falls back to a starter template if the server has none yet, and writes it to disk only after you confirm.
If you’d rather build or edit dnsApp.config by hand, here’s the schema pab understands (matching Technitium’s own Advanced Blocking App schema):
{
"enableBlocking": true,
"blockingAnswerTtl": 60,
"blockListUrlUpdateIntervalHours": 24,
"blockListUrlUpdateIntervalMinutes": 0,
"networkGroupMap": {
"192.0.2.50": "Kids",
"203.0.113.10": "IoT"
},
"groups": [
{
"name": "Kids",
"enableBlocking": true,
"allowTxtBlockingReport": false,
"blockAsNxDomain": true,
"blockingAddresses": [],
"allowedDomains": ["youtube.com"],
"blockedDomains": ["ads.example.com"],
"allowListUrls": [],
"blockLists": ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"],
"allowedRegex": [],
"blockedRegex": [],
"regexAllowListUrls": [],
"regexBlockListUrls": [],
"adblockListUrls": []
},
{
"name": "IoT",
"enableBlocking": true,
"allowTxtBlockingReport": false,
"blockAsNxDomain": false,
"blockingAddresses": ["0.0.0.0"],
"allowedDomains": [],
"blockedDomains": [],
"allowListUrls": [],
"blockLists": [],
"allowedRegex": [],
"blockedRegex": [],
"regexAllowListUrls": [],
"regexBlockListUrls": [],
"adblockListUrls": []
}
]
}
enableBlocking/blockingAnswerTtl/blockListUrlUpdateInterval{Hours,Minutes}: the global blocking toggle, DNS answer TTL for blocked responses, and how often Technitium refreshes remote block lists.networkGroupMap: maps client IPs/CIDR ranges to groups — this is whatpab mapandpab unmapedit.groups: each group’s rule set:blockedDomains/allowedDomains: literal domain entriesblockedRegex/allowedRegex: pattern-matched entriesblockLists/allowListUrls/adblockListUrls/regexBlockListUrls/regexAllowListUrls: remote list URLsblockAsNxDomain/blockingAddresses: how a blocked query is actually answered — NXDOMAIN, or a sinkhole address. This is exactly whatpab verifycompares a live DNS response against.
A full working example lives at dnsApp.config.example in the repository root. You can edit the file manually, fetch it with pab init, or edit specific fields with pab map/pab unmap — all three paths respect the same schema validation.
Troubleshooting
“Configuration file not found”
Ensure dnsApp.config exists in your current directory, or specify --config:
pab --config /etc/technitium/dnsApp.config map --ip 192.0.2.50 --group Kids
No config yet? Run pab init to fetch one from your Technitium server (or generate a starter template) instead of creating one by hand.
“Failed to deploy to node”
Check:
- Node URL and token are correct (via environment variables or
~/.config/pab/secrets.json) - Technitium API is reachable (test with
curl -k https://your-node:5380/api/) - API token has permission to write configuration
“IP validation failed”
Ensure your IP is:
- Valid IPv4 (e.g.,
192.0.2.50) - Valid IPv6 (e.g.,
::1) - Valid CIDR range (e.g.,
203.0.113.0/28)
“Group not found”
The group name must exist in your dnsApp.config. Use /view groups in interactive mode to see all available groups.
pab verify reports “not-blocked” or “inconclusive”
- “not-blocked”: the live DNS response didn’t match the group’s configured blocking behavior. Confirm you’ve deployed the latest config (
pab deploy -f) and that the domain is actually listed in the group you expect (pass--groupto check a specific one). - “inconclusive”: the group has
blockAsNxDomain: falsebut noblockingAddressesconfigured, so pab has nothing to compare the live response against. Add ablockingAddressesentry to the group, or verify against a group that has one configured.
See Also
- CLI Reference: Full command documentation with examples
- Installation: Install and setup