A terminal UI for monitoring WireGuard peers. It joins human-readable peer
names from your config file with live runtime stats from wg show, and presents
them in a clean, auto-refreshing table.
WireGuard · wg0 4 peers · updated 0s ago
╭───────────────┬───────────┬────────────────────┬─────────────────────────┬───────────┬───────────────────────╮
│ Name │ Status │ Endpoint │ Allowed IPs │ Handshake │ Transfer (↓rx / ↑tx) │
├───────────────┼───────────┼────────────────────┼─────────────────────────┼───────────┼───────────────────────┤
│ bob.van │ ● online │ 203.0.113.10:51820 │ 10.0.0.2/32 │ 12s ago │ ↓1.2 MiB ↑514.0 KiB │
│ alice.johnson │ ● online │ 198.51.100.7:48123 │ 10.0.0.3/32, 10.0.0.4/32│ 1m ago │ ↓942.0 MiB ↑117.8 MiB │
│ carol.smith │ ● offline │ 192.0.2.55:51820 │ 10.0.0.5/32 │ 14m ago │ ↓40.1 MiB ↑8.0 MiB │
│ dave.lee │ ● never │ — │ 10.0.0.6/32 │ never │ ↓0 B ↑0 B │
╰───────────────┴───────────┴────────────────────┴─────────────────────────┴───────────┴───────────────────────╯
r refresh · q quit
The ● online / ● offline / ● never markers are colored green / amber / grey.
wg show is raw and, crucially, does not show human-readable peer names — those
live only as comments in the config file:
[Peer]
# bob.van
PublicKey = aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4=
AllowedIPs = 10.0.0.2/32wgtui reads that comment, matches it to the peer by public key, and shows the
name alongside live status, endpoint, handshake age, and transfer counters.
go install wgtui/cmd/wgtui@latest # or: make installRequires Go 1.26+.
wgtui -i wg0 # monitor interface wg0 (default)
wgtui --demo # synthetic data — no WireGuard neededReading WireGuard state and the config file usually requires elevated privileges:
sudo wgtui -i wg0| Flag | Default | Description |
|---|---|---|
-i |
wg0 |
WireGuard interface to monitor |
-config |
/etc/wireguard/<iface>.conf |
Path to the interface config (for names) |
--demo |
false |
Use synthetic data; runs without WireGuard |
| Key | Action |
|---|---|
r |
Refresh now |
q / Ctrl+C |
Quit |
The table also auto-refreshes every 2 seconds.
- Name — from the
# namecomment in the config (falls back to a short public-key prefix if unnamed). - Status —
● online(green) if a handshake occurred within the last 3 minutes,● offline(amber) otherwise,● never(grey) if the peer has never connected. - Endpoint — the peer's remote
ip:port. - Allowed IPs — the peer's configured allowed IP ranges (comma-separated).
- Handshake — relative age of the latest handshake.
- Transfer — bytes received (↓) and sent (↑), human-readable.
Data access sits behind a small Provider interface, so the UI is decoupled
from the source:
CommandProvidershells out towg show <iface> dumpand reads the config file for names — the production path.FakeProviderreturns synthetic peers — powers--demoand the tests.
The parsers are pure functions, so the entire test suite runs without WireGuard installed.
cmd/wgtui/ entry point + flags
internal/wg/ peer model, config & dump parsers, providers
internal/ui/ Bubble Tea model, columns, Lipgloss theme
testdata/ sample config + dump fixtures
Built with the Charm stack: Bubble Tea (event loop) and Lipgloss + lipgloss/table (rendering).
make # build into ./bin
make demo # run the TUI with synthetic data
make test # run all tests
make snapshot # print a rendered demo frame
make check # vet + test
make cross # cross-compile for all platforms into ./dist
make help # list all targetsCross-compilation targets linux/{amd64,arm64} and darwin/{amd64,arm64} by
default (override with make cross PLATFORMS="linux/amd64 windows/amd64").
Builds are static (CGO_ENABLED=0) and reproducible (-trimpath). Since
wg/wg-quick are Linux-native, the Linux builds are the primary deployment
target; the rest are handy for --demo and local development.