servers: what is listening, and who can reach it¶
The problem¶
Dev servers pile up: a vite --host from last week, a python -m http.server
started to pass one file across, a preview an agent left running. Some of them
serve their folder to the whole Wi-Fi or every device on the VPN, because the
command bound to 0.0.0.0 instead of 127.0.0.1.
lsof -iTCP -sTCP:LISTEN lists them but does not say what you need to know:
can another machine reach this, and what is it? The name column is cut to 9
characters, each port shows twice (IPv4 and IPv6), and nothing says which
project it belongs to.
The method¶
- Reach comes from the address, read with Python's
ipaddress:*,0.0.0.0,[::]reach every interface;127.0.0.0/8,::1and::ffff:127.0.0.1are local; any other address (192.168.1.5, a VPN address) is reachable on that network and counts as exposed. A plain "is it*?" test calls those local, which is exactly wrong. - One row per process; if any of its ports is reachable, the row is.
- Two batched lookups:
ps -o pid= -o etime= -o command= -p 1,2,3andlsof -a -p 1,2,3 -d cwd -Fpn. The folder a server started in usually tells you which project it is. - Careful stop: TERM, poll until the port is free, KILL only if the same pid still holds the same port, so a reused pid is never hit.
- Rebind recipes for
http.server, Vite, Next, Astro, uvicorn, Flask, Hugo, Jekyll, Rails,serveandphp -S: the old address flag and its value are dropped (--host 0.0.0.0,--host=…, a bare--host), the loopback one added.--runrestarts in the same folder and reports success only once the port listens on loopback alone.
servers # table, exposed first
servers --exposed # exit 1 if anything not kept is reachable
servers kill 5173 # TERM, wait, KILL
servers local 5173 # print the command bound to 127.0.0.1
servers local 5173 --run # and restart it
SERVERS_KEEP=postgres servers kill --exposed
Limits¶
- Needs
lsof. Without root it sees only your own processes;sudo serversshows system services too. - TCP only.
- "Reachable" is what the program asked for; a firewall may still block it.
local --runkeeps the arguments and folder, not environment variables.- Recipes match command text; a server behind a wrapper script may have none,
and then
localprints the original command for you to change.
Code¶
methods/servers/ in the methods repository: one Python file (standard library), a README and a 66-check test against real lsof and real loopback-only servers.