A gorgeous TUI dashboard for port and process monitoring on macOS
Project description
portpier
Lightweight TUI dashboard for monitoring and managing ports on macOS. System ports (< 1024) are always hidden.
portpier shows every active user-space socket (all states) with full process
metadata, a live-updating table, one-keypress process termination, a rich detail
view, and a command palette.
Install
macOS (Homebrew):
brew install fhswno/portpier/portpier
Or run it instantly without installing (needs uv):
uvx portpier
Other Python installers:
uv tool install portpier
pipx install portpier
Requires Python ≥ 3.11. macOS is the primary target; Linux works too. System ports (< 1024) are always hidden.
From source (development)
uv provisions the virtualenv and the Python version — no need to install Python yourself.
# From the project root:
make install # create the venv + install everything (runtime + dev tools)
make run # launch portpier
# …or, globally (editable — code changes are picked up without reinstalling):
uv tool install -e .
portpier
Keyboard reference
Main dashboard
| Key | Action |
|---|---|
↑ / ↓ |
Move row selection |
Enter |
Open the detail view for the selected process |
k |
Kill the selected process (opens the confirm dialog) |
/ |
Focus the search box |
s |
Cycle the sort column |
Esc |
Clear the search |
Ctrl+P |
Open the command palette |
t |
Cycle theme |
r |
Force an immediate data refresh |
q / Ctrl+Q |
Quit |
| Click a row | Select it |
| Click a column header | Sort by that column (toggle asc/desc) |
Search filters client-side (case-insensitive substring) across port, process name, type, state, and PID. The status bar shows the live match count.
Detail view
| Key | Action |
|---|---|
Esc / q |
Return to the dashboard (same row stays selected) |
k |
Kill this process (opens the confirm dialog; returns to the dashboard on success) |
↑ / ↓ |
Scroll the detail content |
Kill dialog
| Key | Action |
|---|---|
Tab / ← → ↑ ↓ |
Move between Graceful / Force Kill / Cancel |
Enter |
Activate the focused button (Graceful = SIGTERM, Force = SIGKILL) |
Esc |
Cancel / dismiss (no action) |
Command palette (Ctrl+P)
| Command | Action |
|---|---|
theme <name> |
Switch theme — dark, light, paper, or matrix |
toggle <column> |
Show/hide a column (e.g. toggle pid). PORT is always visible. |
sort <column> [asc|desc] |
Sort by port, memory, cpu, or uptime |
refresh <seconds> |
Set the refresh interval (0.5–60) |
port range <min> [max] |
Filter to a port range (e.g. port range 3000 9999) |
port range reset |
Clear the port-range filter (back to 1024–65535) |
help |
Show the command reference |
Every change is persisted immediately to the config file (below).
Themes
| Name | Look |
|---|---|
dark |
Default. Dark canvas, high contrast, purple accent. |
light |
Clean light mode for daytime/bright terminals. |
paper |
Stark black on white — print-like, minimal. |
matrix |
Green-on-black, monospace terminal aesthetic. |
Configuration
Config lives at ~/.config/portpier/config.toml. It is created the first time
you change a setting (via the palette); a fresh install runs entirely on
defaults. To reset, just delete the file.
schema_version = 1
[display]
theme = "dark" # "dark" | "light" | "paper" | "matrix"
refresh_interval = 2.0 # seconds; clamped to [0.5, 60.0]
columns = ["port", "process", "type", "pid", "memory", "cpu", "state", "uptime"]
default_sort_column = "port" # "port" | "memory" | "cpu" | "uptime"
default_sort_order = "asc" # "asc" | "desc"
[filters]
min_port = 1024 # hard floor: never below 1024
max_port = 65535
Unknown keys are ignored. An unknown/missing schema_version falls back to
defaults with a warning. min_port is always floored to 1024.
Notes
- Without
sudo: processes you own are fully visible. Other users' processes raiseAccessDeniedand don't appear; the status bar then shows⚠ Some processes require sudo to inspect. - With
sudo: all processes are visible and the warning is absent. - macOS sandboxes
open_files()/environ(), so the detail view's Open FDs and Environment sections are often empty withoutsudo— this is expected, not a bug.
Project layout
src/portpier/
├── __main__.py # CLI entry point (--version / --help / launch)
├── app.py # root Textual app
├── config.py # TOML config (load/save, validation, atomic write)
├── data/ # psutil collection + immutable models
├── ui/ # screens, widgets, themes, command palette
└── utils/ # formatting, process-type inference, signals
tests/ # pytest suite (data layer + parsers + signals)
The data layer is fully testable without instantiating a Textual app.
Common commands
make # list all targets
make check # typecheck + lint + test
make typecheck # mypy --strict on src/
make lint # ruff lint on src/
make fmt # auto-format + auto-fix with ruff
make test # run the test suite
make smoke # live data-layer check against real ports
make clean # remove the venv and all caches
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 portpier-0.1.1.tar.gz.
File metadata
- Download URL: portpier-0.1.1.tar.gz
- Upload date:
- Size: 57.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4920ab888d1546c686894f49a5f81990825f70f60c4683455af986b7eb4d53c4
|
|
| MD5 |
136bf786f0df7a6803364ffeff46c8c5
|
|
| BLAKE2b-256 |
f30637615cdf70ab98ece82c6d17ffd03b1fb74277aec8e5f5e5dc15a8c72579
|
Provenance
The following attestation bundles were made for portpier-0.1.1.tar.gz:
Publisher:
publish.yml on fhswno/portpier
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
portpier-0.1.1.tar.gz -
Subject digest:
4920ab888d1546c686894f49a5f81990825f70f60c4683455af986b7eb4d53c4 - Sigstore transparency entry: 1997713035
- Sigstore integration time:
-
Permalink:
fhswno/portpier@c2217d33c7dd3ebb7ac1fe28ab767111ce84c801 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fhswno
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c2217d33c7dd3ebb7ac1fe28ab767111ce84c801 -
Trigger Event:
push
-
Statement type:
File details
Details for the file portpier-0.1.1-py3-none-any.whl.
File metadata
- Download URL: portpier-0.1.1-py3-none-any.whl
- Upload date:
- Size: 37.0 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 |
c265679bc5353e4a3ae8eb34db62eb65225cca4203692cfce433728742133b2f
|
|
| MD5 |
f468b847f5884756aa9973762b4c92ce
|
|
| BLAKE2b-256 |
872a43f4bf269b8f76279c9ad2055d7f8d0b29ca16cf1dc9cbec6fe49da8f1a7
|
Provenance
The following attestation bundles were made for portpier-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on fhswno/portpier
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
portpier-0.1.1-py3-none-any.whl -
Subject digest:
c265679bc5353e4a3ae8eb34db62eb65225cca4203692cfce433728742133b2f - Sigstore transparency entry: 1997713195
- Sigstore integration time:
-
Permalink:
fhswno/portpier@c2217d33c7dd3ebb7ac1fe28ab767111ce84c801 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fhswno
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c2217d33c7dd3ebb7ac1fe28ab767111ce84c801 -
Trigger Event:
push
-
Statement type: