A fast, keyboard-driven terminal UI for Pi-hole v6, built in Go on the Charm v2 stack (Bubble Tea, Bubbles, Lip Gloss). It aims for feature parity with the Pi-hole web admin β query log, allow/deny lists, groups, clients, adlists, local DNS, live config, and system tools β without leaving your terminal.
- Dashboard β blocking status, query/percentage tiles, top clients & domains at a glance.
- Query Log β cursor-paginated, filterable live log of DNS queries.
- Domains β manage allow/deny lists across exact and regex kinds with group assignment.
- Groups & Clients β full CRUD, with client suggestions pulled from the network table.
- Adlists β block/allow lists plus a streamed gravity update log.
- Local DNS β A/AAAA host records and CNAME records.
- Settings β browse and edit the entire FTL config tree, manage connections (add/edit/remove instances), and switch themes live.
- System / Tools β FTL/host/system info, diagnosis messages, the network device table, a live DNS-log tail, and guarded destructive actions (restart DNS, flush logs/network).
- Multi-instance β configure several Pi-holes and switch between them
instantly (
sor the command palette). - Command palette (
ctrl+k) β fuzzy-jump to any screen, toggle blocking, change themes, or switch instances. - Help overlay (
?) β context-aware cheat-sheet of global and per-screen keys. - Themes β the signature Gloss default (multi-stop gradient treatment),
plus
deep-night,light-luxury,pihole-classic, and automatic adoption of your Omarchy theme when present.
Requires Go 1.25+.
go install github.com/z19r/tihole/cmd/tihole@latestOr build from a checkout:
go build -o tihole ./cmd/tiholetihole # launch the dashboard
tihole config # jump straight to the config editor to add or fix an instance
tihole help # usageOn first run, tihole walks you through a short setup wizard and writes a config file. After that it connects to the active instance and opens the dashboard.
A wrong address or password never aborts startup: authentication happens lazily,
so connection failures show up as in-app error banners, and a structurally
broken instance drops you straight into the config editor to fix it. You can
also open the editor any time with tihole config or from the command palette
(ctrl+k β Settings).
| Key | Action |
|---|---|
1β9 |
Jump to a screen by number |
ββ / j k |
Move the selection (screens on the rail, rows in a panel) |
enter / β / tab |
Descend from the sidebar into the active screen |
esc |
Climb back to the sidebar |
ctrl+k |
Command palette |
s |
Switch to the next instance |
d |
Toggle blocking |
ctrl+t |
Cycle theme |
? |
Help overlay |
q / ctrl+c |
Quit |
Per-screen actions (add a, edit e, delete x, refresh r, β¦) are shown in
the help bar and the ? overlay. See docs/keys.md for the
complete reference and an explanation of the two-zone focus model.
Config lives at ~/.config/tihole/config.yaml (mode 0600). It is written and
edited by the app, but can also be hand-authored:
active: home
theme: deep-night
instances:
- name: home
url: https://pi.hole
password_env: TIHOLE_HOME_PASSWORD # preferred: read the app password from env
verify_tls: true
- name: cabin
url: http://10.0.0.53
password: plaintext-ok-but-env-is-better
verify_tls: false # self-signed / no TLSEach instance needs a name, a url, and an app password supplied either
inline (password) or, preferably, via password_env naming an environment
variable. Set verify_tls: false for self-signed certificates.
- The config file is created
0600in a0700directory. - Passwords and session IDs are never logged.
- Authentication uses Pi-hole v6's
X-FTL-SIDsession header, re-authenticating transparently on expiry, and logs out on exit so it doesn't leak a session seat. - Some Pi-hole actions require
webserver.api.allow_destructiveto be enabled on the server; tihole surfaces a clear hint when the API rejects them.
go build ./... # compile
go vet ./... # static checks
gofmt -l . # formatting (should print nothing)
go test ./... # tests
go test -cover ./internal/... # with coverageThe codebase separates concerns strictly: the domain packages (internal/pihole,
internal/config, internal/theme) never import the TUI, and screens receive
their dependencies through a shared core.AppContext.
docs/architecture.mdβ package layout, dependency injection, theScreencontract, and message flow.docs/keys.mdβ complete key reference and the focus model.docs/troubleshooting.mdβ config, TLS, auth, and destructive-action issues.docs/CHARM_V2_API.mdβ Charm v2 specifics.docs/superpowers/specs/2026-07-23-tihole-design.mdβ the original design spec.
Released under the MIT License.