Repository navigation
Releases: mukes555/PortNanny
Release list
v2.4.2
What's new in v2.4.2
Notifications that know when to keep quiet, and a test harness that stops
writing to the store of whoever is using the Mac.
Fixed
- Notifications could arrive in a flood. A watched port that an agent
kept restarting produced a banner per change, each with its own sound and
its own entry in Notification Center. With the screen locked, macOS held
every one of them and delivered the lot at unlock. Now: one banner per
port per minute, at most three in half a minute before the rest become a
summary, one sound per burst rather than one each, and a new banner about
a port replaces the last one instead of stacking. - Nothing is posted while the screen is locked or the display is asleep.
The changes are counted instead, and one silent summary ("12 changes on
:3000, :5173 and 2 more") arrives when the screen comes back. The guard
still does its work while you are away; only the banners wait. - Refusal banners are rationed the same way: ten agents refused in a minute
is one story, not ten interruptions. portnanny version --jsonreportsdefaultsDomain, the preference domain
that build reads. The end-to-end suites use it to check they are isolated
before they run: they provoke refusals on purpose, and against the real
domain every one of them became a banner on the person's screen, plus
leases and History rows they had to clean up. Each run now gets its own
domain, and the guard suite stops flipping the real "refuse unclaimed
servers" setting.
📦 Install
Download PortNanny-2.4.2.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
- Ration the banners, and say nothing while nobody is looking by @mukes555 in #66
- Keep the test suites out of the real preference domain by @mukes555 in #67
- Release 2.4.2 by @mukes555 in #68
Full Changelog: v2.4.1...v2.4.2
v2.4.1
What's new in v2.4.1
The guard, findable. It was the app's own automation and the only way to
reach it on a row was to know to right-click.
Fixed
- The guard had no visible way in. A row offered Kill, Watch and (in
Advanced) Details; guarding a port meant knowing to right-click it, or
having something watched already, since the shield lived in the watched
section. Every row now carries the shield beside Kill, dimmed until the
mouse or the keyboard is on the row, and orange while the guard is on. - The orange shield meant "protected" on a row and "guarded" in the
Workbench. Protected is a lock now, and the shield means the guard
everywhere.
📦 Install
Download PortNanny-2.4.1.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
Full Changelog: v2.4.0...v2.4.1
v2.4.0
What's new in v2.4.0
PortNanny opens on who is working now: an Agents tab beside Simple and
Advanced, the AI tools on this Mac out of Settings and into it, and
portnanny agents for the same answer in a terminal.
Added
- An Agents tab, next to Simple and Advanced, and PortNanny opens on it.
What is listening is grouped by the session that started it: each agent
with its servers and how much memory they hold, the ports an agent has
reserved and not started on yet, the sessions that have ended (with one
button to clean up after them), and everything nobody claims. When another
agent has just been refused one of a session's ports, the section says so. - The AI tools on this Mac moved out of Settings and into the Agents tab,
under the sessions they explain, with how many of each are running.
Settings > Agents now holds settings only: the guard switch, project setup,
and lease length. Current leases show in the Agents tab, under the session
holding them. portnanny agentsanswers the same question in a terminal, and in
--jsonfor the agents themselves: every session here, the servers it is
running, the ports it has claimed, and which one is you. The rules file
PortNanny installs into projects now points agents at it.- The Workbench opens on Agents, and its session cards show claims too.
📦 Install
Download PortNanny-2.4.0.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
- An Agents view: PortNanny, organised by who is working by @mukes555 in #61
- Three tabs, Agents first, and Settings that only hold settings by @mukes555 in #62
- Release 2.4.0 by @mukes555 in #63
Full Changelog: v2.3.0...v2.4.0
v2.3.0
What's new in v2.3.0
A bug-hunting round: fuzzing, concurrent agents, corrupted stores, hostile
tools and the app's own lifecycle. Everything below was reproduced first.
Fixed
- Guarding a busy port killed the server on it. Watching or guarding a
port recorded its occupant in a different format from the one scans
write, so the next scan read the server already there as a newcomer: a
watch announced it, and a guard killed the very thing it was set to
protect. - One long argument froze every scan. The patterns that find a secret
in a command line backtracked over every way of splitting a run of word
characters: 800 characters took 2.6 seconds and a few kilobytes took
minutes, on every listener, on every scan. 500 KB now takes 0.15s. - Killing a port inside tmux killed the whole session. A tmux server
keeps the argv of the command that opened it, sotmux new -s build tsc --watchlooked like a reloader and the kill went to tmux and everything
under it: every pane, the editor, other servers. - A port held by another user read as free. The listener table cannot
see root's or_postgres's sockets, sofree 5432exited 0 and the
next server still could not bind. Kill, free, wait, reserve and exec now
ask the kernel before answering. - Kills reported more than they did. A partial kill exited 0 as
"killed"; a tree kill swallowed every child it could not stop (and its
pgrep fallback killed none at all); a process that had already exited
was recorded in History as killed;freechecked that the pid died
rather than that the port was free. - An agent installed through npm or pip looked like an ended session,
because it runs asnode …/bin/geminiorpython3 …/bin/aider. That
letkill --orphanedreap a live agent's server and allowed a rival
agent's kill that should have been refused. - Agent kills trimmed History to 50 entries however long you had set
it, and changing the length in Settings dropped everything an agent had
recorded since the window last read it. - One damaged entry hid every lease and every history row, and the
next write saved that empty list over the good ones. Entries are now
salvaged one at a time. - Four crashes: a non-finite JSON-RPC id, an oversized
CLAUDE_PID, a
closed stderr, and a stored lease naming an impossible pid. - The popover's shortcuts stayed live after it closed, so Return in
the Workbench started a kill from a list nobody could see, and Escape
stopped working everywhere. A confirmation opened from the popover fed
its own Return back and stacked a second dialog. - Cut, Copy, Paste, Select All, Undo and Close did nothing in every
PortNanny text field and window: a menu bar app still needs a main menu
for those keys to route. - The refresh timer stopped while any dialog or menu was open, blinding
the guard exactly then; the guard announced "Auto-killing" before
signalling anything and said nothing when a kill failed; refusal
banners never asked for notification permission, so macOS dropped them;
clicking a watch or guard banner did nothing;portnanny://showduring
launch was dropped, closing the welcome tour left nothing on screen, and
a second copy of PortNanny guarded every port twice. - Smaller ones:
free-port --rangealone was rejected, a lease reason
kept--token=…unredacted and quoted it back to other agents, a
process name with a carriage return could write its own row into
portnanny list, Docker containers publishing a port range went
unnamed, a test worker under a path with a space could not be killed,
portnanny://kill/9999/3000acted on :3000,agent-docsreplaced a
symlinked CLAUDE.md with a private copy, and a scan lost to a stalled
network mount stopped every later one.
Changed
- Exit codes: a partial kill is 4, a lease or history write that fails is
73, andexecanswers 126 and 127 like a shell. - MCP results say
isErrortruthfully: await_for_port_freethat timed
out is an error, an unknownlist_portsfilter is refused rather than
silently listing everything,kill_portvalidates its port and pid, and
list_portswith filtermineand no identity says so. - The shortcut recorder refuses ⌘W, ⌘Q and combinations macOS owns, which
it used to accept and then never fire. - Toasts and errors now appear in the Workbench and Settings; both were
drawn only by the popover.
📦 Install
Download PortNanny-2.3.0.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
- Bug hunt: crashes, the guard, hostile input, and what kills report by @mukes555 in #59
- Release 2.3.0 by @mukes555 in #60
Full Changelog: v2.2.1...v2.3.0
v2.2.1
What's new in v2.2.1
Two bugs that end-to-end testing found and the unit suite could not: a
kill that could never stop a Docker container, and a force kill that only
worked from the list. The README now describes the app that exists.
Fixed
portnanny killcould not stop a Docker container. It scanned
without asking for container names, so it never had one, and every
Docker-published port answered "a containerdocker pscan name" and
stopped nothing, whileportnanny whoison the same port printed the
exactdocker stopcommand it would not run. The MCPkill_porthad it
too. A kill now fetches the name when a target is a container it cannot
name yet, and pays thedocker psonly then.- Command-Return force killed a selected row but not a typed one. With
kill 3000in the search field it sent a plain SIGTERM, contradicting
the keyboard help and the code's own comment.
Changed
- The README was checked line by line against the code and rewritten
around what it found: eighteen claims were wrong or misleading, among
them Command-K killing "the current filter" (on All it kills dev
servers), "hide UDP sockets" (the switch shows them, and is on),
"the only network request" (the inspector's peek is a local one), and
a scan taking "about 20 ms with no subprocesses" (kernel calls in a few
milliseconds, with lsof and ps as fallbacks). It also documents the
right-click menu and that the Tests filter lists test processes rather
than ports, neither of which it mentioned before. Tests/e2e/holds the harness that caught the Docker bug: the guard and
the MCP server driven against real listeners. Manual, not in CI, since
it needs real processes and Docker.
📦 Install
Download PortNanny-2.2.1.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
- README says what the app does, and says it once by @mukes555 in #55
- portnanny kill can stop a Docker container again by @mukes555 in #56
- Keep the end-to-end harness that caught the Docker bug by @mukes555 in #57
- Release 2.2.1 by @mukes555 in #58
Full Changelog: v2.2.0...v2.2.1
v2.2.0
What's new in v2.2.0
A face for every state, and an audit behind it. The quokka was one image
wearing three names; she now has four, and a search that finds nothing
says so in her own voice. Behind that, six passes over the code found
thirty things worth fixing, including a guard that could miss a
supervisor's respawn and secrets reaching agents through a listener's
child processes.
Changed
- New artwork, and three moods that are actually different. The mascot
shipped as one file under three names, so "All quiet: nothing is
listening" and the guard badge both showed the waving quokka. There is
now a sleeping one for the empty state, a bust bold enough to read at
badge size for the guard, and a new one for a search that matches
nothing. The app icon, the README banner, and every screenshot are
redrawn to match. scripts/make_artwork.swiftkeeps an alpha channel the source already
has. It used to recompute one from whiteness whatever it was given,
which turned a transparent background opaque black and the white
pinafore see-through.- The banner is a drawn asset rather than one composed by a script, so
scripts/make_logo.swiftand the two generated logo files are gone. - The README opens with the problem it solves and carries a nav row;
docs/ARTWORK.mdholds the prompts that draw the character. - Destructive buttons ask first. Clear History, both Release buttons,
and Reset Defaults went straight through while every kill confirms. - Empty states tell the truth. A filtered list said "nothing is
listening" while the header counted every port; the Workbench table had
no empty or scanning state at all. - The detail sheets close properly: a real button with a name for
VoiceOver, on Escape, instead of a fake traffic light. Ten icon-only
buttons gained accessibility labels. - One vocabulary (Stop watching, Remove guard, Option-click), ":3000" in
notification titles, and "free" readable in light mode. - Fewer wakeups: a successful scan no longer republishes to every view,
the hidden scan runs at a background priority, and Settings stopped
asking launchd for its login-item status on every redraw.
Fixed
- The guard could miss a supervisor's respawn. Watched occupancy
remembered a process name, so a server restarted under the same name
looked like nothing had happened and the guard never fired. wait --timeout 0always said "still in use", because it never
looked at the port before checking the clock.- Attribution walked the wrong chain. With a session pid to follow,
the CLI stopped before reaching its own parents, sowhoaminamed the
wrong source and a lease taken by one agent could be refused to that
same agent. A caller whose agent has no session id of its own now
borrows its pid, the valueexecstamps on what it starts, so a
detached server and its own session match. - Claude Code installed through npm runs as node, matched no
signature, and its live sessions read as ended, which let anyone stop
its servers. Servers under a path containing a space (fnm's default)
were classified Other rather than Node.js, and pm2 under fnm was not
found. - A project name that arrived with the first full scan after a background
one was not treated as a change, so rows kept a missing project. A CPU
sample taken milliseconds after another showed a process at 50%. - A lease warning meant for people was collected and dropped.
- A rule file with one PortNanny marker and not its pair was appended
to, duplicating the block; the next run then deleted the person's own
text between the two markers.list --mine --orphanedquietly applied
one of the two.exec --rangewas dropped when it equalled the default.
Security
- Command lines of a listener's children went into
--jsonand MCP
output unredacted, so a worker started with--token=...leaked. The
redaction also missed digit-bearing names (S3_SECRET_KEY), JSON
secrets, and header secrets. - A lease reason is capped and stripped of control characters, and so are
process names and command lines: a process could name itself with escape
sequences that rewrite the terminal readingportnanny list. - A tree kill could signal a recycled pid. Each child was checked
against a name read at kill time, which compares the new process with
itself and can never fail; the walk now carries the name it saw. - Output carries the short session key rather than the full one, so an
agent cannot copy what it read and pass as another agent's session. - Every GitHub Action is pinned to a commit, the release token is scoped
to the job that publishes, and the Homebrew cask is pinned to the
checksum the build wrote rather than to a fresh download that could
hash an error page.
📦 Install
Download PortNanny-2.2.0.dmg below, open it, and drag PortNanny to
Applications. Universal binary: runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
- Full audit: 30 confirmed findings fixed, with tests by @mukes555 in #50
- Close the last three security findings from the audit by @mukes555 in #51
- Artwork prompts, and a README with a hook by @mukes555 in #52
- New quokka artwork, and moods that differ from each other by @mukes555 in #53
- Release 2.2.0 by @mukes555 in #54
Full Changelog: v2.1.1...v2.2.0
v2.1.1
What's new in v2.1.1
The polish after the rename: a bigger, whole quokka in the header and the
Workbench, README images that never came from anyone's real Mac, and a
cask that current Homebrew installs without a word.
Changed
- A bigger quokka. The avatar grows to 40 pt in the popover header and
36 pt in the Workbench sidebar, and the head crop now runs from the cap
to the chin with room on both sides, so the face is whole and centred
instead of ending at the mouth. The menu bar keeps its round glyph. - The Raycast extension scaffold under
extensions/raycastis gone (the
extension was dropped), and so is the unpinned pre-2.0 cask file; the
release workflow and the tap render the pinned template only.
Fixed
- README images are scripted. The popover screenshots, the palette
shot, the Workbench capture, and the menu bar strip were rendered from
the live scan of the Mac that built them, which put its project folders,
paths, and user name on the page. Every image now comes from the same
fabricated ports as the demo GIF (PORTNANNY_SNAPSHOT_DATA=demo), a
refresh in that mode is a no-op, and a test keeps the fixtures free of
anything from the rendering machine. The old images were also removed
from the repository's history.
Distribution
- The cask uses the stanzas current Homebrew asks for (
postflight_steps
anddepends_on macos: :ventura), sobrew upgradeno longer prints
deprecation warnings. docs/FIRST-RUN.md notes the one-time
brew trust mukes555/tapthat Homebrew may ask for after the rename.
📦 Install
Download PortNanny-2.1.1.dmg below, open it, and drag PortNanny to
Applications. Universal binary — runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
Full Changelog: v2.1.0...v2.1.1
v2.1.0
What's new in v2.1.0
PortKilla is now PortNanny. The old name said "killer" for an app that
spends most of its time keeping servers alive and attributed, and it sat
one letter from a much larger project. Nothing written for the old name
stops working.
Changed
- Renamed to PortNanny. New app name and bundle id
(com.mukes555.PortNanny),portnannycommand,PORTNANNY_OWNERand
PORTNANNY_SESSION,portnanny://links, repository
(github.com/mukes555/PortNanny; the old URL redirects), Homebrew cask
portnanny, and Claude Code pluginportnanny@portnanny. - The maid quokka. New artwork for the icon, the menu bar, the header,
the tour, and the banner: a quokka with a cap, a mug, and a laptop. - The README and the app's update banner say
brew upgraderather than
brew reinstall, since the cask has been pinned per release since 2.0.
Added
- The old name keeps working, for at least a year: the
portkilla
command (Homebrew links it next toportnanny),PORTKILLA_OWNERand
PORTKILLA_SESSION(read wherever the new names are;execexports
both), andportkilla://kill/3000. - Preferences move over. The first launch copies settings, watched
ports, guards, history, refusals, and leases from the PortKilla domain
into the new one, once, and never over a value the new app has already
written. The CLI does the same for the domain it shares with the app.
Distribution
brew upgrade --cask portnannyfollows the rename (the tap carries
cask_renames.json): PortKilla.app goes, PortNanny.app comes, and macOS
asks again for notifications and Launch at login because it keys both to
the bundle id. Every step is in docs/FIRST-RUN.md.
📦 Install
Download PortNanny-2.1.0.dmg below, open it, and drag PortNanny to
Applications. Universal binary — runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortNanny.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortNanny.app
skips the dialog. Homebrew (brew install --cask portnanny) does this for you.
What's Changed
Full Changelog: v2.0.0...v2.1.0
v2.0.0
What's new in v2.0.0
The quokka release: a new look, a Workbench window, port leases, a setup
wizard for AI tools, and a guard that knows whose server it is. Everything
since 1.16, reviewed twice.
Added
- Settings > Agents. The AI tools this Mac has and how each is
recognised, the guard's one switch ("Refuse agents a server nobody
claims", followed by the CLI and the MCP server too), a project setup
with a click per step (whatportkilla setupdoes: rule files, the MCP
registration for Claude Code), and leases: the default length
portkilla reserveuses and a Release button for each active lease. - Notifications per event. A watched port freeing up, something taking
one, a guard acting, and an agent being refused each have a switch under
the master. - What the list shows. Settings > Display can hide UDP sockets and
ephemeral ports (49152 and up). - Updates. The daily check is a switch, and "Include beta releases"
offers prereleases, which rank below the release they precede. portkilla setupwalks through setting up the AI tools on this Mac:
checksportkillaon PATH, and for each tool found offers to register
the MCP server with Claude Code (claude mcp add), write its rule file
into the project, and shows the rest (Cursor's mcp.json, Codex's
config.toml, shell completions). Asks before every change;--yes
applies all; without a terminal it prints the plan.- Rule files for every tool.
portkilla agent-docs --cursorwrites
.cursor/rules/portkilla.mdc(with frontmatter),--windsurfwrites
.windsurf/rules/portkilla.md,--codexwrites AGENTS.md,--claude
CLAUDE.md; folders are created, existing files keep their content. - A Claude Code plugin in
plugins/portkilla: the MCP server, the lsof
hook, aportkillaskill, and/portkilla:portsand/portkilla:free
commands. Install withclaude plugin marketplace add mukes555/PortKilla
thenclaude plugin install portkilla@portkilla. - Leases on free ports.
portkilla reserve <port> [--for 10m] [--reason ...]takes a lease thatfree-portandexecskip for
everyone else and that makeskillrefuse other agents on the port
(people are warned) until it expires (a day at most) orportkilla release <port>gives it back.portkilla reservationslists them; the
Workbench watchlist shows them with a Release button, and a listener on
a leased port wears a "reserved" chip. MCP:reserve_port,
release_port, and afree_porttool. - Drift. PortKilla reads the port a project meant to use (PORT in
.envfiles,--port/PORT=in package.json scripts,port:in
vite.config) and marks a web server that ended up elsewhere with an
"expected :3000" chip; the inspector andwhoissay who holds the port
it wanted.portkilla driftlists every such server. portkilla exec.portkilla exec --free-port --prefer 3000 -- npm run devpicks a free port nobody has leased, exportsPORT, leases the
port for the run, exportsPORTKILLA_OWNERandPORTKILLA_SESSION
(yours unless given) so the server is attributed even when the tool
leaves no marker, forwards signals, and exits with the command's status.
--port Ninsists on one port and explains who has it when it is busy.- The Workbench. A full-size window (overflow menu, or type
> workbench)
with a sidebar of views: a sortable table of every port; ports grouped by
project with Reveal, Open in editor, and Kill all; ports grouped by agent
session (live, ended, editor terminals, unclaimed) with Stop all and a
one-click clean-up of ended sessions; the watchlist with guard and watch
toggles and a field to add a port; and History. An inspector on the right
shows the selected port's overview, the evidence behind its owner (the
ancestry walked, the markers found), and its history, with Kill, Force,
Tree, Open, Watch, and Guard at hand. - A command palette in the search field. Type
kill 3000,open 5173,
watch 8080,guard 8080, orfree portand Return does it; the strip
under the field says what will happen, including who owns the target and
whether a supervisor is involved.>lists app commands (Refresh, Bulk
Kill, Pin, Workbench, History, Find a Free Port, Settings, Quit). - Rows slide in and fade out as ports come and go.
- Sparklines. The Workbench keeps the last sixty scans of CPU and
memory per process: a trend column in the table, and CPU and memory
charts in the inspector. - Who is connected. The inspector's Connections tab lists the remote
end of every established connection, marked local, from the local
network, or from elsewhere;portkilla whoisprints the same. - Peek at a web server. A Peek button in the inspector sends one GET to
the local server and shows its status, content type, and page title. A
Settings toggle (off by default) does it automatically for web ports. - A welcome tour on first launch: what the app shows, how agents are
kept from killing each other, and how to set the agents up (with the
commands to copy). Later from the menu or> tour. - The quokka. A new app icon, and the mascot in the welcome tour (waving
on the first page, on guard on the second) and in the empty state (asleep
on the tablet).scripts/make_artwork.swiftturns the artwork into the
icon and the cut-outs. - Supervisors understood. PortKilla recognises what would undo a plain
kill: pm2 apps, launchd jobs (Homebrew services and your own
LaunchAgents), Docker containers, and reloaders (nodemon,next dev,
uvicorn --reload,--watchmodes, Flask's and Django's reloaders,
gunicorn, puma, and nginx masters). Rows show an orange chip and the
detail view says why.portkilla killstops a reloader together with its
child and runspm2 stop,brew services stop,launchctl bootout, or
docker stopfor the rest; when the tool is not on PATH it prints the
command and exits 6.--forcekills the listener itself, except for
Docker, where it meansdocker kill. The app asks "Stop via pm2" or
"Kill anyway". Docker's backend is never killed through a port. - Refusals reach you. When the guard refuses an agent, the running app
shows a notification with "Stop it anyway" and "Show in PortKilla", the
History window lists the refusal in orange with who was refused, and
portkilla historyincludes it. portkilla whoisshows the supervisor and its stop command;list --json
carriesmanagedBy.portkilla whois <port>(or--pid): everything PortKilla knows about
a listener, including the evidence behind its owner (the ancestry walked,
the markers found, what was declared) and whatkillwould do for the
caller and why. Also an MCP tool,whois_port.portkilla kill --orphanedstops every server left behind by an
agent session that has ended, for any agent; exit 0 when there is
nothing to clean up.portkilla doctor --agentsprints the agent compatibility matrix
checked against this machine: which tools run or are installed, how each
is recognised, how precisely its sessions are told apart, and where each
fact came from. The same matrix is in docs/AGENTS.md, kept in step by a
test.PORTKILLA_SESSION: tools that export no session id (Codex, Gemini,
custom bots) can export any unique string next toPORTKILLA_OWNER, and
their servers stay tied to that session after reparenting.- A refusal now says when the server runs in the caller's working
directory: a hint that it may be the caller's own unclaimed server, or
the user's. portkilla schema whoisandschema agentsdocument the new outputs.- Connected clients. Each listener shows how many established
connections it has (a chip in the row, a Clients line in the detail
view,connectionsinlist --json), and killing a server with live
clients always confirms first. portkilla free-port [--prefer N] [--range A-B] [--json]prints the
first port that is neither listening nor bound (a real bind probe),
starting from the preferred one. Exit 1 when the range is full.portkilla schema <command>documents every field of that command's
--jsonoutput, so scripts and agents do not have to guess.- Agents no longer kill what nobody claims. An identified agent that
asks to stop a server PortKilla cannot attribute is refused (exit 3,
guardVerdict: "refused", reason "not attributed"); it should start its
own servers withPORTKILLA_OWNERset or ask the person. People at a
plain terminal and the GUI are unaffected;--forcestill overrides.
Commands from an editor terminal count as a person's, except against
another agent's running server, where they need--force. - Scenario tests spawn real servers (
portkilla __serve <port>, debug
builds only) and drive the guard through the CLI as a separate process:
attribution by declaration and by environment after reparenting,
refusals, overrides, real kills,free-port,whoami. portkilla mcpannounces itself on stderr when run from a terminal
(it used to wait in silence), andportkilla mcp --setup [claude|cursor| codex]prints the registration for each agent. There is no background
mode by design: each agent starts its own copy over stdin/stdout when it
needs one and stops it afterwards.
Changed
- The quokka is the brand. The menu bar shows the quokka traced in
black and white from the artwork (lighter while nothing is listening)
or, from Settings > Display, the app icon in colour; the bolt is gone.
The popover header carries the artwork's head with a live summary line,
About shows the app icon, and the Workbench sidebar and empty inspector
wear it too. - A bigger popover. Regular is now 580 by 720 points; Settings >
Display offers Compact (the old 500 by 600) and Large (660 by 840). The
pinned window follows. - **Richer rows....
v1.16.0
⚡ What's new in v1.16.0
The agent tools batch
Added
portkilla mcp: a Model Context Protocol server over stdin/stdout,
no dependencies. Tools:list_ports,kill_port(dry-run by default,
refusals come back as tool errors the model reads),whoami,
wait_for_port_free. The agent spawns and reaps it, so nothing is
resident or registered: the guard becomes a tool instead of a habit.
Register with{"mcpServers":{"portkilla":{"command":"portkilla","args":["mcp"]}}}
(Claude Code, Cursor) or[mcp_servers.portkilla]in Codex's config.portkilla agent-docs --write [--file CLAUDE.md]appends the agent
snippet between markers, once, and updates it in place on later runs.
Opt-in only; no other command writes into your repository.portkilla agent-docs --claude-hookprints a Claude Code PreToolUse
hook that turnskill -9 $(lsof -ti:PORT)into a nudge toward
portkilla free, at the point of the habit.
📦 Install
Download PortKilla-1.16.0.dmg below, open it, and drag PortKilla to
Applications. Universal binary — runs natively on Apple Silicon and Intel;
requires macOS 13 (Ventura) or newer.
Not notarized (no Apple Developer account). First launch on macOS 15 or
newer: open it once, then System Settings → Privacy & Security → Open
Anyway. On macOS 13 and 14: right-click PortKilla.app → Open → Open.
Either way,xattr -dr com.apple.quarantine /Applications/PortKilla.app
skips the dialog. Homebrew (brew install --cask portkilla) does this for you.
What's Changed
Full Changelog: v1.15.0...v1.16.0