Cross-platform CLI + interactive TUI to list and kill processes by port
Project description
PortSlayer ⚡
Cross-platform CLI + interactive TUI to list active ports and safely kill processes by port number.
Features
- 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
Tech Stack
| 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 |
Installation
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 needed
Homebrew 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.
Minimum supported versions
| 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.
Install from source
git clone https://github.com/AppestoX/portslayer.git
cd portslayer
pip install -e ".[dev]"
Linux — additional notes
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 list
Windows — additional notes
Run PowerShell or Command Prompt as Administrator to see all processes.
netstat and taskkill are built into every Windows installation.
Usage
Interactive TUI (recommended)
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 |
CLI
List all active ports
portslayer list
Sample 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:… │
╰──────┴──────────┴─────────────┴───────┴────────────┴──────────────╯
Filter ports
# 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 --listening
Inspect a specific port — or a partial prefix
portslayer 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.
Kill the process on a port
portslayer kill 3000
kill 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 node
PortSlayer will:
- Show full process details
- Ask for explicit confirmation
- Kill the process only after
yis entered
# Skip confirmation (use with care)
portslayer kill 3000 --force
Scripting with JSON output
list 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 --json
Shell completion
Typer wires this up automatically — no extra setup needed:
portslayer --install-completion # installs completion for your current shell
Version
portslayer --version
Project Structure
portslayer/
├── 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
Running Tests
pip install -e ".[dev]"
pytest
With coverage:
pytest --cov=portslayer --cov-report=term-missing
Security
| 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-Specific Commands Used
| 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) |
Publishing (maintainers)
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.
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Run tests:
pytest - Open a pull request
License
MIT — free to use, modify, and distribute.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file portslayer-1.1.2.tar.gz.
File metadata
- Download URL: portslayer-1.1.2.tar.gz
- Upload date:
- Size: 24.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a961a3650b536b02e91e7409ea371046de3e5d0bb467ec9bcf2c0cc1fa94e28
|
|
| MD5 |
0bbf212d5044a76bf8a8af8d40698af8
|
|
| BLAKE2b-256 |
85aee041f25e4101e8ace556b5db3e3aa95fd6954b0fda01485c9913b2be2f49
|
Provenance
The following attestation bundles were made for portslayer-1.1.2.tar.gz:
Publisher:
release.yml on AppestoX/portslayer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
portslayer-1.1.2.tar.gz -
Subject digest:
2a961a3650b536b02e91e7409ea371046de3e5d0bb467ec9bcf2c0cc1fa94e28 - Sigstore transparency entry: 2168720954
- Sigstore integration time:
-
Permalink:
AppestoX/portslayer@727dd3de072683292cd8fcb0f48e08bd1f6dfa78 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/AppestoX
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@727dd3de072683292cd8fcb0f48e08bd1f6dfa78 -
Trigger Event:
push
-
Statement type:
File details
Details for the file portslayer-1.1.2-py3-none-any.whl.
File metadata
- Download URL: portslayer-1.1.2-py3-none-any.whl
- Upload date:
- Size: 20.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73055c6284435006863378c15a9c7eeaad81c2f95cac342520ad0955a4cfd37c
|
|
| MD5 |
1f1bf9df9c6d214290dcaaba7fe23ec0
|
|
| BLAKE2b-256 |
31ba7cde951985ed867ddcd51f59401b088a1deef0493cdc5648e7dc1661ca9d
|
Provenance
The following attestation bundles were made for portslayer-1.1.2-py3-none-any.whl:
Publisher:
release.yml on AppestoX/portslayer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
portslayer-1.1.2-py3-none-any.whl -
Subject digest:
73055c6284435006863378c15a9c7eeaad81c2f95cac342520ad0955a4cfd37c - Sigstore transparency entry: 2168721032
- Sigstore integration time:
-
Permalink:
AppestoX/portslayer@727dd3de072683292cd8fcb0f48e08bd1f6dfa78 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/AppestoX
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@727dd3de072683292cd8fcb0f48e08bd1f6dfa78 -
Trigger Event:
push
-
Statement type: