Primary question: Does the causal-chain view from systemd init down to the target replace your current stack of lsof, pstree, and docker inspect lookups?
RepoDaily adoption score
RepoDaily rates this as 90/100 (strong) for adoption: evidence, installation path, production risk, differentiation, license clarity, and AI/agent fit are scored from the article sources and adoption notes.
4 source(s) across 3 source category/categories, plus a RepoDaily-specific evidence module when available.
5 workflow step(s), 6 next-action step(s), and 4 command/install signal(s) were detected.
Trending momentum is +342 stars, with maintenance/release/issue signals counted when present.
Risk is marked low, with 4 security note(s) and 3 explicit skip condition(s).
3 opportunity lens item(s), 5 alternative(s), and 4 type-specific section(s) support differentiation.
License source or license wording is present.
2 AI/agent-related signal(s) were detected in the article text and metadata.
Project overview
witr is a single Go binary that answers a deceptively hard question: why is this thing running? Given a PID, a port, a file lock, or a container identifier, it walks kernel and cgroup metadata upward through systemd units and parent processes until it can print a human-readable causal chain — typically ending at PID 1 or a known service manager. The output is designed to be copy-pasteable for incident chats and postmortems.
The tool ships in two surfaces: a cobra-based CLI for scripting and piping, and a bubbletea TUI that opens when you run witr with no arguments. The TUI exposes Processes, Ports, Containers, and Locks panes with an ancestry side-panel, matching the shape operators expect from a process explorer but grounded in witr's chain logic rather than flat tables.
A distinguishing artifact is the playground folder. The docs/ directory doubles as a GitHub Pages site that runs a simulated Linux box entirely in the browser. Visitors type real witr commands against authored fixture data; nothing touches their machine. This functions as both a guided tutorial (two incidents: webbox and devbox) and a free-form sandbox.
The playground's fidelity is enforced in CI. js/engine.js is a faithful port of witr's Go output layer — internal/output/*.go and internal/app/app.go — and scripts/check-fixtures.mjs replays the JavaScript engine over the same world with a pinned clock, asserting byte-for-byte equality against golden fixtures rendered by witr's actual Go output package. If the browser engine drifts from the real CLI, the build fails.
Why it is trending now
- 342 period stars and rank 9 on the 2026-08-10 Go trending list for a diagnostic CLI signals real appetite for process-lineage tooling that goes beyond flat pstree output.
- The zero-install browser playground removes the usual friction of trying a system-level CLI — an operator can investigate a simulated EADDRINUSE on :8000 before installing anything.
- The fidelity guarantee is concrete: js/engine.js ports internal/output/*.go and internal/app/app.go, and CI asserts byte-for-byte equality between the JavaScript engine and golden fixtures generated by witr's real Go output package.
- The dependency stack is familiar and trustworthy: spf13/cobra v1.10.2 for CLI parsing, charmbracelet/bubbletea v1.3.10 with bubbles v1.0.0 and lipgloss v1.1.0 for the TUI, and coreos/go-systemd/v22 v22.7.0 with godbus/dbus/v5 v5.1.0 for cgroup and service-manager introspection.
Problem it solves
- A deploy fails with EADDRINUSE and you cannot tell which teammate's http.server is squatting :8000 — the webbox scenario in the playground models exactly this.
- dpkg or unattended-upgrade holds a lock and every apt invocation blocks — witr traces the lock file back to the owning process and its systemd unit.
- A git index.lock blocks every commit and you need to know whether it is stale before removing it — the devbox scenario walks through this with a fix-by-kill flow.
- A python3 zombie is orphaned and you must find the parent that can reap it — witr surfaces the ancestry rather than just listing the zombie in a flat table.
- A stray ffmpeg pins the CPU and you need the full chain to justify killing it in production.
How it works
- Point witr at a target using one of its primary flags: --pid for a process, --port for a listening socket, --file for a file or lock, or --container for a container identifier.
- witr collects process metadata from /proc, resolves socket ownership for ports, and reads cgroup and systemd unit data through coreos/go-systemd and godbus/dbus.
- It walks parent PIDs upward, annotating each hop with its systemd unit, user, and container where applicable, until it reaches PID 1 or a service manager boundary.
- The CLI renders the chain as ANSI text by default; --json emits machine-readable output, --tree shows a nested view, --env includes environment variables, and --verbose adds extra detail per hop.
- With no arguments, the bubbletea TUI opens a dashboard with Processes / Ports / Containers / Locks panes and an ancestry side-panel that updates as you navigate.
Command Surface
- Entry point: ./cmd/witr — build produces a single binary named witr in the repo root.
- Core tracing flags exercised in the playground tutorial: --port, --file, --pid, --verbose.
- Optional side-quest flags that tick off in the incident tracker: --json, --tree, --env, --container.
- Running witr with no arguments opens the interactive TUI: Processes / Ports / Containers / Locks panels with an ancestry side-panel, built on bubbletea v1.3.10.
- Build requires Go 1.25+ (toolchain go1.25.10 per go.mod); CONTRIBUTING.md specifies `go build -o witr ./cmd/witr` followed by `./witr --help` as a smoke test.
- The -ldflags block injects commit and date metadata so `witr --version` reports accurate build information.
Zero-Install Trial Path
The docs/ folder is deployed directly by GitHub Pages at the owner's witr Pages URL. It hosts an interactive terminal-first playground that simulates a Linux box with authored process, port, container, and lock data.
Two guided incidents are available. On webbox, tasks are informational: a deploy dies with EADDRINUSE on :8000 (the squatter is a teammate's http.server), a dpkg lock is held by a scheduled unattended-upgrade, and a Node app's resource weight needs assessment. On devbox, tasks are fix-by-kill: a git index.lock blocks commits, a python3 zombie needs reaping through its parent, and a stray ffmpeg pins the CPU — kill actually removes the process and its subtree, and the engine, constellation, TUI, and incident tracker all reflect it live.
A three.js process constellation visualizes the machine. When a query resolves, the causal chain (systemd → … → target) lights up while everything else dims. Nodes and the legend (pid 1 / listener / process / warning) are clickable.
Reset restores the pristine box. The playground can also be run locally with `cd docs && python3 -m http.server 8099` since ES modules require http:// rather than file://.
Architecture Read
- Module path: github.com/pranshuparmar/witr, declared in go.mod with go 1.25 and toolchain go1.25.10.
- TUI stack: charmbracelet/bubbles v1.0.0 for components, bubbletea v1.3.10 for the Elm-architecture runtime, lipgloss v1.1.0 for styling.
- CLI framework: spf13/cobra v1.10.2 with spf13/pflag v1.0.10 for flag parsing.
- System introspection: coreos/go-systemd/v22 v22.7.0 for unit and cgroup data, godbus/dbus/v5 v5.1.0 for D-Bus communication with service managers.
- Terminal support: mattn/go-isatty v0.0.20 for TTY detection, muesli/reflow for text wrapping, golang.org/x/sys v0.38.0 for low-level syscalls.
- Output layer lives in internal/output/*.go and app routing in internal/app/app.go — the same packages the playground's js/engine.js ports to JavaScript.
- Golden fixtures are generated by a small Go program in fixtures/gen/ that renders through witr's actual output package, ensuring the test fixtures match real CLI behavior.
Maintenance Risk Read
The project is early but shows deliberate engineering discipline. The CI fidelity check — scripts/check-fixtures.mjs asserting byte-for-byte equality between the JavaScript playground engine and Go-generated golden fixtures — means any change to witr's output format that is not reflected in the playground will fail the build. This protects against the most common drift problem in projects that ship both a CLI and a web demo.
All dependencies are pinned to specific versions in go.mod rather than floating tags or replace directives. The Charm stack (bubbletea, bubbles, lipgloss) and the systemd/dbus libraries are actively maintained upstream, reducing the risk of supply-chain stagnation.
CONTRIBUTING.md documents a clear build path (Go 1.25+, `go build -o witr ./cmd/witr`) and points contributors to GitHub Issues for both questions and enhancement suggestions, indicating a standard open-source contribution model rather than a closed inner-source process.
Who should pay attention?
Good fit if
- Linux operators who live in tmux and paste CLI output into incident channels
- SREs who need a fast causal chain for on-call pages about port conflicts or stuck package-manager locks
- Developers debugging local docker-compose environments where multiple containers hold overlapping ports
- Instructors teaching Linux process concepts — the browser playground is a zero-risk sandbox with guided incidents
Skip for now if
- Windows-only or macOS-first users: witr's value depends on /proc, cgroups, and systemd; the TUI may launch but lineage data will be incomplete
- Organizations that have standardized on a commercial APM platform with its own built-in process tracing and correlation
- Users who need remote, multi-host correlation — witr is single-box by design and does not aggregate across machines
Risks and cautions
Apache-2.0 Go binary with an optional static-file browser playground; no background services, no network calls, no persistent state.
- License is permissive Apache 2.0, confirmed in the repo root LICENSE file.
- Build is a standard `go build -o witr ./cmd/witr` against Go 1.25+ with no CGO or external runtime dependencies beyond the Go toolchain.
- The playground is static files served by GitHub Pages; docs/README.md explicitly states it simulates witr against authored fixtures and nothing touches the visitor's machine.
- The tool is read-only by nature — it traces and displays metadata; it does not kill, modify, or persist unless the operator acts on the information (and even the playground's kill commands operate on simulated data only).
- The browser playground runs entirely client-side against authored fixture data; docs/README.md states it simulates witr, not a real shell, and never touches the visitor's machine.
- The CLI reads /proc, cgroup, and socket metadata; expect it to need the same privileges as ps or lsof to see processes owned by other users.
- go.mod pins specific dependency versions: bubbletea v1.3.10, cobra v1.10.2, go-systemd/v22 v22.7.0, dbus/v5 v5.1.0 — no floating tags or replace directives.
- No telemetry endpoints or phone-home behavior are referenced in the source pack; analytics.js in the playground is described as an optional GoatCounter wrapper that no-ops when blocked.
Alternatives to compare
| Approach | When to use | Trade-off |
|---|---|---|
htop | You need a real-time, interactive flat process viewer with sorting and filtering but not causal lineage from a port or lock back to systemd init. | Free, GPL, pre-installed on many distros |
btop | You want a visually rich system monitor with CPU, memory, disk, and network graphs but the same flat-process limitation as htop. | Free, Apache-2.0 |
psmisc (pstree, fuser, killall) | You need the closest classic Unix equivalent — pstree shows parent-child trees and fuser finds processes using a file or port, but neither reconstructs the full systemd-unit-annotated chain. | Free, GPL, part of most Linux base installs |
lsof | You specifically need to list open files and the processes holding them, including network sockets, without the ancestry narration witr provides. | Free, license varies by distribution |
glances | You want a Python-based, multi-dimensional system monitor with a web UI and optional client-server mode rather than a lineage-focused diagnostic CLI. | Free, LGPL-3.0 |
What this trend reveals
Pipe --json output into on-call runbooks
witr's --json flag emits machine-readable causal chains. An SRE team could wrap it in a script that captures the chain at the moment an alert fires, attaching the full ancestry to the incident ticket automatically.
Inspect the actual --json schema by running witr --port <port> --json in the playground or against a real machine, then write a jq filter against the fields before committing to the integration.
Extend --container to Kubernetes pod metadata
The --container flag already walks cgroup data for Docker and similar runtimes. A contributor could add a mapping layer that resolves container IDs to Kubernetes pod and namespace labels when running on a node.
Test on a real Kubernetes node where the cgroup hierarchy includes the kubelet-managed container runtime, and verify that the existing cgroup-walking code in internal/ can access the pod-level cgroup directory.
Add expected-vs-actual process baseline diffing
Because witr prints the full systemd ancestry, a wrapper could compare today's chain against a stored baseline for a given service and flag unexpected new children — useful for drift detection after deployments.
Capture baselines from a staging host with known services, then run the diff against a production host after the next deploy to confirm the comparison catches real drift without excessive false positives.
RepoDaily verdict
witr turns the most common Linux debugging question — why is this running? — into a single readable causal chain from target to systemd init. The bubbletea TUI, the zero-install browser playground with byte-for-byte CI fidelity checks, and the Apache-2.0 single-binary distribution make it one of the most approachable diagnostic CLIs on the Go trending list this week.