Technical Briefing – pktz Prepared for a technical client; consultant‑grade, evidence‑first.
The Problem
Your machine is continuously exchanging data with countless endpoints. Traditional tools either sample packets, require clang/ bpftool to inspect eBPF programs, or show only high‑level statistics. There is no single binary that gives per‑process, per‑connection traffic totals with live rates, DNS history, GeoIP, and container aggregation—all without packet sampling.
What This Does
pktz is a TUI network‑traffic monitor built on eBPF. Core responsibilities are split across a few small, focused packages:
| Responsibility | Key files |
|---|---|
| eBPF program & byte‑code | bpf/pktz.c (C source), pre‑compiled objects internal/collector/pktz_bpfeb.o and pktz_bpfel.o bundled in the Go module |
| Packet capture & socket mapping | internal/collector/collector.go, internal/collector/pktz_bpfeb.go / pktz_bpfel.go (platform‑specific eBPF loader), internal/collector/procnet.go (reads /proc/<pid>/fd/), internal/collector/iface.go |
| Process & connection enumeration | internal/collector/container.go, internal/collector/history.go, internal/collector/dns.go |
| TUI rendering & navigation | internal/tui/tui.go, internal/tui/graph.go, internal/tui/styles.go, internal/tui/tcpdump.go |
| Resolver & GeoIP | internal/resolver/resolver.go, internal/geoip/geoip.go, internal/geoip/download.go |
| Entry point & CLI | main.go (wires flags --pid, --app, etc.) |
| Build & CI | Makefile, go.mod, go.sum, .github/workflows/ci.yml, .github/workflows/release.yml |
The flow is: main.go parses CLI flags → loads the appropriate eBPF object (pktz_bpfeb.o/pktz_bpfel.o) via the collector → the BPF program attaches to the kernel’s sk_receive_queue and sock_ops hooks, capturing every byte. The collector then walks /proc/<pid>/fd to map sockets back to processes, aggregates RX/TX rates, and pushes the data into the TUI layer for live display, connection drill‑down, graphing, and GeoIP/ DNS enrichment.
No packet sampling is used; every byte is accounted for, and the eBPF objects are pre‑compiled and bundled, so a clang/libbpf-dev installation is only needed when regenerating the objects after changing bpf/pktz.c.
How It Is Wired
- Start –
main.gorunspktz(orpktz --pid N/pktz --app name). - Load eBPF – The collector (
collector.go) reads the bundled.ofiles and callsbpftool/libbpfto load them; the C program (bpf/pktz.c) defines the tracepoints. - Capture – The BPF program records per‑socket byte counters and socket states.
- Map to processes –
procnet.goenumerates/proc/<pid>/fd;container.gogroups sockets by container when Docker/container runtime is detected. - Enrichment –
dns.goresolves hostnames;geoip.goadds country flags and ASN names (downloaded viainternal/geoip/download.go). - Render –
tui.gopresents a sorted process list, a 5‑minute Unicode block graph (graph.go), and drill‑down panes for connections, DNS history, and tcpdump output. - Exit –
qquits;Esc/Backspacereturns to the process list.
The module graph is shallow: a change to bpf/pktz.c forces a rebuild of the two .o files, after which the Go code continues unchanged. All other directories depend only on the collector’s API, making the system easy to extend without ripple effects.
How To Use It
Install (choose one):
# Pre‑built binary (amd64)
curl -Lo pktz https://github.com/immanuwell/pktz/releases/latest/download/pktz-linux-amd64
chmod +x pktz && sudo mv pktz /usr/local/bin/
# Or with Go (fetches + compiles)
go install github.com/immanuwell/pktz@latest
Build from source (editing code):
make check # test, vet, build
make install # copy to /usr/local/bin
Clang/libbpf/bpftool are only required to regenerate the eBPF objects after modifying bpf/pktz.c.
Run:
sudo pktz # needs root for eBPF load + /proc read
# Or grant capabilities once:
sudo setcap cap_bpf,cap_perfmon,cap_dac_read_search+ep $(which pktz)
pktz # sudo‑free (limited visibility)
Flags (from main.go):
sudo pktz --pid 1234 # jump straight to PID detail view
sudo pktz --app fire # filter list to processes matching "fire"
GeoIP (optional):
sudo pktz --download-geoip-db # downloads from DB-IP.com, no key needed
# then press `g` in the TUI to toggle flags/ASN
Keybindings (summary):
↑/↓orj/k– navigateEnter– open connection detailEsc/Backspace– go backh/l– cycle detail panesSpace– expand/collapse process groups– cycle sort column/– filter processes by namec– toggle process / container viewr– toggle hostname resolutionv– compact IPv6g– toggle GeoIP flagsm– toggle mouseq– quit
Real‑World Use
Scenario: A developer notices unexplained outbound traffic from a laptop during a video call. Running sudo pktz shows a process named zoomus with 2 MiB/s TX to 52.30.200.112. Pressing Enter reveals the connection is ESTABLISHED to a Cloudflare ASN (🇺🇸 CLOUDFLARE). Toggling r resolves the remote host to a.prod.zoom.us. The 5‑minute graph confirms the spike coincides with the call start. The user can then decide whether to allow the connection or terminate the process.
Code Health & Issues
- Tests: 2 test files exist (
internal/tui/graph_test.go,internal/tui/sort_test.go); they cover graph rendering and sort logic. - CI: GitHub Actions workflows (
ci.yml,release.yml) run on push and tags; no failures reported. - License:
LICENSEpresent (MIT‑style). - Lockfile:
go.sumchecked in, ensuring reproducible builds. - Structural red flags: None detected. The module graph is flat; the only “hub” is the collector API, which is deliberately minimal.
Observation: The repo does not include integration tests that exercise the full TUI or eBPF loading on diverse kernels, and the GeoIP database download is a manual one‑off step. These are the only gaps visible from the file structure.
The Bottom Line
pktz delivers a rare combination: zero‑sampling, per‑process eBPF traffic accounting, live TUI visualisation, and optional GeoIP/DNS enrichment—all packaged in a single binary with minimal build dependencies. It is well‑suited for engineers who need real‑time, per‑process network accounting without the overhead of packet capture tools. The codebase is clean, the responsibilities are well‑scoped, and the only missing piece is broader automated testing across kernel versions. Use it when you need granular, live insight into who is talking where on a Linux host.