Harbor 1.0:Mac上のすべてのdev serverを、ワンキーストロークで
localhostでlistenしているすべてのdev serverを、リアルタイムのvitals、ワンクリックの操作つきで一覧できる、native macOSのmenu-barユーティリティを作ってshipしました。正式にnotarizeされたreleaseです。何ができるのか、そしてshipするまでに何が必要だったのかを書きます。
どの開発者も、あの瞬間を知っています。Error: listen EADDRINUSE: address already in use :::3000。
何かがportに居座っている。二日前に閉じたはずのプロジェクトのVite serverかもしれません。terminalを閉じても生き残ったNext.jsのprocessかもしれません。あなたはいつもと同じ呪文 — lsof -ti :3000 | xargs kill — を貼りつけ、正しいものが殺されることを祈ります。
Harbor 1.0は、私が二度とそれをしなくて済むように存在しています。
60秒で見るHarbor — Remotionで作ったモーションデモで、release candidateの実際のパネル映像を振り付けたものです。
Harborとは何か
Harborは、localhostでlistenしているすべてのdev serverを表示し、そのまま操作できるようにするnative macOSのmenu-barユーティリティです。menu barのbollardをクリックする(あるいはどこからでも⌘⌥Pを押す)と、ライブのパネルが開きます。listenしているすべてのport、どのframeworkなのか、どれだけの時間動いているのか、CPUとメモリをどれだけ消費しているのか、そして何かが静かにバッテリーを焼いているときのenergy badgeまで、ひと目でわかります。

そのパネルから、すべてがワンアクションで届きます。
- Kill:serverを終了させます — まずSIGTERM、必要なときだけSIGKILLへエスカレートし、identityの再検証をかけるので、再利用されたPIDが無実のprocessを巻き込むことはありません。
- Kill All:何を終了するのかを正確に列挙して確認するので、盲滅法な一掃はありません。
- Open in browser:schemeを自動判別し、IPv6のloopbackも処理します。
- Jump to terminal:Harborがそのprocessを所有しているterminalを特定してアクティブにします。Terminal、iTerm2、Warp、Ghostty、WezTerm、kitty、Alacritty、そしてVS Code/Cursorのterminalに対応します。
全体がキーボードファーストです。矢印キー、タイプしてフィルタ、Returnで実行、Escで閉じる。ライトとダーク、VoiceOver、Dynamic Type、reduced motionに対応し、まさにそれがそうであるべきシステムユーティリティのように見え、振る舞います。

Port番号以上のものを見る
port番号だけでは、本当の問いに答えられません。その問いとは、これは何で、殺してもいいのか? です。
Harborは、それぞれのlistenerの背後にあるframework — Vite、Next.js、Rails、Flask、Expressなど — を検出します。そしてserverが子workerの陰に隠れているとき(上のスクリーンショットのように、Next.jsのdev serverは一部の構成でこうなります)、推測で片づけるのではなく、正直にruntimeのbadgeへフォールバックします。system daemonがdev serverのふりをすることもありません。well-known-portのresolverがAirPlayやScreen Sharingのようなものに名前をつけるので、「Show all」ビューは謎ではなく在庫表のように読めます。
どの行もインラインのinspectorに展開できます。完全なcommand line、working directory、所有しているアプリ、bind scope(これはloopbackでlistenしているのか、それともネットワークに露出しているのか?)、そして離散的なゼロ基点のバーとして描かれるCPU/メモリのローリングsparkline — だからidleなprocessはidleに見えて、地震計のようには見えません。

