🔀

Portlessの仕組み

localhost:3000の代わりにmyapp.localhost — ポート番号を名前に置き換えるプロキシ

ローカル開発はポート地獄だ。localhost:3000localhost:3001localhost:5173... 何番が何だったか覚えられないし、AIエージェントに「3000番に接続して」と言っても次に変わったらまた教えなければならない。

Portlessはこの問題を根本的に解決する。portless myapp next devで実行すればhttp://myapp.localhostでアクセス可能。

コアアーキテクチャ:名前 → ポートマッピングプロキシ

ブラウザ                    Portless Proxy (443)           開発サーバー
myapp.localhost ──────→  routes.json参照 ──────→  localhost:4231
api.myapp.localhost ──→  サブドメインマッチ ──→  localhost:4582

実行フロー:portless myapp next dev

  1. プロキシデーモン確認~/.portless/proxy.pidを読み取り。なければ自動起動(デタッチされた子プロセス)。システムサービスではなく初回使用時に自動起動。

  2. ポート割り当て — 4000〜4999の範囲でランダムポート探索。net.createServer().listen()で実際にバインドテスト。

  3. フレームワーク検出 + ポート注入

  • Next.js、Express、Remix → PORT環境変数をネイティブに認識

  • Vite、Astro、Angular → PORTを見ないフレームワーク。--port--hostフラグをCLI引数に自動挿入。

  1. ルート登録routes.json{ hostname: 'myapp', port: 4231, pid: 12345 }を記録

  2. 子プロセス実行PORT=4231で実行。終了時にルート自動削除。

プロキシ内部

TCPソケットの最初の1バイトをpeekしてTLS/平文HTTPを1つのポート(443)で分岐。

ルーティング:ホスト名完全一致 → サブドメイン一致。x-portless-hopsヘッダーでループ検出(5回以上で508エラー)。

.localhostが機能する理由

.localhostはRFC 2606予約TLD。Chrome/Firefox/Edgeは*.localhost/etc/hostsなしで127.0.0.1に自動解決。Safariのみhostsファイル同期が必要(portlessが自動処理)。

自動HTTPS

  1. 自己署名CA(EC、10年有効期限)
  2. SNIコールバックでホスト名ごとの証明書を動的生成
  3. portless trustでシステムキーチェーンにCA登録
  4. NODE_EXTRA_CA_CERTSを子プロセスに注入

ファイルベースの状態管理

routes.jsonは毎リクエスト再読み込み(キャッシュなし)。ディレクトリ作成によるアトミックミューテックス。PID生存チェックでゾンビルート自動GC。

Worktreeサポート(v0.5.2+)

git worktreeのブランチを検出してホスト名の先頭に付加:feat-auth.myapp.localhost

動作フロー

1

CLIがプロキシデーモンの存在確認 → なければdetachedプロセスで自動起動(ポート443)

2

4000〜4999の範囲で空きポート割り当て + フレームワーク別ポート注入(PORT envまたは--portフラグ)

3

routes.jsonに{ hostname, port, pid }登録 → プロキシが毎リクエストこのファイルを読んでルーティング

4

TCP最初の1バイトpeek(0x16 = TLS)でHTTPS/H2 vs 平文HTTPを分岐 — 1ポートで両方処理

5

SNIコールバックでホスト名別証明書を動的生成 + キャッシュ。自己署名CAをシステム信頼ストアに登録

6

子プロセス終了時にroutes.jsonから自動削除 + PID生存チェックでゾンビルートGC

メリット

  • ポート番号を覚える必要なし — 名前ベースURL
  • HTTPS + HTTP/2自動 — 証明書生成・インストールまでワンステップ
  • フレームワーク不問 — Next.js、Vite、Express、Nuxtなど全対応
  • 常時起動ではない — 初回使用時自動起動、システムサービス登録不要
  • Worktree + サブドメイン対応でブランチ別独立アクセス

デメリット

  • ポート443使用時にmacOS/Linuxでsudo必要
  • Safariは*.localhostの自動解決未対応 → /etc/hosts同期が必要
  • routes.jsonを毎リクエスト読み込み — 超高頻度リクエスト時にI/O負荷の可能性
  • Dockerコンテナ内部では追加設定が必要(PID強制指定)
  • Node.js 20+必須、Windows未対応

ユースケース

モノレポで複数サービスを同時実行(frontend.localhost, api.localhost, admin.localhost) AIエージェントにポート番号ではなくURL名で開発サーバーへのアクセスを指示 HTTPS必須API(OAuthコールバックなど)のローカルテスト git worktreeごとに独立URL自動付与(feat-auth.myapp.localhost)