Cross-platform CLI + interactive TUI to list active ports and safely kill processes by port number.
- Interactive TUI dashboard — just run
pk(orportslayer): arrow keys to navigate,/to search,kto kill, live auto-refresh - Short alias — every command works as both
portslayer <cmd>and the 2-letterpk <cmd> - List all active ports — port, PID, process name, protocol, state, addresses
- Filter by port number, process name, or protocol (TCP/UDP)
- Kill processes by port or by process name — with mandatory confirmation before any destructive action
- JSON output (
--json) for scripting, plus built-in shell completion (--install-completion) - Cross-platform — Linux (via
ss/lsof), Windows (vianetstat/taskkill), macOS (vialsof) - Install however you like — pip, npx/npm, or uvx
| Layer | Technology | Reason |
|---|---|---|
| TUI | Python + Textual | Full-screen keyboard-driven dashboard, no extra runtime for users |
| CLI | Python + Typer + Rich | Single language, beautiful tables, type-safe commands |
| Core | Pure Python stdlib | No external dependencies for port scanning / killing |
| Tests | pytest | Simple, fast, widely adopted |
| Packaging | pyproject.toml (setuptools) | PEP 517/518 standard, pip-installable |
Pick whichever package manager you already have — they all install the same tool.
pip install portslayer # Python / pip — requires Python >= 3.9
npx @appestox/portslayer # Node.js — try it with zero install
npm install -g @appestox/portslayer # Node.js — install permanently
uvx portslayer # zero-install, no Python setup neededHomebrew and Scoop manifests live in packaging/ but are not
published to a public tap/bucket yet.
The npm package is a thin wrapper that installs the same portslayer PyPI
package underneath — see packaging/ for how each is built
and published.
| Method | Requirements |
|---|---|
pip install portslayer |
Python 3.9+ |
npx @appestox/portslayer / npm install -g |
Node 14+ and Python 3.9+ (the wrapper pip-installs the PyPI package; it does not install Python for you) |
uvx portslayer |
uv — it downloads a compatible Python automatically |
If pip says No matching distribution found for portslayer, your Python is
older than 3.9 — check with python --version and upgrade.
git clone https://github.com/AppestoX/portslayer.git
cd portslayer
pip install -e ".[dev]"No extra system packages required. ss is available by default on all modern
Linux distributions (iproute2). lsof is used as a fallback if ss is
not available.
To see processes owned by other users you must run with sudo:
sudo portslayer listRun PowerShell or Command Prompt as Administrator to see all processes.
netstat and taskkill are built into every Windows installation.
pk(pk is the short alias for portslayer — both commands are identical and installed together; use whichever you prefer. The rest of this doc uses portslayer for clarity, but pk works everywhere it does.)
Launches a full-screen dashboard — no flags to remember:
| Key | Action |
|---|---|
↑ / ↓ |
Navigate the port list |
/ |
Search/filter (port, process, protocol, state) |
k / Del |
Kill the selected process (asks to confirm) |
r |
Refresh now (auto-refreshes every 3s anyway) |
Esc |
Clear search |
q |
Quit |
portslayer listSample output:
╭────────────────────────────────────────────────────────────────────╮
│ Active Ports (24 entries) │
├──────┬──────────┬─────────────┬───────┬────────────┬──────────────┤
│ Port │ Protocol │ State │ PID │ Process │ Local Address│
├──────┼──────────┼─────────────┼───────┼────────────┼──────────────┤
│ 22 │ TCP │ LISTEN │ 783 │ sshd │ 0.0.0.0:22 │
│ 80 │ TCP │ LISTEN │ 1042 │ nginx │ 0.0.0.0:80 │
│ 5432 │ TCP │ LISTEN │ 2103 │ postgres │ 127.0.0.1:… │
╰──────┴──────────┴─────────────┴───────┴────────────┴──────────────╯
# By exact port number
portslayer list --port 443
# By partial port prefix — matches every port starting with these digits
portslayer list --port 808 # -> 8080, 8081, 8083, ...
# By process name (partial match)
portslayer list --process nginx
# By protocol
portslayer list --protocol tcp
# Only LISTENING ports
portslayer list --listening
# Combine filters
portslayer list --protocol tcp --listeningportslayer find 8080 # exact port
portslayer find 808 # partial prefix -> matches 8080, 8081, 8083, ...Typing fewer digits is a feature, not an error — no need to remember or type the full number if you just want to see everything in that range.
portslayer kill 3000kill always requires an exact port number (not a prefix) — this is a
deliberate safety choice so a mistyped digit can't broaden which processes
get terminated. Use find/list with a prefix first to see what's there,
then kill the exact port you want.
# Kill by process name instead of port (kills every port that process owns)
portslayer kill --name nodePortSlayer will:
- Show full process details
- Ask for explicit confirmation
- Kill the process only after
yis entered
# Skip confirmation (use with care)
portslayer kill 3000 --forcelist and find both accept --json for piping into jq or other tools
instead of printing a table:
portslayer list --json | jq '.[] | select(.protocol == "TCP")'
portslayer find 808 --jsonTyper wires this up automatically — no extra setup needed:
portslayer --install-completion # installs completion for your current shellportslayer --versionportslayer/
├── portslayer/
│ ├── __init__.py # Package version / metadata
│ ├── cli.py # Typer CLI (list / find / kill) — bare invocation launches the TUI
│ ├── tui.py # Textual interactive dashboard
│ ├── core/
│ │ ├── __init__.py
│ │ ├── models.py # PortInfo dataclass
│ │ ├── port_scanner.py # Platform-specific scanning + parsers
│ │ └── process_killer.py# Safe process termination
│ └── utils/
│ ├── __init__.py
│ ├── platform_utils.py# OS detection, admin check
│ └── validators.py # Port number validation
├── tests/
│ ├── test_validators.py
│ ├── test_port_scanner.py
│ └── test_process_killer.py
├── packaging/
│ ├── npm/ # npm wrapper package (`npx @appestox/portslayer`)
│ ├── homebrew/ # Homebrew tap formula
│ └── scoop/ # Scoop bucket manifest
├── pyproject.toml
├── requirements.txt
├── requirements-dev.txt
├── LICENSE
└── README.md
pip install -e ".[dev]"
pytestWith coverage:
pytest --cov=portslayer --cov-report=term-missing| Concern | Mitigation |
|---|---|
| Command injection | All subprocess calls use explicit argument lists — never shell=True |
| Input validation | Port numbers are validated (regex + range check) before any system call |
| Confirmation requirement | Every kill operation requires explicit user confirmation |
| Privilege transparency | Users are warned when not running as root/Administrator |
| PID validation | PID existence is verified before sending signals |
PortSlayer never executes raw shell input from the user.
| Platform | List ports | Get process names | Kill process |
|---|---|---|---|
| Linux | ss -tulnp → lsof -i -n -P |
embedded in ss output |
os.kill(pid, SIGKILL) |
| Windows | netstat -ano |
tasklist /FO CSV /NH |
taskkill /PID <pid> /F |
| macOS | lsof -i -n -P |
embedded in lsof |
os.kill(pid, SIGKILL) |
Order matters — the npm, Homebrew, and Scoop packages all install the PyPI package under the hood, so PyPI goes first.
- PyPI (source of truth):
python -m pip install build twine python -m build twine upload dist/* - npm wrapper (
packaging/npm/) — bumpversioninpackaging/npm/package.jsonto match the PyPI release, then:cd packaging/npm && npm publish
- Homebrew tap (
packaging/homebrew/) — seepackaging/homebrew/README.mdfor generating dependency resource blocks and the sdist checksum, then push to yourhomebrew-portslayertap repo. - Scoop bucket (
packaging/scoop/) — seepackaging/scoop/README.mdfor the checksum step, then push to your bucket repo. Winget requires a compiled installer and is documented as a follow-up in the same file.
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Run tests:
pytest - Open a pull request
MIT — free to use, modify, and distribute.