Repository navigation
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