Harbor 1.0: Jeder Dev-Server auf deinem Mac, nur einen Tastendruck entfernt
Ich habe ein natives macOS-Menüleisten-Tool gebaut und ausgeliefert, das jeden Dev-Server zeigt, der auf localhost lauscht — mit Live-Vitals, Ein-Klick-Aktionen und einem echten, notarisierten Release. Hier ist, was es kann und was es gekostet hat, es zu shippen.
Jeder Entwickler kennt den Moment: Error: listen EADDRINUSE: address already in use :::3000.
Irgendetwas hockt auf dem Port. Vielleicht ist es der Vite-Server aus einem Projekt, das du vor zwei Tagen geschlossen hast. Vielleicht ist es ein Next.js-Prozess, der sein Terminal überlebt hat. Du fügst dieselbe Beschwörungsformel ein, die du immer einfügst — lsof -ti :3000 | xargs kill — und hoffst, dass sie das Richtige killt.
Harbor 1.0 existiert, damit ich das nie wieder tun muss.
Sechzig Sekunden Harbor — eine Motion-Demo, gebaut mit Remotion, die echtes Panel-Footage aus dem Release Candidate choreografiert.
Was Harbor ist
Harbor ist ein natives macOS-Menüleisten-Tool, das jeden Dev-Server zeigt, der auf localhost lauscht — und dich damit handeln lässt. Klick auf den Poller in deiner Menüleiste (oder drück ⌘⌥P von überall) und du bekommst ein Live-Panel: jeden lauschenden Port, welches Framework dahintersteckt, wie lange er schon läuft, was er dich an CPU und Speicher kostet, und ein Energie-Badge, wenn etwas leise deinen Akku verheizt.

Aus diesem Panel heraus ist alles nur eine Aktion entfernt:
- Kill einen Server — erst SIGTERM, Eskalation auf SIGKILL nur wenn nötig, mit Identitäts-Revalidierung, damit eine wiederverwendete PID nie einen unschuldigen Prozess mitreißt.
- Kill All — mit einer aufgezählten Bestätigung, die genau benennt, was beendet wird. Keine blinden Rundumschläge.
- Im Browser öffnen — Schema automatisch erkannt, IPv6-Loopback berücksichtigt.
- Zum Terminal springen — Harbor ermittelt, welches Terminal den Prozess besitzt, und aktiviert es. Funktioniert über Terminal, iTerm2, Warp, Ghostty, WezTerm, kitty, Alacritty und die Terminals von VS Code/Cursor hinweg.
Das Ganze ist keyboard-first: Pfeiltasten, Tippen zum Filtern, Return zum Handeln, Esc zum Schließen. Es sieht aus und verhält sich wie das System-Tool, das es ist — Light und Dark, VoiceOver, Dynamic Type, Reduce Motion.

Mehr sehen als eine Portnummer
Eine Portnummer allein beantwortet nicht die eigentliche Frage, nämlich: Was ist das und kann ich es killen?
Harbor erkennt das Framework hinter jedem Listener — Vite, Next.js, Rails, Flask, Express und mehr — und wenn sich ein Server hinter einem Child-Worker versteckt (Nexts Dev-Server macht das in manchen Setups, wie in den Screenshots oben), fällt es ehrlich auf das Runtime-Badge zurück, statt zu raten. System-Daemons geben sich auch nicht als Dev-Server aus: Ein Resolver für Well-Known Ports benennt Dinge wie AirPlay und Bildschirmfreigabe, sodass sich die "Alle anzeigen"-Ansicht wie ein Inventar liest statt wie ein Rätsel.
Jede Zeile klappt in einen Inline-Inspector auf: die vollständige Command Line, das Arbeitsverzeichnis, die besitzende App, den Bind Scope (lauscht das auf Loopback oder ist es zu deinem Netzwerk hin exponiert?) und rollende CPU-/Memory-Sparklines, gezeichnet als diskrete, an Null verankerte Balken — sodass ein Idle-Prozess auch idle aussieht statt wie ein Seismograph.

