All projects Harbor · Write-up
Finding every dev server on the machineJeden Dev-Server auf dem Rechner finden
Harbor is a dashboard for every dev server running on your machine. It finds them without being told, shows each app live, and can start and stop them.Harbor ist ein Dashboard für alle Dev-Server, die auf dem eigenen Rechner laufen. Es findet sie, ohne dass man sie einträgt, zeigt jede App live und kann sie starten und stoppen.
Runs locally · private
- Type
- Local toolLokales Tool
- Stack
- React · Node · Express
- Year
- 2026
- Code
- PrivatePrivat
Which tab was localhost:3000?
With a few projects open, ports pile up: a Vite server here, a Next.js app there, an API on 7070, something on 8080 nobody remembers starting. Harbor answers that from one screen: every running dev server as a card, labelled with its framework, with a live preview of the app.
Discovery without registration
Nothing has to be registered. Every 2.5 seconds Harbor runs a short pipeline and streams the result to the dashboard over server-sent events. It starts from the one list the operating system already keeps:
lsof -iTCP -sTCP:LISTEN -P -n # every listening socket, and its process
- List listening socketsEvery TCP port in LISTEN state, with the process that owns it.
- Drop known noiseAirPlay, Spotify, Docker, Electron helpers and the like go first, before any work is spent on them.
- Ask over HTTPA real
GETwith a 700 ms timeout. Anything that doesn’t answer like HTTP is gone; databases and queues fall out here. - Check where it livesThe process’s working directory must be inside your home folder, not in an app bundle or
/Library: the strongest sign it’s one of your projects. - Name the frameworkFrom the dependencies in
package.json, with response headers and HTML as the fallback: Vite, Next.js, Astro, Rails, Django and more.
The order matters. The cheap filters run first, and the network probe only runs for what’s left.
Live previews
Each card holds a scaled, live iframe of the running app, and a click opens it full size. Some servers forbid being embedded, through X-Frame-Options or a CSP. Harbor detects that and shows a clean card with an “open in new tab” button instead of an empty frame.
Stopping means checking
A SIGTERM isn’t a guarantee: nodemon, ts-node-dev and some Python reloaders catch or ignore it. So a stop sends SIGTERM, checks every 150 ms for up to two seconds whether the process actually exited, escalates to SIGKILL if it didn’t, and checks again. Watch-mode tools can also respawn a fresh process on the same port right away; Harbor sees the new PID and says so, instead of looking like a stop that silently failed.
SIGTERM, then after SIGKILL.The confirm step went through one real bug. The first version turned the stop icon into a confirm state that was only visible while hovering and expired after 2.5 seconds, so anyone who read the tooltip before clicking again missed the window. It’s now an explicit Cancel/Confirm panel that stays visible, with an eight-second safety timeout.
An identity that survives a restart
Noisy entries, such as an HMR helper port, can be hidden. A hidden server is keyed by its project folder and port, not by its PID, because watch-mode tools hand the process a new PID on every reload. A hide keyed by PID would undo itself on the next file save.
Launching works from the same screen: pick a project folder, Harbor detects the package manager and suggests a run command from package.json, and the process starts detached, as if you’d run it in your own terminal. It shows up on the dashboard as soon as it binds a port.
Boundaries
Harbor only touches localhost, and runs on macOS and Linux because it relies on lsof and ps. It isn’t deployed anywhere: it is a tool for the machine it runs on.
Welcher Tab war localhost:3000?
Mit ein paar offenen Projekten sammeln sich die Ports: hier ein Vite-Server, dort eine Next.js-App, eine API auf 7070, irgendwas auf 8080, von dem niemand mehr weiß, wer es gestartet hat. Harbor beantwortet das auf einem Bildschirm: jeder laufende Dev-Server als Karte, mit seinem Framework beschriftet und mit einer Live-Vorschau der App.
Erkennung ohne Anmeldung
Eintragen muss man nichts. Alle 2,5 Sekunden läuft eine kurze Pipeline, und das Ergebnis geht per Server-Sent Events ans Dashboard. Sie beginnt mit der einen Liste, die das Betriebssystem ohnehin führt:
lsof -iTCP -sTCP:LISTEN -P -n # jeder lauschende Socket und sein Prozess
- Lauschende Sockets auflistenJeder TCP-Port im Zustand LISTEN, mit dem Prozess, dem er gehört.
- Bekanntes Rauschen verwerfenAirPlay, Spotify, Docker, Electron-Helfer und Ähnliches fliegen zuerst raus, bevor Arbeit in sie fließt.
- Per HTTP fragenEin echtes
GETmit 700 ms Timeout. Was nicht wie HTTP antwortet, ist weg; Datenbanken und Queues fallen hier heraus. - Prüfen, wo es liegtDas Arbeitsverzeichnis des Prozesses muss im Home-Ordner liegen, nicht in einem App-Bundle oder unter
/Library: das stärkste Zeichen, dass es eins deiner Projekte ist. - Das Framework benennenAnhand der Abhängigkeiten in
package.json, mit Response-Headern und HTML als Rückfall: Vite, Next.js, Astro, Rails, Django und mehr.
Die Reihenfolge zählt. Die billigen Filter laufen zuerst, und die Netzwerkprobe läuft nur für das, was übrig bleibt.
Live-Vorschauen
Jede Karte enthält ein verkleinertes, live laufendes iframe der App, ein Klick öffnet es in voller Größe. Manche Server verbieten das Einbetten, per X-Frame-Options oder CSP. Harbor erkennt das und zeigt statt eines leeren Rahmens eine saubere Karte mit „In neuem Tab öffnen“.
Stoppen heißt nachprüfen
Ein SIGTERM ist keine Garantie: nodemon, ts-node-dev und manche Python-Reloader fangen es ab oder ignorieren es. Deshalb sendet ein Stopp SIGTERM, prüft zwei Sekunden lang alle 150 ms, ob der Prozess wirklich beendet ist, eskaliert sonst zu SIGKILL und prüft erneut. Watch-Mode-Tools können außerdem sofort einen neuen Prozess auf demselben Port starten; Harbor sieht die neue PID und sagt das auch, statt wie ein Stopp auszusehen, der still gescheitert ist.
SIGTERM, dann nach SIGKILL.Der Bestätigungsschritt hatte einen echten Fehler. Die erste Version machte aus dem Stopp-Symbol einen Bestätigungszustand, der nur beim Hovern sichtbar war und nach 2,5 Sekunden ablief; wer vor dem zweiten Klick den Tooltip las, verpasste das Fenster. Jetzt ist es ein eigenes Panel mit Abbrechen und Bestätigen, das sichtbar bleibt, mit acht Sekunden Sicherheits-Timeout.
Eine Identität, die Neustarts übersteht
Störende Einträge, etwa ein HMR-Hilfsport, lassen sich ausblenden. Ein ausgeblendeter Server wird über Projektordner und Port erkannt, nicht über seine PID, denn Watch-Mode-Tools geben dem Prozess bei jedem Reload eine neue PID. Ein Ausblenden per PID würde sich beim nächsten Speichern einer Datei selbst aufheben.
Starten geht vom selben Bildschirm aus: einen Projektordner wählen, Harbor erkennt den Paketmanager und schlägt einen Startbefehl aus package.json vor, und der Prozess startet losgelöst, als hättest du ihn im eigenen Terminal gestartet. Auf dem Dashboard erscheint er, sobald er einen Port belegt.
Grenzen
Harbor fasst nur localhost an und läuft auf macOS und Linux, weil es auf lsof und ps angewiesen ist. Es ist nirgends deployt: Es ist ein Werkzeug für den Rechner, auf dem es läuft.