I got tired of looking up what was running on port 3000
occidere is a small command-line tool for finding out what is running on a local port.
I built it because I kept running into the same problem: a dev server would refuse to start because port 3000 was already in use, and I'd have to look up the right lsof command again. I know I've used that command enough times to remember it. Apparently not.
Now I can run this instead:
occidere 3000It shows the process, PID, project, working directory, address, and whether it is still running. There are commands for listing every listening port, opening one in the browser, and stopping the process too.
occidere list
occidere open 3000
occidere kill 3000That's most of the tool. I don't want it to grow much beyond that unless people use it and keep running into the same missing thing.
why I built it
The commands are different everywhere. macOS has lsof, Linux has ss, and Windows has netstat, tasklist, and taskkill. None of them are especially hard. They're just forgettable, and usually I need one of them because something else has already gone wrong.
The first version of occidere found the process on port 3000 and printed node.
Correct. Also barely more useful than the PID.
I had a few Node processes running and still couldn't tell which project owned the port, so I added project detection. occidere gets the process working directory, looks for the nearest package.json, and uses the package name when there is one.
The same lookup went from this:
Port 3000
Process node
PID 28812to this:
Project alishan.dev
Port 3000
Process node
PID 28812
Path /Users/you/projects/alishan-dev
Address *:3000
Status RunningThat was the point where the output started answering the question I actually had.
the cross-platform part
Most of the work is hidden under five fairly boring commands.
macOS uses lsof. Linux tries lsof and falls back to ss. Windows combines netstat with tasklist. The output from all of them is different, so each platform has its own parser and then everything gets turned into the same shape before it reaches the terminal.
Stopping processes needed its own handling. occidere kill asks for confirmation and tries a normal shutdown first. If the process is still alive after 1.5 seconds, --force takes over.
Windows was the annoying one here. taskkill can refuse a normal shutdown and return an error saying the process can only be terminated with /F. The forced path had to catch that error, try again with /F, and check that the process was actually gone.
The runtime has no npm dependencies. It runs on Bun and uses built-in Node modules plus the process tools that come with the operating system.
You can install it with npm:
npm install -g occidere
occidere listwhat happens next
I'm going to leave the feature list alone for a bit and see if anyone else finds the tool useful.
There are things I could add. Windows project detection is still limited. JSON output would make scripting easier. Shell completion and Docker awareness both make sense. I don't know yet whether any of those are the right next thing.
The useful feedback will be much less exciting: installation failed, this process was named badly, this port didn't show up, I expected this command to do something else. That's what I want to find out before turning a five-command utility into a pile of options.
For now it does the thing I built it for. Something is using a port, and I can find out what without searching for the command first.