what it does
PortDoc opens a local web ui that answers the annoying questions around dev servers:
- what is running across local ports and dev services
- which project owns it based on process metadata and project-root hints
- what url to open for each local app or api
- why a port is taken when a server starts on the wrong number or fails
- what looks stale from an old terminal tab or forgotten repo
- which services are exposed as local-only, lan-visible, docker-bound, or unknown
- how to install it from
portdoc.devon Linux, macOS, and Windows
the terminal launches it. the browser is the product.
why i built it
i constantly have a few local apps running at once: astro, next, vite, node
apis, workers, local databases, docker containers, and the occasional process
that should have died yesterday. the usual answer is some mix of lsof, ss,
ps, memory, and guessing.
that is fine once. it is not fine as a daily workflow. i wanted a calm dashboard that thinks in projects, apps, ports, and urls, not raw socket trivia.
how it’s built
a quick tour:
- rust single binary: the app runs locally and ships as one executable
- axum server: serves the browser ui and exposes the local snapshot api
- embedded vite app: react and typescript for the dashboard, bundled into the binary for release builds
- snapshot contract: one json shape feeds the ui and
portdoc --json - platform probes: linux, macOS, and Windows adapters collect socket and process metadata with unknown owners treated as data, not failure
- dashboard ui: project cards, services table, status callouts, actions, and a graphite theme tuned for quick scanning
- release pipeline: cargo-dist builds the release artifacts, pushes the Homebrew tap, and signs Windows binaries for v0.1.1
- astro site: portdoc.dev is the public install page
and keeps
/install.shplus/install.ps1as 307 redirects to GitHub Release assets
what’s next
v0.1.1 is public, the website is live, and Windows binaries are signed. the next round is less about proving the product can ship and more about making it useful every day: better labels, remembered services, contributor triage, and clearer signals for the old dev server that is blocking the one i actually want to run.
the win is not seeing more network data. the win is knowing which old dev server is blocking the thing i actually want to run.