コンテンツへスキップ

アーキテクチャ

全体の流れ

デフォルト(ローカル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回の再試行を行う指数バックオフ付き自動再接続
  • 接続状態インジケーター