Free • macOS 13+ • Local-first

Know what's listening. Share it when you mean it.

PortShelf is a menu-bar utility that shows every local listening port, the process behind it, and the project that owns it - with safe local actions, named .localhost URLs, and opt-in sharing through Cloudflare or Tailscale.

Free download from GitHub. Local-first by default. Public routes are created only when you explicitly opt in.
Optional Sharing

Put one local service where the right people can reach it.

PortShelf can create a Cloudflare Tunnel for a detected listener, add the proxied DNS route, and keep the connector running from the menu-bar app. Or use an existing Tailscale install to publish a private tailnet Service, with Funnel available only after an explicit public-share confirmation.

Native Cloudflare management

Downloads verified cloudflared builds, creates DNS and Access resources, and removes them on Stop Sharing.

Tailscale-aware guardrails

Checks signed-in status, tagged-node requirements, and allowed Funnel ports before attempting a share.

Nothing global by accident

Cloudflare public routes and Tailscale Funnel both require explicit confirmation before a listener is exposed.

Share via PortShelfportshelf-web.welford.me
Opt-in
CloudflarePublic or Access
TailscaleService or Funnel
Stop SharingCleans up the route
Features

Your localhost, on a shelf.

Cloudflare Tunnel sharing

Expose a selected local listener on your Cloudflare-managed hostname, with PortShelf managing the tunnel, DNS route, Access policy, and cleanup.

Tailscale Services and Funnel

Share privately to your tailnet with Tailscale Services, or publish an allowed Funnel port when your tailnet policy permits it.

Name the port, not the number

Attach a friendly name to a project+port pair and open it as personal.localhost instead of hunting for localhost:3000.

Which project owns port 3000?

PortShelf walks up from the process working directory past marker files - .git, package.json, go.mod, Cargo.toml - to name the project, not just the PID.

Framework detection

Next.js, Vite, Rails, Django, Go, Rust, Postgres, Redis and more, inferred from the command that started the listener.

Safe actions

Open the URL, copy it, reveal the project in Finder, or open it in your editor. One click each, right from the menu bar.

Redacted diagnostics

Copy Diagnostics strips token=, secret=, bearer and other secret-looking values before anything reaches your clipboard.

Quiet by design

Menu-bar first with no Dock icon. Optional local notifications when common dev ports change. Launch at login if you want it.

Named Ports

Turn localhost numbers into memorable local names.

When a listener belongs to a detected project, PortShelf can publish a bare .localhost route for it. Name your Gatsby site personal once, then open it through personal.localhost in the browser.

Pick a detected listener

Named ports are scoped to a known project folder plus the port, so the route follows the project without claiming unknown system listeners.

Give it a hostname

Choose a lowercase single-label name like personal. PortShelf maps it to personal.localhost and keeps direct localhost:port URLs available.

Open HTTP or HTTPS

An opt-in local router helper leases loopback port 80/443 only while PortShelf is open, then forwards traffic to the live listener.

Project listenerpersonal Gatsby sitelocalhost:3000
Named URLpersonal.localhostHTTP or HTTPS

HTTPS routes use a locally generated PortShelf CA. After one Keychain trust approval, PortShelf issues exact host certificates such as personal.localhost for Safari, Chrome, and Edge.

How it works

Fixed tools. No magic.

PortShelf inspects the local machine using the same macOS tools you would: lsof for listening sockets, ps for command and parent data. Named routing is an explicit opt-in helper that binds only to loopback while PortShelf is open. Sharing is also opt-in: Cloudflare uses OAuth, Tailscale uses user-provided OAuth client credentials, and secrets stay in Keychain.

$lsof -nP -iTCP -sTCP:LISTEN -Fpcfn
$ps -p <pid> -o pid= -o ppid= -o command=
$lsof -a -d cwd -p <pid> -Fn
Privacy

All inspection stays on this Mac.

PortShelf does not send process, project, port, diagnostic, route, or certificate data to a PortShelf backend. Cloudflare and Tailscale sharing touch third-party APIs only for listeners you explicitly share, and provider secrets stay in Keychain.

Read the full Privacy Policy
No backendNo telemetryNo analyticsNo account systemNo sudoOpt-in sharing onlyCloudflare Access allowlistsProvider tokens in KeychainOpt-in local helperLoopback-only routesLocal certificatesNo packet sniffingNo cloud sync
Download

Free, from GitHub.

PortShelf is distributed as a direct download from GitHub Releases. It requires macOS 13.0 or newer.

First launch on an unsigned build

This build is not notarized with an Apple Developer ID yet, so macOS will warn you on first open. The source is public, and you can build it yourself.

  1. Unzip and move PortShelf.app to Applications.
  2. Right-click the app and choose Open.
  3. Click Open again in the dialog. That is it - once.

Or clear the quarantine flag in Terminal:

$xattr -d com.apple.quarantine /Applications/PortShelf.app