Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Moniport

Moniport

A macOS menu bar app that shows which local ports are in use — and kills whatever is holding one.


Dev servers and AI coding agents leave listeners behind. Moniport puts them in the menu bar with the process, its pid and the project directory it was started from, so freeing port 3000 doesn't mean opening Activity Monitor or remembering the lsof incantation.

The Moniport panel listing active ports

Features

  • Sees what's listening. Every TCP port in a configurable range — 3000–9999 by default, or everything you own.
  • Names things usefully. node becomes vite, with the working directory it was launched from, so you can tell two dev servers apart.
  • Kills from the menu bar. Right-click for a graceful SIGTERM, or force a SIGKILL. Kill All only ever touches what's on screen.
  • Opens in a click. Click a row for http://localhost:<port>; copy the URL, pid or full command from the context menu.
  • Ignores the noise. Hide a port by number, or every port an app opens. Ignored ports leave the list and the count but still register as activity.
  • Filters. Search by port, process name or path.
  • Stays out of the way. No Dock icon, nothing in Cmd-Tab, follows the system appearance, and can launch at login.

The menu bar icon

State Icon
Ports listening, some not ignored Lit, with the count
Ports listening, all ignored Lit, no count
Nothing listening at all Dimmed

Install

Moniport is built from source. There's no download or Homebrew cask yet — see Why there's no binary.

Requirements: macOS 14 Sonoma or later, Xcode, and XcodeGen.

brew install xcodegen
git clone https://github.com/iAbadia/Moniport.git
cd Moniport
Scripts/build.sh --run

That builds the app, copies it to /Applications and launches it. The icon appears in the menu bar — there's no Dock icon, by design. Drop --run to just build into build/.

To have it start with your Mac, turn on Launch at login in Settings. macOS only registers login items for apps in a stable location, which is why --run installs to /Applications rather than running in place.

Why there's no binary yet

Distributing a Mac app outside the App Store means signing and notarizing it with a Developer ID certificate, which needs a paid Apple Developer account. Without notarization, macOS refuses to open a downloaded app, and Homebrew quarantines casks by default — so a download would be worse than no download.

Building it yourself sidesteps this entirely: an app you compiled was never quarantined, so Gatekeeper never challenges it. The build signs ad-hoc, with no developer account required.

A signed release and a Homebrew cask are planned. Scripts/release.sh and Casks/moniport.rb are already written and waiting on the certificate.

How it works

Moniport reads listening sockets straight from the kernel with libproc: proc_listpids to walk processes, proc_pidinfo(PROC_PIDLISTFDS) to walk their file descriptors, proc_pidfdinfo(PROC_PIDFDSOCKETINFO) to keep the TCP sockets in LISTEN. That's the same path lsof takes.

It does this rather than shelling out to lsof because the app polls for its whole lifetime, and spawning a subprocess every few seconds is exactly the sort of thing that shows up in battery usage. A full scan takes about 6 ms. Reading the kernel directly also avoids lsof's 9-character truncation of process names, which is the difference between Code Helper (Plugin) and Code\x20H.

Polling runs at 1 s while the panel is open and 5 s while it's closed; both are configurable.

Only your own processes are visible — reading another user's file descriptors needs elevated privileges, and a dev server you started runs as you.

Tools/portprobe is a CLI over the same code, for checking the scanner against lsof directly:

cd Tools/portprobe
swift run portprobe                # list
swift run portprobe --kill 3000    # terminate whatever holds a port

# should agree, modulo formatting
lsof -nP -a -u $UID -iTCP -sTCP:LISTEN +c 0

What it touches

It's a utility that terminates processes, so it's worth being precise about what it can reach:

  • No network access, no telemetry, no analytics. Nothing leaves the machine. The only URL it handles is the http://localhost:<port> it hands to your browser when you click a row.
  • It only sees your own processes. Reading another user's file descriptors needs elevated privileges it doesn't have and doesn't ask for.
  • The only thing it writes is your settings, to UserDefaults.
  • Killing sends SIGTERM first, escalating to SIGKILL only if the process ignores it. Before signalling, it re-checks that the pid still belongs to the same process it listed, so a process that exits between refresh and click can't cause an unrelated one to be killed.
  • It is not sandboxed — reading other processes' file descriptors and signalling them is incompatible with the App Store sandbox.

Development

project.yml is the source of truth — the .xcodeproj is generated and not checked in, so run Scripts/build.sh (which regenerates it) after adding files. Open Moniport.xcodeproj afterwards if you'd rather work in Xcode.

Path
Sources/MoniportKit Port scanning, process resolution, killing
Sources/Moniport SwiftUI menu bar app
Tools/portprobe CLI harness over MoniportKit
Design Icon artwork and the script that fits it to the icon grid
Scripts Build, icon and release scripts
Casks Homebrew cask, for when there's something to distribute
Script
Scripts/build.sh [--run] Build; --run installs to /Applications and launches
Scripts/make-icon.sh Regenerate the icon from Design/icon-source.png
Scripts/release.sh <version> Archive, sign, notarize, staple, package a DMG

MoniportKit is a separate module so Tools/portprobe can exercise exactly the shipping code from a terminal. Tools/portprobe/Sources/MoniportKit is a symlink to Sources/MoniportKit, not a copy.

Passing --open-settings opens the Settings window at launch, which is otherwise only reachable by clicking the menu bar panel.

Roadmap

  • A signed, notarized release and a Homebrew cask, once there's a Developer ID certificate to sign with
  • An App Store build is not possible: reading other processes' file descriptors and signalling them is incompatible with the sandbox

Prior art

Inspired by Port Monitor, a paid app with the same premise.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages