A local macOS dashboard to manage every port that your machine is listening on. For each port, Porthole shows the process, working directory, git repo and branch, and the agent or shell that spawned it, and gives you the ability to instantly kill or restart.
macOS only. Single static binary. No daemon, no telemetry, no network access beyond
127.0.0.1.
curl -fsSL https://raw.githubusercontent.com/ollfel/porthole/main/install.sh | bashOr grab a release tarball directly from the
Releases page and drop the binary
in your $PATH.
portholeThen open http://127.0.0.1:47212.
The first poll runs immediately, then every 2 seconds. New ports flash with a "new" badge. Closing a process makes its row disappear and (after the next poll) move into history.
porthole serve the dashboard (default)
porthole --port 47213 bind a different port
porthole clear-history delete all closed-port rows
porthole clear-all delete every row (live reappears on next poll)
Increasingly developer workflows require co-ordination of multiple agents from different tools, working not just on different features but across different projects in parallel.
Each agent spins up its own dev server, databases and background processes, each on its own port: your local machine is a stateful environment that changes continuously without your explicit direction or, possibly, awareness. Your machine accumulates running servers, stale processes and occupied ports that you did not explicitly start and cannot easily see.
Remembering what each port is being used for and resolving conflicts forces you to context switch and acts as a drag on shipping what you care about.
Existing solutions do not account for multiple agents, from different AI tools, working across many projects concurrently:
- Terminal gives you
lsof -i | grep LISTEN, a static snapshot of ports and PIDs but little else meaningful. - Individual coding tools — Claude Code, Codex, Conductor, Cursor — operate with their own context, unaware of each other and what else is running.
- Docker Desktop solves the problem only for containerised processes.
- Other GUIs, such as Netiquette, show port usage but do not know about your project or agent workflows.
Porthole gives you continuous visibility into every process and which repo owns which port:
- Shows every running dev server — with the project name derived automatically from the process working directory, not a raw PID.
- Displays the port each server is running on.
- Kill or restart any server with a single click.
- Updates live — so when an agent spins something up or a process dies unexpectedly, you see it immediately.
- Surfaces silent failures — orphaned processes, stale servers, port conflicts — before they cause confusion or wasted debugging time.
Any developer who has ever:
- Forgotten which project is running on which port
- Tried to start a server only to find the port already in use
- Lost track of what an AI agent started on their behalf
- Juggled multiple projects simultaneously and wished they had a control panel
- Spent ten minutes debugging a cryptic error that turned out to be a zombie process holding a port
Restart re-runs the original command in its original working directory using
Porthole's own environment. This works great for most dev servers —
next dev, python3 -m http.server, bun run start, and anything else that
reads config from files in cwd.
If your process needs custom env vars (virtualenvs, API keys, direnv,
1Password CLI, aws-vault, doppler, etc.), the restart will likely fail.
Use Copy instead: it gives you cd {cwd} && {command} ready to paste into
your own terminal, where your secret managers are already live. That way
secrets stay in your shell, not in Porthole.
Porthole binds 127.0.0.1 only, validates the Host header on every request
(blocking DNS rebinding), and requires a same-origin Origin header on POST
routes. /api/kill sends SIGTERM via process.kill(pid) and can only
affect processes your user already owns.
History is stored at ~/.porthole/history.db (SQLite, 30-day retention).
Clear it anytime with porthole clear-history.
See ARCHITECTURE.md for the full threat model.
bun install
bun test # 63 tests, ~200ms
bun run dev # bun --hot, edit src/* and refresh
bun run build # dist/porthole-darwin-arm64
bun run build:all # arm64 + x64The compiled binary embeds src/index.html at build time, so the static
binary in dist/ is fully self-contained — no asset paths to chase.
src/
poller.ts # lsof wrapper + diff
enricher.ts # cwd, git, parent-chain, docker heuristic
db.ts # bun:sqlite wrapper
server.ts # Bun.serve + SSE + CLI entry point
index.html # single-file frontend (no bundler)
*.test.ts # bun:test suites
migrations/
0001_init.sql # schema (also embedded in db.ts)
.github/workflows/
ci.yml # tests on every push
release.yml # tag → build arm64 + x64 → GitHub release
install.sh # curl | bash installer
ARCHITECTURE.md # how it's built (poll loop, data model, security)
DESIGN.md # visual design system (typography, color, spacing)
Found a bug, have a feature idea, or something just feels wrong? Open an issue at https://github.com/ollfel/porthole/issues. A bug report template is included to capture the most useful repro info.
MIT © 2026 ollfel