README.md
with Interactive TUI Mode ✨
📖 Read the story behind witr
</div>Purpose • Installation • ✨ TUI • Flags • Examples • Platforms
Goals • Core Concept • Output Behavior • Success Criteria • Sponsors
</div>witr exists to answer a single question:
Why is this running?
When something is running on a system, whether it is a process, a service, or something bound to a port, there is always a cause. That cause is often indirect, non-obvious, or spread across multiple layers such as supervisors, containers, services, or shells.
Existing tools (ps, top, lsof, ss, systemctl, docker ps) expose state and metadata. They show what is running, but leave the user to infer why by manually correlating outputs across tools.
witr makes that causality explicit.
It explains where a running thing came from, how it was started, and what chain of systems is responsible for it existing right now, in a single, human-readable output or an interactive TUI dashboard.
witr is distributed as a single static binary for Linux, macOS, FreeBSD, and Windows.
witr is also independently packaged and maintained across multiple operating systems and ecosystems. An up-to-date overview of packaging status is available on Repology. Please note that community packages may lag GitHub releases due to independent review and validation.
[!TIP] If you use a package manager (Homebrew, Conda, Winget, etc.), we recommend installing via that for easier updates. Otherwise, the install script is the quickest way to get started.
curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh | bash
The script will:
linux, darwin or freebsd)amd64 or arm64)/usr/local/bin/witr/usr/local/share/man/man1/witr.1irm https://raw.githubusercontent.com/pranshuparmar/witr/main/install.ps1 | iex
The script will:
witr.exe to %LocalAppData%\witr\bin.PATH.You can install witr using Homebrew on macOS or Linux:
brew install witr
You can install witr using conda, mamba, or pixi on macOS, Linux, and Windows:
conda install -c conda-forge witr
# alternatively using mamba
mamba install -c conda-forge witr
# alternatively using pixi
pixi global install witr
On Arch Linux and derivatives, install from the AUR package:
yay -S witr-bin
# alternatively using paru
paru -S witr-bin
# or use your preferred AUR helper
You can install witr via winget:
winget install -e --id PranshuParmar.witr
You can install witr using npm:
npm install -g @pranshuparmar/witr
You can install witr on FreeBSD from the FreshPorts port:
pkg install witr
# or
pkg install sysutils/witr
Or build from Ports:
cd /usr/ports/sysutils/witr/
make install clean
You can install witr using Chocolatey:
choco install witr
You can install witr using Scoop:
scoop install main/witr
You can install witr from the AOSC OS repository:
oma install witr
You can install witr from the GNU Guix repository:
guix install witr
You can install witr using uniget:
uniget install witr
You can install witr using aqua:
# Add package
aqua g -i pranshuparmar/witr
# Install package
aqua i pranshuparmar/witr
You can install witr using brioche:
brioche install -r witr
witr provides native packages for major Linux distributions. You can download the latest .deb, .rpm, or .apk package from the GitHub releases page.
Generic download command using curl:
# Replace <package name with the actual package that you need>
curl -LO https://github.com/pranshuparmar/witr/releases/latest/download/<package-name>
Debian/Ubuntu (.deb):
sudo dpkg -i ./witr-*.deb
# Or, using apt for dependency resolution:
sudo apt install ./witr-*.deb
Fedora/RHEL/CentOS (.rpm):
sudo rpm -i ./witr-*.rpm
Alpine Linux (.apk):
sudo apk add --allow-untrusted ./witr-*.apk
You can install the latest version directly from source:
go install github.com/pranshuparmar/witr/cmd/witr@latest
This will place the witr binary in your $GOPATH/bin or $HOME/go/bin directory. Make sure this directory is in your PATH.
If you prefer manual installation, follow these simple steps for your platform:
Unix (Linux, macOS, FreeBSD)
# 1. Determine OS and Architecture
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "x86_64" ] && ARCH="amd64"
[ "$ARCH" = "aarch64" ] && ARCH="arm64"
# 2. Download the binary
curl -fsSL "https://github.com/pranshuparmar/witr/releases/latest/download/witr-${OS}-${ARCH}" -o witr
# 3. Verify checksum (Optional)
curl -fsSL "https://github.com/pranshuparmar/witr/releases/latest/download/SHA256SUMS" -o SHA256SUMS
grep "witr-${OS}-${ARCH}" SHA256SUMS | (sha256sum -c - 2>/dev/null || shasum -a 256 -c - 2>/dev/null)
rm SHA256SUMS
# 4. Rename and install
chmod +x witr
sudo mkdir -p /usr/local/bin
sudo mv witr /usr/local/bin/witr
# 5. Install man page (Optional)
sudo mkdir -p /usr/local/share/man/man1
sudo curl -fsSL https://github.com/pranshuparmar/witr/releases/latest/download/witr.1 -o /usr/local/share/man/man1/witr.1
Windows (PowerShell)
# 1. Determine Architecture
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") {
$ZipName = "witr-windows-amd64.zip"
} elseif ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
$ZipName = "witr-windows-arm64.zip"
} else {
Write-Error "Unsupported architecture: $($env:PROCESSOR_ARCHITECTURE)"
exit 1
}
# 2. Download the zip
Invoke-WebRequest -Uri "https://github.com/pranshuparmar/witr/releases/latest/download/$ZipName" -OutFile "witr.zip"
# 3. Extract the binary
Expand-Archive -Path "witr.zip" -DestinationPath "." -Force
# 4. Verify checksum (Optional)
Invoke-WebRequest -Uri "https://github.com/pranshuparmar/witr/releases/latest/download/SHA256SUMS" -OutFile "SHA256SUMS"
$hash = Get-FileHash -Algorithm SHA256 .\witr.zip
$expected = Select-String -Path .\SHA256SUMS -Pattern $ZipName
if ($expected -and $hash.Hash.ToLower() -eq $expected.Line.Split(' ')[0]) { Write-Host "Checksum OK" } else { Write-Host "Checksum Mismatch" }
# 5. Install to local bin directory
$InstallDir = "$env:LocalAppData\witr\bin"
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
Move-Item .\witr.exe $InstallDir\witr.exe -Force
# 6. Add to User Path (Persistent)
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($UserPath -notlike "*$InstallDir*") {
[Environment]::SetEnvironmentVariable("Path", "$UserPath;$InstallDir", "User")
$env:Path += ";$InstallDir"
Write-Host "Added to Path. You may need to restart PowerShell."
}
# 7. Cleanup
Remove-Item witr.zip
Remove-Item SHA256SUMS
If you use Nix, you can build witr from source and run without installation:
nix run github:pranshuparmar/witr -- --help
If you use pixi, you can run without installation on Linux or macOS:
pixi exec witr --help
witr --version
man witr
witr supports tab completion for all flags. To enable it, add the appropriate line to your shell configuration:
Bash
echo 'eval "$(witr completion bash)"' >> ~/.bashrc
source ~/.bashrc
Zsh
echo 'eval "$(witr completion zsh)"' >> ~/.zshrc
source ~/.zshrc
Fish
witr completion fish | source
# To make it permanent:
witr completion fish > ~/.config/fish/completions/witr.fish
PowerShell
witr completion powershell | Out-String | Invoke-Expression
# To make it permanent, add the above line to your $PROFILE
If you installed via a package manager (Homebrew, Conda, etc.), please use the respective uninstall command (e.g., brew uninstall witr).
To completely remove script/manual installation of witr:
Unix (Linux, macOS, FreeBSD)
sudo rm -f /usr/local/bin/witr
sudo rm -f /usr/local/share/man/man1/witr.1
Windows
Remove-Item -Recurse -Force "$env:LocalAppData\witr"
Running witr without any arguments or with the -i flag launches the Interactive Mode (TUI). This provides a real-time, terminal-based dashboard for exploring processes and ports.
--env show environment variables for the process
-x, --exact use exact name matching (no substring search)
-f, --file strings file path(s) to find process for (repeatable)
-h, --help help for witr
-i, --interactive interactive mode (TUI)
--json show result as JSON
--no-color disable colorized output
-p, --pid strings pid(s) to look up (repeatable)
-o, --port strings port(s) to look up (repeatable)
-s, --short show only ancestry
-t, --tree show only ancestry as a tree
--verbose show extended process information
-v, --version version for witr
--warnings show only warnings
Positional arguments (without flags) are treated as process or service names. Multiple names can be passed. By default, name matching uses substring matching (fuzzy search). Use --exact to match only processes with the exact name.
All target flags (--pid, --port, --file) are repeatable and can be mixed with each other and with positional name arguments. When multiple targets are provided, results are shown sequentially with labeled dividers. All output modes (standard, short, tree, JSON, env, warnings, verbose) work with multiple inputs.
The TUI is launched if no arguments or relevant flags (--pid, --port, --file) are provided, or if the --interactive flag is explicitly used.
witr node
Target : node
Process : node (pid 14233)
User : pm2
Command : node index.js
Started : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)
Restarts : 1
Why It Exists :
systemd (pid 1) → pm2 (pid 5034) → node (pid 14233)
Source : pm2
Working Dir : /opt/apps/expense-manager
Git Repo : expense-manager (main)
Listening : 127.0.0.1:5001
witr --port 5000 --short
systemd (pid 1) → PM2 v5.3.1: God (pid 1481580) → python (pid 1482060)
witr --pid 143895 --tree
systemd (pid 1)
└─ init-systemd(Ub (pid 2)
└─ SessionLeader (pid 143858)
└─ Relay(143860) (pid 143859)
└─ bash (pid 143860)
└─ sh (pid 143886)
└─ node (pid 143895)
├─ node (pid 143930)
├─ node (pid 144189)
└─ node (pid 144234)
Note: Tree view includes child processes (up to 10) and highlights the target process.
witr ng
Multiple matching processes found:
[1] nginx (pid 2311)
nginx -g daemon off;
[2] nginx (pid 24891)
nginx -g daemon off;
[3] ngrok (pid 14233)
ngrok http 5000
Re-run with:
witr --pid <pid>
To avoid substring matching and only find processes with an exact name, use the --exact flag:
witr nginx -x
witr --file /var/lib/dpkg/lock
Explains the process holding a file open.
witr nginx --port 5432 --pid 1234
----- [name: nginx] -----
Target : nginx
Process : nginx (pid 2311)
...
----- [port: 5432] -----
Target : postgres
Process : postgres (pid 891)
...
----- [pid: 1234] -----
Target : node
Process : node (pid 1234)
...
All target flags are repeatable and can be mixed. Results appear in the order you typed them. All output modes (--short, --tree, --json, --env, --warnings, --verbose) work with multiple inputs.
/proc).ps, lsof, sysctl, pgrep.Get-CimInstance, tasklist, netstat.procstat, ps, lsof.| Feature | Linux | macOS | Windows | FreeBSD | Notes |
|---|---|---|---|---|---|
| Process Selection | |||||
| By Name | ✅ | ✅ | ✅ | ✅ | |
| By PID | ✅ | ✅ | ✅ | ✅ | |
| By Port | ✅ | ✅ | ✅ | ✅ | |
| By File | ✅ | ✅ | ❌ | ✅ | |
| Multiple/mixed inputs | ✅ | ✅ | ✅ | ✅ | Repeatable flags, mixed types. |
| Exact Match | ✅ | ✅ | ✅ | ✅ | |
| Full command line | ✅ | ✅ | ✅ | ✅ | |
| Process start time | ✅ | ✅ | ✅ | ✅ | |
| Working directory | ✅ | ✅ | ✅ | ✅ | |
| Environment variables | ✅ | ⚠️ | ❌ | ✅ | macOS: Partial support due to SIP restrictions. |
| Network | |||||
| Listening ports | ✅ | ✅ | ✅ | ✅ | |
| Bind addresses | ✅ | ✅ | ✅ | ✅ | |
| Port → PID resolution | ✅ | ✅ | ✅ | ✅ | |
| Service Detection | |||||
| Service Manager | ✅ | ✅ | ✅ | ✅ | Linux: systemd, macOS: launchd, Windows: Services, FreeBSD: rc.d |
| Service Description | ✅ | ✅ | ✅ | ✅ | Linux: Description, macOS: Comment, Windows: Display Name, FreeBSD: rc header |
| Configuration Source | ✅ | ✅ | ✅ | ✅ | Linux: Unit File, macOS: Plist, Windows: Registry Key, FreeBSD: Rc Script |
| Supervisor | ✅ | ✅ | ✅ | ✅ | |
| Containers | ✅ | ✅ | ✅ | ✅ | Docker (plus Compose mappings), Podman, K8s (Kubepods), Containerd. Colima on macOS/Linux. Jails on FreeBSD. |
| SSH session detection | ✅ | ✅ | ✅ | ✅ | Detects remote IP and terminal. |
| tmux/screen detection | ✅ | ✅ | ❌ | ✅ | Shows session name in source. |
| Schedule detection | ✅ | ✅ | ❌ | ❌ | Linux: systemd timers, macOS: launchd intervals/calendar. |
| Snap/Flatpak detection | ✅ | ❌ | ❌ | ❌ | |
| Health & Diagnostics | |||||
| CPU usage detection | ✅ | ✅ | ✅ | ✅ | |
| Memory usage detection | ✅ | ✅ | ✅ | ✅ | |
| Health status detection | ✅ | ✅ | ✅ | ✅ | |
| Open Files / Handles | ✅ | ✅ | ⚠️ | ✅ | Windows: count only. |
| Deleted binary detection | ✅ | ✅ | ✅ | ✅ | Warns if executable is missing. |
| Capability warnings | ✅ | ❌ | ❌ | ❌ | Warns about dangerous capabilities on non-root processes. |
| Context | |||||
| Git repo/branch detection | ✅ | ✅ | ✅ | ✅ | |
| Interactive Mode (TUI) | |||||
| Process Dashboard | ✅ | ✅ | ✅ | ✅ | |
| Port Dashboard | ✅ | ✅ | ✅ | ✅ | |
| Process Details | ✅ | ✅ | ✅ | ✅ | |
| Process Actions | ✅ | ✅ | ❌ | ✅ |
Legend: ✅ Full support | ⚠️ Partial/limited support | ❌ Not available
witr inspects system directories which may require elevated permissions.
If you are not seeing the expected information, try running witr with sudo:
sudo witr [your arguments]
On macOS, witr uses ps, lsof, and launchctl to gather process information. Some operations may require elevated permissions:
sudo witr [your arguments]
Note: Due to macOS System Integrity Protection (SIP), some system process details may not be accessible even with sudo.
On Windows, witr uses Get-CimInstance, tasklist, and netstat. To see details for processes owned by other users or system services, you must run the terminal as Administrator.
# Run in Administrator PowerShell
.\witr.exe [your arguments]
witr treats everything as a process question.
Ports, services, containers, and commands all eventually map to PIDs. Once a PID is identified, witr builds a causal chain explaining why that PID exists.
At its core, witr answers:
witr returns meaningful exit codes for use in scripts, CI pipelines, and monitoring:
| Code | Meaning |
|---|---|
| 0 | Clean: process found, no warnings |
| 1 | Warnings: process found but has one or more warnings |
| 2 | Not found: no matching process or service |
| 3 | Permission denied: insufficient privileges |
| 4 | Invalid input: bad arguments or ambiguous match |
witr nginx --short
case $? in
0) echo "All clear" ;;
1) echo "Warnings detected" ;;
2) echo "Process not running" ;;
3) echo "Need elevated privileges" ;;
4) echo "Invalid input or ambiguous match" ;;
esac
What the user asked about.
Executable, PID, user, command, start time and restart count.
A causal ancestry chain showing how the process came to exist. This is the core value of witr.
The primary system responsible for starting or supervising the process (best effort).
Examples:
Only one primary source is selected.
Non‑blocking observations such as:
witr is successful if:
Special thanks to the people supporting witr ❤️
<p> <a href="https://github.com/timcolson" title="Tim Colson"> </a> </p>