Remote Control blocked `/previews/*`, so a phone or browser paired through the tunnel had no web preview at all: the entry was hidden and the agent's dev server stayed out of reach. Previews now work there, behind a host setting that is on by default. A preview's sandboxed document sends neither the device cookie nor a usable `Origin`, so the Remote Control gate admits a live `/previews/<token>/…` capability on its token alone, as local listeners already do, while the host's Web previews switch is on. The ingress, which refused every WebSocket, now admits preview upgrades only: `admitRemoteUpgrade()` parses the target exactly as the preview proxy does and the manager hands the socket to the application's path-preview upgrade handler, never to the shared upgrade event (so `/previews/<token>/../../sidecars/…` cannot reach another handler). Every other upgrade still gets 404. The switch lives in Settings → Remote Access → Remote Control. Only the host can change it (`PUT /api/remote-control/previews`); the registry stores only the opt-out. Turning it off refuses later preview requests, including the Referer-based root-relative asset fallback, and closes live preview WebSockets; remote `/api/meta` then reports `previewsUnavailable`, which hides Open web preview on paired devices. Exposing previews through a public tunnel makes their address a bearer credential, so path-proxied preview responses now carry `Content-Security-Policy: sandbox` with the iframe's own flags (one shared constant, `previews/sandbox.ts`) and `Referrer-Policy: same-origin`, on every content type. A preview document opened outside its iframe stays an opaque origin instead of running with CodeNomad's origin and device cookie, and the capability path stays out of Referer headers to other sites. Such a standalone document then behaves like the preview iframe: its root-relative resources rely on the runtime bridge's path rewriting, without the Referer fallback. The per-token preview.localhost host keeps its own origin and headers. Help (Remote Control, mobile and client overview pages, all ten locales), dev-docs/REMOTE_CONTROL.md, architecture.md and UI_SHELL.md describe the behavior; the switch label and caption are translated in all locales. Validation: a Remote Control surface test drives the real application through the ingress: a preview page and an `Origin: null` form post work without a device cookie and carry the sandbox and referrer headers, unknown tokens and non-preview or dot-segment upgrades get 404, a preview WebSocket upgrades, a paired device cannot change the switch, and turning it off closes the live WebSocket, refuses the preview and its Referer asset fallback, and flags the metadata. Registry, metadata and proxy tests cover persistence of the opt-out, the flag and the headers per entry point; browser tests cover the switch and the conversation header with previews on and off.
11 KiB
リモートコントロール
リモートコントロールを使うと、受信ネットワークポートを開かずに、ペアリングしたブラウザーやデスクトップクライアントから、稼働中の CodeNomad バックエンドを利用できます。サーバーパスワード、ループバック以外の --host、VPN は不要です。直接接続とは別のアクセス経路です。
開始してペアリングする
- ホストで 設定 → リモートアクセス を開きます。
- リモートコントロールを開始 を選び、リモート接続の準備ができました を待ちます。初回はトンネルの識別情報と証明書の準備に時間がかかる場合があります。
- 別の端末を接続 で QR コードを読み取るか リンクをコピー し、その使い捨てリンクを目的の端末で開きます。
- ペアリングしたブラウザーのサイトデータを保持します。バックエンドの稼働中はトンネルアドレスを開き直すと再接続できます。
リンクの有効期間は 5 分、成功するのは 1 回 です。利用した人はペアリング済み端末になれるため、認証情報として扱ってください。コードはクエリ文字列ではなく URL フラグメントに入ります。新しいペアリングリンクを作成 は、直前の未使用リンクを即座に無効化します。停止、バックエンドの終了、トンネル障害は、未使用のペアリングリンクをキャンセルします。
別のデスクトップ CodeNomad をペアリングするには、そのホームページで CodeNomad サーバーに接続 を選び、リンクを サーバー URL に貼り付け、接続 を選びます。リンクを開くとコードが消費されるため、ペアリングリンクは 保存 からでもウィンドウを開きます。ブックマークにはトンネルのオリジンのみを保存し、コードは保持しません。ペアリングリンクでは TLS 検証を有効のまま維持します。
ペアリング済み端末を管理する
- ホストで 設定 → リモートアクセス → リモートコントロール に戻ります。
- ペアリング済み端末 と 最終アクセス の時刻を確認します。各端末には、
iPhone · Safariのように、ペアリングしたプラットフォームとブラウザーにちなんだ名前が付きます。 - 接続を許可しなくなった端末には 取り消す を選びます。
取り消しは、その端末の現在の HTTP/SSE 接続と将来のアクセスを終了します。復旧するには新しいリンクで再ペアリングします。端末は 30 日間未使用 で期限切れになります。上限の 32 台 に達すると、新しいペアリングは最も長く見られていない端末を置き換え、その接続を閉じます。
バックエンドは、生の端末認証情報ではなくハッシュを、設定ベースディレクトリの remote-control-devices.json に保存します。クライアントは、ホスト単位の Secure、HttpOnly、SameSite=Lax の端末 Cookie を保持します。ブラウザーでの有効期間は、サーバーの未使用 30 日の制限より長い場合がありますが、登録簿が正規の情報です。サイトデータの消去、ブラウザープロファイルの切り替え、プライベートブラウジングでは、再ペアリングが必要になる場合があります。
直接アクセスのパスワードを変えても、端末は失効 しません。リモートコントロールを停止 はトンネルを切断しますが、ペアリング済み端末一覧は保持します。許可を取り除く場合は 取り消す を使ってください。
現在の表示を失わず再ペアリングする
- クライアントに このデバイスを再ペアリング が表示されたら、そのページを開いたままにします。
- ホストで 新しいペアリングリンクを作成 を選び、同じクライアントのブラウザー/プロファイル で開きます。保存済みデスクトップサーバーでは、新しいペアリングリンクで接続し直して、既存のリモートウィンドウのストレージを再利用します。
- 待機しているページに戻り、接続を確認 を選びます。ページにフォーカスが戻ったときにも再確認されます。
復旧は開いているページと下書きを保持しますが、失敗した操作は再実行しません。再試行の前に、送信、回答、削除が実際に反映されたか確認してください。直接アクセスのパスワードでは、リモートコントロールのペアリングを復旧できません。
利用可能性と寿命
| 操作/状態 | 効果 |
|---|---|
| リモートコントロールを開始 | このバックエンドを接続し、今後の起動用に選択を記憶します。 |
| リモートブラウザーを閉じる | ホストのバックエンド停止や端末の失効は行いません。 |
| リモートコントロールを停止 | アクセスを切断し、起動時の自動再開を解除します。端末記録は保持します。 |
| バックエンドの終了/最後のデスクトップウィンドウを閉じてアプリを終了 | リモートコントロールはオフラインになります。OpenCode の共有サービスが稼働し続けても、停止したバックエンドを通してこの UI は提供できません。 |
| リモートコントロールを開始したままにした後の起動 | 新しいペアリングリンクを発行せず、バックグラウンドで再接続します。一度も開始していない環境で自動有効化することはありません。 |
| 再開失敗または致命的なトンネルエラー | 記憶した選択は残り、ホストにエラーが表示されます。リモートコントロールカードの更新アイコンで状態を読み直します。リモートコントロールを開始 で今すぐ再試行し、リモートコントロールを停止 で選択を解除します。どちらもしなければ次回起動で再試行します。 |
起動時の自動再開は、設定ディレクトリごとに 1 つのバックエンドに限られます。トンネル容量を増やすために、同じ設定で競合するホストを起動しないでください。デスクトップ終了後も到達可能にするには、別途管理するスタンドアロンサーバーを配備します。リモートコントロール自体は独立したデーモンではありません。
利用できる画面と利用できない画面
ペアリング済み端末は、同じ所有関係チェックの下で、通常のプロジェクト/会話 API、プロンプト送信、ファイル確認、権限/Form への回答を利用できます。ペアリングは強力なアクセスであり、読み取り専用の共有リンクではありません。トンネルがホスト内部で通信を転送しても、ループバック権限を与えることはありません。
| 対象 | リモートコントロール経由 |
|---|---|
| 通常の認証済み UI、ワークスペース、会話、SSE | ペアリング済み端末で利用可能。 |
| パスワードログイン/ログアウト、パスワード変更、ローカルトークン初期設定 | 利用不可。直接接続の認証が無効でも、端末認証情報が必要です。 |
| リモートコントロールの状態 | ペアリング済み端末で読み取り可能ですが管理は不可。開始/停止、ペアリング作成、端末一覧、取り消しはホストのアクセス経路に限ります。 |
| デスクトップのリモートウィンドウ/サーバー管理とプロキシルート | 利用不可(/api/remote-servers/、/api/remote-proxy/)。 |
| SideCar の コンテンツプロキシ | 利用不可(/sidecars/)。すべての SideCar API がブロックされるという意味 ではありません。ペアリング済みクライアントは /api/sidecars 登録 API にアクセスできます。 |
| Web プレビューの内容 | ホストの 設定 → リモートアクセス → リモートコントロール で Web プレビュー がオン(既定)の間は利用可能。各プレビューのアドレスはペアリングなしでそのページを開くため、他人に知られないようにしてください。スイッチをオフにすると、提供中のプレビューとその WebSocket が停止し、ペアリング済み端末では再読み込み後に Web プレビューを開く が表示されなくなります。 |
| 自動操作ブリッジ | 利用不可(/api/opencode-plugin/)。ネイティブデスクトップの能力がブラウザーの能力になるわけではありません。 |
| WebSocket アップグレード | Web プレビューでのみ利用可能。 |
ブロックされたプロキシ/WebSocket が必要な作業では、適切に保護された直接接続を使用します。リモートコントロールは自動的にその経路へ切り替わりません。
リレーとプライバシー
CodeNomad は OpenTunnel を使用し、マシンの共有既定トンネル識別情報上のランダムな 1 ルートを取得します。停止は CodeNomad のブリッジを解放し、他アプリが使用する共有識別情報は解放しません。
TLS は CodeNomad バックエンドで終端します。リレーは暗号文を転送するためアプリ内容を読めませんが、ホスト名、接続の時刻、通信量を観測できます。トンネルの証明書/秘密鍵はホストに残ります。ホストのトンネルストレージと CodeNomad 設定を認証情報として保護してください。推測困難なアドレスは認可ではありません。
CODENOMAD_REMOTE_CONTROL_API はセルフホストの OpenTunnel API を選び、未設定ならホスト型サービスを使用します。変わるのはトンネルの配置であり、ペアリング要件やブロック対象ではありません。認証情報と診断の扱いは、セキュリティを参照してください。