露出はファーストクラスのシグナルです。loopbackの外にバインドされたものは、すべてパネルのヘッダーでフラグが立ちます — その露出カウントのbadgeこそ、dev serverと、うっかり公開してしまったネットワークサービスとの違いであり、あなたが探しに行く前に浮かび上がります。
portは取られているのに何もlistenしていない、そんなデバッグセッションのために、opt-inのビューがUDP socketと、詰まった非LISTENのTCP状態(CLOSE_WAIT、FIN_WAIT)を、控えめなセクションに浮かび上がらせます。デフォルトはオフです。デフォルトのビューは、きれいなままであるべきだからです。
ローカルファースト、しかも検証可能に
Harborは決して外部に通信しません。telemetryもaccountもなく、あなたが求めたネットワーク通信(serverをbrowserで開く、アップデートを確認する)以外は行いません。アプリはDeveloper IDで署名され、hardened runtimeで、Appleによってnotarizeされ、公開されたSHA256とともにuniversal DMGとして配布されます。アップデートはEdDSAで署名されたappcastを通じてSparkleで届きます。
これはどれもマーケティングの言葉ではありません — 実際に手を動かして初めて埋まるチェックリストです。そしてそこから、この投稿で本当に書きたかった部分に入ります。
ビルドの物語
HarborはnativeなSwiftとSwiftUIで書かれています — 厳密な非循環の依存グラフを持つ5つのSwiftPMモジュールです。最下層に純粋なmodel、servicesレイヤーにenumerationとprocess intelligence、その上にdesign systemとUI、最上部に一つのcomposition root。protocolはcoreに置き、implementationはservicesに置き、UIは抽象に依存します。退屈なアーキテクチャであり、それは意図した退屈です。どのレイヤーもunit-testableで、test suiteは650を超えるテストを走らせます。
実装の大半はagent-assistedでした — 並行するgit worktreeで働くcoding agentたちのチームで、どの変更も同じ関門を通ってlandします。フルビルド、フルtest suite、lint、format、別のagentによる敵対的レビュー、そして人間によるmerge判断。興味深い教訓は、agentがコードを書くということではありませんでした。品質が宿るのはレビューだということです。
一つの機能を考えてみます。UDPと非listeningなsocketのためのopt-inビューです。実装agentはきれいなブランチを仕上げました — グリーンのテスト、14コミット分のドリフトにわたる丁寧なconflict解消、トグルがオフのときのバイト単位の互換性。完成しているように見えました。そこへ、その仕事に利害を持たない別のagentが走らせたレビューパスが、四つのことを見つけたのです。settingsのトグルは、shipするアプリが決して呼ばないコードパスに配線されていて、機能全体がランタイムで到達不能でした。Kill Allは、その爆風半径を新しい従属行まで黙って広げていました。lsofのフォールバックパーサは接続済みsocketを読み違え、それらをネットワーク露出としてフラグしていたはずでした。そして通常のTCPのchurnが、adaptiveなrefreshループを永遠に最大頻度に張りつかせていたはずでした。四つの本物のバグ、うち一つは破壊的で、自動ゲートがすべて祝福したブランチの中にありました。
そのパターンは、下まで繰り返し現れました。問題を見つけるのは、書いた本人ではありません — 懐疑する者です。人間が判断する前に、まっさらな目がすべての変更に襲いかかるよう仕事を構成すること。それがワークフロー全体で最もleverageの大きい一手であり、それはずっと人間にとって真実だったのと、まったく同じだけagentにとっても真実です。
二つのバグは、それぞれに一文を捧げるに値します。本当にshipして初めて出会う類のものだからです。
一つ目。release pipelineはDMGの中のアプリには署名していましたが、DMGコンテナそのものには署名していませんでした。すべての自動ゲートがグリーンだったのに、最初の完全にcredentialされた実行 — 本物の証明書、本物のnotarization — がGatekeeperの外側署名チェックにぶつかって失敗しました。しかも後から直すこともできません。stapleされたDMGを再署名すると、notarizationのチケットが無効になるからです。pipelineは、submitの前に署名するよう作り直しました。どんなdry runもこれを見つけられなかった。本物だけが見つけました。
二つ目。release candidateのQA中 — つまり私がユーザーのようにあちこちクリックしていたとき — パネルがCPU 97%で固まりました。犯人はSwiftUIのレイアウトlivelockでした。lazy stackが、自己サイズ調整するpopoverの内側で、scroll viewのvisible rectに対して再測定を繰り返す。行のinspectorがカーソルの下で展開している間、一度も収束しないフィードバックループです。修正はほとんど気恥ずかしいものでした。パネルは数十行しか表示しないので、laziness は何も買っていなかった。plainなstack、安定したgeometry、バグは消えました。
shipすることは、buildすることとは別の規律です。最後の5パーセント — notarization、stapling、Gatekeeper、update署名、checksum、release evidence — こそ、サイドプロジェクトがいつも眠りにつく場所です。そこを通り抜けることが、「1.0」という言葉の意味のほとんどです。
Bollard
Harborのマークは、ロープのかかった係留bollardです — harborが船を安全につなぎとめるために実際に使うもの、Harborがあなたのdev serverを視界につなぎとめるのと同じように。二つのsea-glassのアークが、生きてlistenしているportを示唆します。これは意図的にanchorではなく、意図的にcontainerでもありません。
![]()
Harborを手に入れる
Harbor 1.0は無料で、macOS 14+で動き、universal(Apple silicon + Intel)です。
- ダウンロード:最新のGitHub releaseのHarbor.dmg — SHA256のchecksumを併記しています。
- ランディングページ:ftchvs.github.io/harbor
- Changelog:完全なリリースノート
いまportが居座っているなら — そして居座っているものは、いつだって必ずあります — Harborがそれが何かを教え、ワンキーストロークで終わらせます。