RepoDaily · 2026-08-10 · Self-hosted app

witr traces any process, port, container, or file back to what started it

#9 Self-hosted app Go +342 pranshuparmar/witr Open repository

A Go CLI and bubbletea TUI that reconstructs the causal chain behind a running process, listening port, container, or held lock, with a zero-install browser playground for guided trial.

Repo typeSelf-hosted app
Best forLinux operators and developers who debug mysterious processes, stuck ports, dpkg locks, or container sprawl and want the full systemd ancestry in one readable chain
Risk levellow
Time to evaluate10–15 minutes in the browser playground; under 5 minutes to build the CLI with Go 1.25+

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?

90/100

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.

Directional score from RepoDaily sources and adoption notes, not a benchmark.Risk: Low
96Evidence quality

4 source(s) across 3 source category/categories, plus a RepoDaily-specific evidence module when available.

100Installability

5 workflow step(s), 6 next-action step(s), and 4 command/install signal(s) were detected.

70Maintenance confidence

Trending momentum is +342 stars, with maintenance/release/issue signals counted when present.

100Production readiness

Risk is marked low, with 4 security note(s) and 3 explicit skip condition(s).

100Differentiation

3 opportunity lens item(s), 5 alternative(s), and 4 type-specific section(s) support differentiation.

82License clarity

License source or license wording is present.

60Agent / AI fit

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.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

Low

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

ApproachWhen to useTrade-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.

Best next action

Run the webbox incident in the browser playground

Open the GitHub Pages playground, let the cold open play, then resolve the :8000 EADDRINUSE, dpkg lock, and Node app weight tasks using --port, --file, --pid, and --verbose. This exercises witr's core causal chain without installing anything and takes under 15 minutes.

  1. Navigate to the witr GitHub Pages URL (linked from the repo).
  2. Select the webbox scenario and let the cold-open incident play.
  3. Read the briefing and the left-side task tracker.
  4. Run `witr --port 8000 --verbose` to trace the http.server squatting the port.
  5. Run `witr --file /var/lib/dpkg/lock` to find the unattended-upgrade holding the dpkg lock.
  6. Optionally try --json, --tree, and --env as side quests, then check the process constellation view to see the chain light up.

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.

Sources