Exposure ist ein First-Class-Signal. Alles, was über Loopback hinaus gebunden ist, wird im Panel-Header markiert — dieses Exposed-Count-Badge ist der Unterschied zwischen einem Dev-Server und einem versehentlichen Netzwerkdienst, sichtbar gemacht, bevor du danach suchen gehst.
Für die Debugging-Sessions, in denen der Port belegt ist, aber nichts lauscht, bringt eine Opt-in-Ansicht UDP-Sockets und hängengebliebene Non-LISTEN-TCP-Zustände (CLOSE_WAIT, FIN_WAIT) in einem herabgestuften Bereich zum Vorschein. Standardmäßig aus, weil die Default-Ansicht sauber bleiben soll.
Local-First, nachweislich
Harbor telefoniert nie nach Hause. Es gibt keine Telemetrie, kein Konto, keine Netzwerk-Calls außer denen, die du anforderst (deinen Server im Browser öffnen, nach Updates suchen). Die App ist Developer-ID-signiert, hat eine Hardened Runtime, ist von Apple notarisiert und wird als universelles DMG mit veröffentlichter SHA256 ausgeliefert. Updates kommen über Sparkle mit EdDSA-signierten Appcasts.
Nichts davon ist Marketing-Sprech — es ist die Checkliste, die echte Arbeit gekostet hat, was mich zu dem Teil dieses Posts bringt, den ich eigentlich schreiben wollte.
Die Build-Story
Harbor ist natives Swift und SwiftUI — fünf SwiftPM-Module mit einem strikt azyklischen Dependency-Graph: reine Models ganz unten, Enumeration und Prozess-Intelligenz in einer Services-Schicht, Design System und UI darüber, ein Composition Root ganz oben. Protokolle leben im Core; Implementierungen leben in den Services; die UI hängt von Abstraktionen ab. Es ist die langweilige Architektur, und sie ist absichtlich langweilig: Jede Schicht ist unit-testbar, und die Test Suite läuft mit über 650 Tests.
Der Großteil der Implementierung war agent-assistiert — ein Team aus Coding-Agenten, das parallel in git worktrees arbeitete, wobei jede Änderung durch denselben Spießrutenlauf landete: voller Build, volle Test Suite, Lint, Format, ein adversarialer Review durch einen separaten Agenten und eine menschliche Merge-Entscheidung. Die interessante Lektion war nicht, dass Agenten Code schreiben. Sondern dass die Qualität im Review lebt.
Nimm ein Feature: die Opt-in-Ansicht für UDP- und nicht-lauschende Sockets. Der Implementierungs-Agent produzierte einen sauberen Branch — grüne Tests, sorgfältige Konfliktauflösung über vierzehn Commits an Drift hinweg, Byte-für-Byte-Kompatibilität, wenn der Toggle aus war. Es sah fertig aus. Dann fand der Review-Durchgang, ausgeführt von einem separaten Agenten ohne eigenes Interesse an der Arbeit, vier Dinge: Der Settings-Toggle war an einen Code-Pfad verdrahtet, den die ausgelieferte App nie aufrief, sodass das ganze Feature zur Laufzeit unerreichbar war; Kill All hatte still seinen Wirkungsradius erweitert, um die neuen untergeordneten Zeilen einzuschließen; der lsof-Fallback-Parser las verbundene Sockets falsch und hätte sie als netzwerkexponiert markiert; und ganz normaler TCP-Traffic hätte den adaptiven Refresh-Loop für immer auf Maximalfrequenz festgenagelt. Vier echte Bugs, einer davon destruktiv, in einem Branch, den jedes automatisierte Gate gesegnet hatte.
Dieses Muster wiederholte sich bis ganz nach unten. Der Autor ist nicht derjenige, der das Problem findet — der Skeptiker ist es. Die Arbeit so zu strukturieren, dass ein frisches Paar Augen jede Änderung angreift, bevor ein Mensch entscheidet, ist das Höchst-Leverage-Ding im gesamten Workflow, und es gilt für Agenten genauso, wie es schon immer für Menschen galt.
Zwei Bugs verdienen ihre eigenen Sätze, weil sie die Sorte sind, der man nur begegnet, wenn man wirklich shippt.
Erstens: Die Release-Pipeline signierte die App innerhalb des DMG, signierte aber nie den DMG-Container selbst. Jedes automatisierte Gate war grün bis zum ersten voll credentialten Durchlauf — echtes Zertifikat, echte Notarisierung — der auf Gatekeepers Prüfung der äußeren Signatur traf und scheiterte. Man kann es auch nicht nachträglich reparieren: Ein erneutes Signieren eines gestapelten DMG macht das Notarisierungs-Ticket ungültig. Die Pipeline wurde umgebaut, um vor der Submission zu signieren. Kein Dry Run hätte das gefunden; nur die echte Sache tat es.
Zweitens: Während der Release-Candidate-QA — ich, herumklickend wie ein User — fror das Panel bei 97 % CPU komplett ein. Der Übeltäter war ein SwiftUI-Layout-Livelock: Lazy Stacks, die sich immer wieder gegen das sichtbare Rect einer Scroll View innerhalb eines selbstdimensionierenden Popovers vermaßen, ein Feedback-Loop, der nie konvergierte, sobald sich der Inspector einer Zeile unter dem Cursor aufklappte. Der Fix war fast peinlich: Das Panel zeigt Dutzende Zeilen, also brachte die Laziness gar nichts. Plain Stacks, stabile Geometrie, Bug weg.
Shippen ist eine andere Disziplin als Bauen. Die letzten fünf Prozent — Notarisierung, Stapling, Gatekeeper, Update-Signierung, Checksums, Release-Evidence — sind der Ort, an dem Side Projects normalerweise einschlafen. Da durchzukommen ist der Großteil dessen, was "1.0" bedeutet.
Der Poller
Harbors Zeichen ist ein Anlegepoller mit einem Tau — das Ding, das ein Hafen tatsächlich benutzt, um Schiffe sicher an Ort und Stelle zu halten, so wie Harbor deine Dev-Server im Blick hält. Zwei Seeglas-Bögen deuten einen lebendigen, lauschenden Port an. Es ist bewusst kein Anker und bewusst kein Container.
![]()
Hol dir Harbor
Harbor 1.0 ist kostenlos, läuft auf macOS 14+ und ist universell (Apple Silicon + Intel).
- Download: Harbor.dmg aus dem neuesten GitHub-Release — SHA256-Checksum wird daneben veröffentlicht.
- Landing Page: ftchvs.github.io/harbor
- Changelog: vollständige Release Notes
Wenn gerade ein Port besetzt ist — und einer ist es, es ist immer einer — wird Harbor dir sagen, was es ist, und es mit einem Tastendruck beenden.