アーキテクチャ
全体の流れ
デフォルト(ローカルtmux + トンネル):
コード
Browser (phone)
|
| HTTPS
v
Cloudflare Quick Tunnel
|
| HTTP/WS
v
Local Termbridge server
|
| PTY
v
Tmux session
直接サンドボックスモード(Daytona、トンネルなし):
コード
Browser (phone)
|
| HTTPS
v
Daytona preview link
|
| HTTP/WS
v
Termbridge server (sandbox)
|
| PTY
v
Tmux session (sandbox)
--no-tunnelが設定されている場合、TermbridgeはCloudflareを経由せず、指定されたパブリックURLを使用します。
コンポーネント
CLIプロセス
- UIアセットを配信するHTTPサーバーを起動します。
- ターミナルI/O用のWebSocketサーバーを起動します。
- 1つ以上のtmuxセッション(またはDaytona PTY)を作成します。
- 1回限りの引き換えトークンを発行します。
- 有効な場合はトンネル(現在はCloudflare)を起動して監視します。
- 直接サンドボックスモードでは、サンドボックス内でTermbridgeを起動し、共有URLを待ちます。
ターミナルバックエンド
- tmuxバックエンド:
node-ptyを使ってtmuxに接続し、実際のキー入力と出力を扱います。 - Daytonaバックエンド: サンドボックス内のDaytona PTYを使用します。
- フルカラー対応のため
TERM=xterm-256colorとCOLORTERM=truecolorを設定します。 - tmuxでは、UIをすっきり保つため、tmuxのステータスバーを無効にします。
ターミナルレジストリ
- 利用可能なターミナルセッションを管理します。
- UIに一覧を提供し、選択されたターミナルIDを検証します。
認証とセッション
- 1回限りのトークンはURLとして出力され、QRコードに埋め込まれます。
GET /__tb/s/:tokenがトークンを引き換え、Cookieセッションを設定します。- WebSocket接続には、有効なセッションCookieとCSRFトークンが必要です。
- CSRFトークンは
GET /__tb/api/csrfで取得し、WebSocketのクエリパラメーターで渡します。
プロキシモード
--proxy <port>を渡した場合:
- すべてのtermbridgeルートは
/__tb/プレフィックスの下に配置されます /__tb/リクエストはターゲットアプリにプロキシされます- UIにより、Views切り替え機能からTerminal/Previewビューを表示できます
- Previewビューは
/を指すiframeを読み込みます - HMR用のWebSocket接続はターゲットに転送されます
セキュリティ
- CSRF対策: クロスサイトハイジャックを防ぐため、WebSocketへのアップグレードにはCSRFトークンが必要です。
- 入力制限: 64KBのHTTP本文、1MBのWebSocketメッセージ、メッセージごとに64KBのターミナル入力。
- レート制限: トークン引き換えとWebSocket接続に対し、IP単位で制限します。
APIエンドポイント
プロキシ対象アプリとの競合を避けるため、すべてのルートは/__tb/プレフィックスの下に配置されます:
GET /__tb/healthz- ヘルスチェックGET /__tb/s/:token- トークン引き換え → Cookie設定 → リダイレクトGET /__tb/api/terminals- ターミナル一覧の取得GET /__tb/api/csrf- WebSocket用CSRFトークンの取得GET /__tb/api/config- 設定の取得(proxyPort、devProxyUrl)POST /__tb/api/terminals- 新しいtmuxセッションの作成GET /__tb/app/*- SPAフォールバックWS /__tb/ws/terminal/:terminalId?csrf=<token>- ターミナルI/Oのストリーミング
プロキシモードが有効な場合:
GET /- プロキシ対象アプリにリダイレクト(無効の場合は/__tb/app)* /*- ターゲットアプリにプロキシ
WebSocketプロトコル
クライアント → サーバー:
JSON
{ "type": "input", "data": "ls -la" }
{ "type": "resize", "cols": 120, "rows": 32 }
{ "type": "control", "key": "ctrl_c" }
サーバー → クライアント:
JSON
{ "type": "output", "data": "..." }
{ "type": "status", "state": "connected" }
UIスタック
- Vite + TanStack Router
- xterm.js + Fitアドオン + WebGLレンダラー
- 固定入力バーを備えたモバイル優先のレイアウト
- 最大10回の再試行を行う指数バックオフ付き自動再接続
- 接続状態インジケーター