kwatch
An eBPF process monitor. Built to learn eBPF, not for production.
- built
- March 2026
- stack
- C and eBPF in the kernel, Go in userspace, Rust terminal UI, React web view
- runs on
- Linux 5.8+ with BTF
- code
- github.com/ismajl-ramadani/kwatch-ebpf
Why
Security tools like Falco and Tetragon, and the EDR agents I worked next to at Cybee, all see what a machine is doing through eBPF: small programs that run inside the Linux kernel and report events as they happen. I wanted to understand that layer properly, so I wrote a small one myself, from the kernel hooks up to a live dashboard.
How it works
What it traces
- Every program that starts, with its arguments, parent process and container group (cgroup), read inside the kernel so nothing is missed between events.
- Every program that exits, with how long it ran.
- Network connections, outgoing and incoming (IPv4), with addresses and ports.
- Sensitive file access: password and sudo files, SSH and GPG keys, cron jobs, container secrets.
Each event is enriched in userspace from /proc (executable path, working folder, memory, open files) the moment it arrives, before the process can disappear.
Things I learned the hard way
- Read it in the kernel, not after. My first version looked up the parent process in
/procafterwards. Short-lived processes were often gone by then, so I moved the parent and cgroup lookup into the eBPF program itself. - Incoming connections need two hooks. The client's address only exists after
acceptreturns, so the entry hook stores a pointer in a kernel map and the exit hook reads the address once the kernel has filled it in. - State is a trap. I first kept a full process tree updated from events, and it drifted. I replaced it with a stateless snapshot: read
/proconce, then apply event diffs. New clients get the snapshot first, then the live stream. - Metrics need restraint. Prometheus counters are labelled only by event type and command, never by PID, path or address, so the number of series stays small no matter how busy the machine is.
Outputs and tools around it
- Events always stream to stdout as JSON. Prometheus, WebSocket and MQTT are switched on in a config file, and an error in one output is logged without stopping the rest.
- A terminal dashboard in Rust (ratatui) shows the live event feed and process tree over the WebSocket, and a small React page does the same in the browser.
- A "chaos" script triggers every kind of event on a test VM, so you can watch the dashboard light up, and integration tests check the tracer against real commands.