Infrastructure & Cloud
witr: a CLI that traces a process, port or container to what started it
pranshuparmar/witr on GitHub
By Mira Vance
Published 22 October 2026 · Last reviewed 22 October 2026

A repository created in December 2025 is already in the official Debian and Ubuntu repositories: on Ubuntu 26.04 and Debian sid, sudo apt install witr works, beside Homebrew, winget, conda-forge, the AUR and FreeBSD ports.
Drafted with AI assistance, researched, verified and edited by the HastCode editorial team. We publish only as many pieces as the Author can verify this way.
TL;DR
• witr is a single static binary for Linux, macOS, FreeBSD and Windows that answers one question, "why is this running?", for a process, port, container or file.
• The design choice: everything is a process question. A port, service or container is mapped to a PID, witr walks the chain that created it (systemd → pm2 → node) and names exactly one primary source.
• Its --json output is a documented contract and its exit codes carry meaning, so it fits scripts, CI and coding agents as well as a terminal.
• Adopt it today as an incident-time diagnostic on boxes you can sudo on. Look elsewhere for fleet-wide or historical answers: witr reads the live machine it runs on, and nothing in the README stores or forwards a result.
In 60 seconds
The README's Quick Install is one command on Linux, macOS and FreeBSD: the script detects your OS and CPU, downloads the latest released binary and man page, and installs them under /usr/local. The README recommends a package manager where you have one; brew install witr, sudo apt install witr and winget install -e --id PranshuParmar.witr are among the sixteen listed.
Then ask about anything by name. This is the README's first example; its output shows the chain systemd (pid 1) → pm2 (pid 5034) → node (pid 14233), the source (pm2), the working directory, the git branch and the sockets.
Run witr with no arguments and you get the TUI instead. To try it without installing anything, the browser playground simulates a Linux web box; its README says the JavaScript engine ports witr's output layer and CI checks it against golden output from the Go code.
Architecture
witr is one Go binary with no server or agent: each run reads the machine it is on and prints an answer. Walk the diagram left to right.
Target. You name a process, or pass --pid, --port, --file or --container, repeatable and mixable. The core concept: every target resolves to a PID. A port held by pid 1 (socket activation) or a runtime forwarder (docker-proxy, pasta, RootlessKit) is explained by the container that published it; that Docker CLI fallback arrived in v0.3.0.
Collectors. Linux reads /proc; macOS shells out to ps, lsof, sysctl and pgrep; Windows calls Win32 APIs (ToolHelp32, PSAPI, Service Control Manager) without PowerShell or WMI; FreeBSD uses procstat, ps and lsof. Container lookups need the runtime's CLI on PATH.
Ancestry. The chain runs up to pid 1. When the starter has exited, witr marks the break with ? (original parent exited), not the adopter, and since v0.3.4 it stops at recycled parent PIDs.
Source. One primary source is chosen: a systemd unit, launchd job, SSH session, Docker container, pm2, cron, an interactive shell or a Snap or Flatpak sandbox. If nothing explains the process, the source is unknown, never a guess.
Context, warnings, output. Working directory, git branch, container and sockets, then warnings such as root, a public listener or LD_PRELOAD. Output is text, --short, --tree, --json or the TUI, plus an exit code from 0 to 6. We infer witr keeps no state: the README describes nothing written between runs.
Signals
Use it if
• You correlate ps, lsof, ss, systemctl and docker ps by hand during incidents: witr's stated purpose is to replace that correlation with one answer that is understandable under stress.
• You want to script it: the JSON contract promises stable field names and types, with breaking changes only in a release whose notes call them out, and the exit codes separate "not found", "permission denied" and "cause unknown".
• Your estate mixes runtimes: the --container flag searches Docker, Podman, nerdctl, Kubernetes via crictl, Incus, LXC, LXD and FreeBSD jails, and matches on compose project and service labels.
• Coding agents work on your machines: the README ships an agent-instructions snippet and a Claude Code plugin so an agent runs witr --port <PORT> --json instead of chaining four tools.
Don't use it if
• You need fleet-wide or historical answers. witr answers for the box it runs on, at the moment you run it, and nothing in the README stores or forwards results. Use osquery, or your metrics and log stack, for "what was running yesterday on 400 hosts".
• Windows is your main platform and you need the full picture. The compatibility matrix marks file locks, tmux and screen detection, schedule detection and process actions unavailable on Windows, environment variables partial, and exit code 6 unused there. Keep Process Explorer beside it.
• You cannot get elevated privileges. Without root, a port held by another user's process names the user but not the process; on macOS, SIP hides some details even with sudo. The runtime's own tooling (systemctl status, docker inspect) may be what your permissions already allow.
Alternatives
• procps (ps, pstree from psmisc): the stock tools witr's README positions itself against; they show state and leave the correlation to you.
• procs: a ps replacement in Rust with a tree view and a Docker column; a better ps, not a causal chain with a named source.
• htop: an interactive process viewer with a tree; built for watching load, not for explaining one process.
• osquery: SQL over processes, listening ports and containers across a fleet, with a daemon and scheduled queries; heavier, and it covers the history and scale witr does not attempt.
• Process Explorer: Microsoft's Windows process tree with handles and DLLs, which covers the ground witr marks partial or unavailable on Windows.
Changelog
• v0.3.4 4 October 2026
• v0.3.3 24 June 2026
• v0.3.2 16 May 2026
• v0.3.1 18 March 2026
• v0.3.0 21 February 2026
• v0.2.7 8 February 2026
• v0.2.6 21 January 2026
• v0.2.5 15 January 2026
• v0.2.4 14 January 2026
• v0.2.3 11 January 2026
FAQ
Is witr production ready?
As a diagnostic, yes: witr ships as a single static binary for Linux, macOS, FreeBSD and Windows, is in the official Debian and Ubuntu repositories, Homebrew, winget and FreeBSD ports, and its JSON output is a documented contract. The version is still pre-1.0 (see the card).
Does witr support Docker and Kubernetes containers?
Yes. witr's --container flag searches Docker, Podman, nerdctl, Kubernetes via crictl, Incus, LXC, LXD and FreeBSD jails, matching on name, image, command or compose labels; the runtime's CLI must be on PATH.
Can I use witr in scripts and CI?
Yes. witr <target> --json prints a report with stable field names and types, and the exit codes say what happened: 0 clean, 1 warnings, 2 not found, 3 permission denied, 4 bad input, 5 internal error, 6 cause unknown.
What are the alternatives to witr?
The nearest alternatives to witr are procps (ps, pstree), procs, htop, osquery and, on Windows, Process Explorer. None produces witr's causal chain with one named source; witr, in turn, does not do fleet queries or history.
How is witr licensed, and who maintains it?
witr is under the Apache License 2.0. Pranshu Parmar (pranshuparmar) writes most of it (the share is in Signals); the contributors graph lists an account named claude second, so we infer part of the work is AI-assisted.
Verdict
witr does one thing a backend engineer needs at two in the morning and does it carefully: it turns "something is on port 5432" into "systemd started pm2, which started this", names the one source responsible, and admits when it cannot tell. We recommend it for anyone who runs Linux, macOS or FreeBSD hosts they can sudo on, and we would put it in a runbook and an agent's instructions on the strength of the JSON contract and the exit codes alone. It is not a monitor, keeps no history and knows nothing about the other 399 hosts, so it sits beside osquery or your metrics stack rather than replacing them; Windows users get the chain but not locks, schedules or process actions. A 1.0 release would remove our only hesitation, which is the pre-1.0 version number on a tool that already behaves like a finished one; a stored or shipped history would change what it is.
Sources
• witr playground README (docs/README.md)
• https://github.com/pranshuparmar/witr
• https://github.com/pranshuparmar/witr/releases
• https://github.com/pranshuparmar/witr/graphs/contributors
• https://github.com/pranshuparmar/witr/tree/main/.github
• https://github.com/pranshuparmar/witr/blob/main/README.md
Get new Explainers by email
How real systems are built, in your inbox. No spam; unsubscribe any time.
More in Infrastructure & Cloud
1. Case StudySingle-tenant Titus: how Netflix gives one container its own VMHow Netflix's Titus gives a few noisy or sensitive workloads a dedicated, right-sized EC2 instance under the same container API, and what that costs.26 October 2026
2. Repo ProfileOpenShell: NVIDIA's sandboxed runtime for autonomous AI agentsOpenShell is NVIDIA's self-hosted sandbox runtime for AI agents: kernel-enforced policy and a credential proxy, at the cost of a fast-moving 0.1 API.22 October 2026
Related
1. ComparisonConnection reuse in HTTP/1.1, HTTP/2 and HTTP/3: how each works and where each breaksHow HTTP/1.1, HTTP/2 and HTTP/3 reuse connections, where each one breaks, and which to use for calls between services.26 October 2026
2. Repo Profileetcd: a Raft-replicated key-value store for coordination dataetcd is a Raft-replicated, linearizable key-value store for coordination-sized data; fast disks and a few-gigabyte ceiling are the price.22 October 2026
3. Repo ProfileFrankenPHP: a PHP application server with the interpreter inside CaddyFrankenPHP embeds the PHP interpreter in the Caddy web server: one Go binary, a worker mode, no PHP-FPM, but every extension must be thread-safe.22 October 2026


