ブラウザ自動化と Computer Use
素の HTTP フェッチから仮想デスクトップまで:組み込みの取得ツール 2 つ、任意の外部ブラウザサーバー、そして隔離されたコンテナ内の Computer Use セッションです。セッションはスタッフが 8 つの
computer_*MCP ツールで駆動します。自動ルーターは存在しません。
経緯についての注記
Section titled “経緯についての注記”このページの以前の版は、L1 → L2 → L3 → L4 → L5 と自力でエスカレーションする5層の自動ルーターを説明していました。「L4 Sandbox Browser」という層も含めて。
そのルーター(browser_router.rs)は 2026-04 に書かれ、呼び出し元を一度も得られず、CHANGELOG にも一度も登場しないまま 2026-09 に削除されました。そこで説明されていた L4 という層は、そもそも独立した機能として存在したことがありません。
実際に出荷されているものはもっと単純です:エージェントが選ぶ組み込みの MCP 取得ツール 2 つ、ヘッドレスブラウジング用の任意の外部 MCP サーバー、そしてコンテナ内の Computer Use セッションです。コスト規律はルーティングエンジンではなく、エージェント自身の判断とツール説明から来ます。
実際に出荷されているもの
Section titled “実際に出荷されているもの”L1 — web_fetch_cached
Section titled “L1 — web_fetch_cached”SSRF 保護・ディスクキャッシュ・レート制限付きの素の HTTP GET。ステータス、content type、本文(6万文字で切り詰め)を返します。ttl_seconds でキャッシュを制御(既定 86400)。
SSRF ゲート(web_fetch::validate_url + resolve_public_addrs)は内部ホストとクラウドメタデータのエンドポイントを拒否し、リクエスト時点で DNS を再解決し、解決されたすべてのアドレスが公開アドレスであることを要求してから、そのリクエスト用にピン留めします。同じゲートが常駐センシングの http_poll / websocket ソースも守ります。
「公開アドレス」の定義はワークスペース全体で 1 つ、duduclaw_core::net_addr::is_public_ip です。web_fetch_cached、web_extract、メディアのダウンロード、常駐センシングのソース、relay URL のチェック、MCP サーバーのインポート、skills RPC、Odoo URL のチェック、wiki フェデレーションのピアチェック、Computer Use のピン留めがこれを使います。拒否される IPv4 ブロックは 0.0.0.0/8、10.0.0.0/8、100.64.0.0/10、127.0.0.0/8、169.254.0.0/16、172.16.0.0/12、192.0.0.0/24、192.0.2.0/24、192.168.0.0/16、198.18.0.0/15、198.51.100.0/24、203.0.113.0/24、224.0.0.0/4、240.0.0.0/4 です。IPv6 で公開とみなされるのはグローバルユニキャスト(2000::/3)のみで、Teredo 2001::/32 とドキュメント用ブロック 2001:db8::/32、3fff::/20 は除きます。IPv4 アドレスを埋め込む形式(IPv4-mapped ::ffff:0:0/96、NAT64 64:ff9b::/96、6to4 2002::/16)は埋め込まれたアドレスで判定されるため、http://[::ffff:127.0.0.1]/ は拒否されます。IPv4-compatible ::/96 とローカル用の NAT64 64:ff9b:1::/48 は、種別ごと拒否されます。この変更の前は、Web ツールのチェックが知っていたのは IPv4 のプライベート、loopback、link-local、0.0.0.0 の各範囲と ::1、fc00::/7 だけだったため、[::ffff:127.0.0.1] のようなアドレスがチェックを通り、loopback に到達できました。
用途:ドキュメントのある API、JSON エンドポイント、生のバイト列だけ必要なサーバーレンダリングページ。
L2 — web_extract
Section titled “L2 — web_extract”同じキャッシュ付き・SSRF 検証済みの経路で URL を取得し、CSS セレクタで要素を抽出します。出力形式は text(既定)、html、json(属性と子要素を含む構造化)。
用途:コンテンツが初期 HTML に入っている従来型のサーバーレンダリングサイト。
L1 も L2 も JavaScript を実行しません。SPA は空の外殻を返すだけです。
L3 — Playwright または Browserbase(外部 MCP サーバーとして)
Section titled “L3 — Playwright または Browserbase(外部 MCP サーバーとして)”JavaScript レンダリングのページを扱うには、そのエージェント自身の <agent_dir>/.mcp.json にブラウザ MCP サーバーを登録します。これはエージェント単位の MCP サーバーで、グローバル登録される DuDuClaw MCP サーバーとは別物です。
この項目を自動で書き込むコードはありません。crates/duduclaw-agent/src/mcp_template.rs には playwright_mcp_config、browserbase_mcp_config、ensure_playwright_in_config、ensure_browserbase_in_config がありますが、どこからも呼ばれていません。運用者が項目を手で書くか、Dashboard の MCP marketplace(marketplace.install、管理者のみ)からそのエージェントに playwright / browserbase をインストールします。手で書く場合、playwright_mcp_config(true) が生成する形は次のとおりです。
{ "mcpServers": { "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp", "--headless"], "env": {} } }}Browserbase については、browserbase_mcp_config と marketplace の browserbase カードが同じ項目を生成します。npx -y @browserbasehq/mcp を実行し、env には BROWSERBASE_API_KEY、BROWSERBASE_PROJECT_ID、GEMINI_API_KEY(最後のものはサーバーのデフォルトモデル用)が入ります。値はカードのインストール時に入力し(marketplace.install は env: { 変数名: 値 } で受け取り、値が欠けていればインストールを拒否して変数名を表示します)、claude mcp add -e と同じく文字列のまま AI 従業員の .mcp.json に書き込まれます。ファイルは所有者のみ読み書き可能(0600)で、mcp.list は各値を set/not_set/reference としてしか返しません。${NAME} 参照はここでは機能しません。CLI は自分の環境から展開しますが、gateway は *_API_KEY 形式の名前をすべて除いた許可リストの環境で AI 従業員の CLI を起動するためです。この変更の前はカードが参照を書き込んでおり、サーバーはキーなしで起動していました。v1.67.1 より前は、marketplace のカードと生成される Playwright 項目が npm に存在しないパッケージ名(@anthropic-ai/mcp-server-playwright、@anthropic-ai/mcp-server-browserbase などの @anthropic-ai/mcp-server-*)を指定し、生成される Browserbase 項目は非推奨の @browserbasehq/mcp-server-browserbase を使っていました。これらの名前ですでに書き込まれた項目は自動では書き換えられず、CLI が起動しようとすると失敗するため、手動で修正するか再インストールしてください。
DuDuClaw のバイナリには含まれず、L2 からここへのフォールバックもありません。そのエージェントの allowed_tools / denied_tools が呼び出せるかを決めます。
L5 — Computer Use
Section titled “L5 — Computer Use”Computer Use の経路は 1 つです:スタッフが 8 つの computer_* MCP ツールを呼び出し、各ステップを自分で決めます。スタッフに [capabilities] computer_use = true が必要です。DuDuClaw の MCP ツールを受け取る CLI を持つランタイムであれば、どれからでも動きます(マルチランタイムを参照)。Anthropic API キーは使いません。
| ツール | パラメータ | 動作 |
|---|---|---|
computer_session_start |
task(文字列、任意、監査ログに残る)、width(整数、320–1920)、height(整数、240–1200)。サイズの既定値は [capabilities.computer_use_config](1280x800)から来る |
スタッフのセッションを開始し、ディスプレイサイズ、上限、高リスクのアクションをチャットで確認できるか、computer_navigate が開けるサイトを報告する |
computer_screenshot |
なし | マスク済みの画面を MCP の画像ブロック(PNG)で返し、使用済みアクション数と残り時間のテキスト行を付ける。画像全体を隠す必要があった場合、テキストにその旨、理由、次にすべきことが書かれる。アクションはスクリーンショットを返さないため、結果の確認にはこれを呼ぶ |
computer_click |
x、y(整数、スクリーンショットのピクセル、0 始まり)、button(left が既定、または right)、double(真偽値、左ボタンのみ) |
クリック 1 回 |
computer_type |
text(文字列、1–2,000 文字) |
フォーカス中の要素に入力する |
computer_key |
key(文字列、英字・数字・+・-・_、最大 64 文字。例:Return、ctrl+s) |
キーまたは組み合わせを押す。無効なキーは拒否され、別のキーに置き換えられることはない |
computer_scroll |
x、y(整数)、direction(up または down、既定 down)、amount(整数 1–20、既定 3) |
そのピクセル位置にポインタを置いてスクロールする |
computer_navigate |
url(文字列) |
コンテナ内のブラウザでページを開く。ブラウザはアドレスバーのないキオスクモードで動くため、ページを開く手段はこれだけ |
computer_session_stop |
session_id(文字列、任意) |
セッションを終了し、そのコンテナを削除する |
呼び出しの経路:ツールは duduclaw mcp-server の中で動きます。これはエージェント CLI ごとに別プロセスで、コンテナは一切所有しません。各呼び出しは loopback 上の gateway への署名付き POST(/api/internal/computer-use、本文 {op: start | screenshot | action | stop | status, …})になり、すべてのチェックを行い、コンテナを所有するのは gateway です(crates/duduclaw-gateway/src/computer_use_sessions/)。そのため gateway が動いている必要があり、動いていない場合、ツールはその旨のエラーを返します。ツールがセッションを開始済み、アクションを実行済みと報告するのは、gateway がそう答えた場合だけです。この経路が呼び出し元をどう認証するかは SECURITY.md に書かれています。
セッションのルール:
- スタッフごとにセッションは 1 つ。 セッションが生きている間の 2 回目の
computer_session_startは拒否され、生きているセッションが示されます。そのスタッフだけがそれを見て駆動できます。 - gateway 全体で同時に 5 セッション。
- 上限。 セッションは
max_session_minutes(既定 10)の経過、操作が 2 分間ないこと(承認待ち、ダッシュボードでの視聴中、人による引き継ぎ中はアイドルに数えません。keep_alive_minutesを設定すると代わりにコンテナを一時停止します。アイドル保持・ライブ表示・引き継ぎを参照)、またはcomputer_session_stopで終了します。クリック、入力、キー、スクロール、ナビゲートの各操作はmax_actions(既定 50)に数えられ、スクリーンショットは数えられません。予算を使い切った後も、セッションはスクリーンショットと停止だけは行えます。 - 拒否される呼び出し元とモード。 エフェメラルエージェント(親のケイパビリティをコピーする Team-as-Agent のロールメンバーを含む)はセッションを開始できません。
[capabilities] computer_use_modeが"native"のスタッフは、エラーコードnative_unsupportedと、このモードは削除されたのでキーを削除するか"container"に設定するよう伝えるメッセージで拒否されます。コンテナへの暗黙のフォールバックはありません。"auto"またはキーなしは"container"と同じ動作です。 - 脅威レベル。 新しいセッションは
~/.duduclaw/threat_levelが GREEN(または存在しない)の間だけ開始されます。YELLOW ではスクリーンショットと停止のみ、RED ではすべてのセッションが終了します。緊急停止ワード(停止、stop、abort、やめてなど)そのものであるチャットメッセージは、すべての Computer Use セッションを終了させます。 - クリーンアップ。 リーパーが 15 秒ごとに、期限切れまたはアイドル上限を超えたセッションを確認します。コンテナには所有する DuDuClaw ホストを示すラベルと期限ラベルが付きます。gateway 起動時とその後 10 分ごとのスイープが、このホストのコンテナのうち、終了済みのもの、または期限を 10 分以上過ぎたものを削除し、Docker を一覧できない場合は何も削除しません。
- リクエスト上限。 スタッフあたり 1 分間に最大 120 リクエスト、リクエスト本文は最大 64 KiB。
gateway 自身が適用するゲート。この経路は MCP ディスパッチャを通らずに到達できるためです:
[capabilities] denied_tools/allowed_toolsとscoped_tools(リストにあるツールは有効なタスクスコープのグラントが必要)は、ツール自身の名前で、すべての操作についてチェックされます。approval_required_tools、irreversible_tools、maybe_irreversible_toolsに挙げられたツールは、ApprovalBroker を通じて人間の判断を待ちます(最大 300 秒。回答がなければ拒否として扱う)。maybe-irreversible のcomputer_*ツールは常に確認され、ここにモデルによる判定はありません。承認テキストにはツール名が書かれ、computer_typeでは文字数だけ、computer_navigateでは検証済みのホストだけ(パスもクエリもなし)が書かれます。computer_navigateの URL は何かを尋ねる前に検証されるため、拒否される URL が承認リクエストを作ることはありません。MCP 側の承認ゲートはこの 8 つのツールをスキップするので、二重に確認されることはありません。- すべてのアクションは、固定のルール(
risk_detector.rs)でリスク評価されます。フォーカス中のウィンドウのタイトルがblocked_actions(既定delete_file、terminal、system_preferences)に一致する場合、CONTRACT.tomlの[must_not] rulesの項目に一致する場合、またはフォーカス中のウィンドウのタイトルを読み取れない場合、アクションは拒否されます。高リスクのアクション(機密フィールドに向けた入力、認証情報に見えるテキストの入力、空でないallowed_appsの外にあるウィンドウ)は、ツール呼び出しがチャネルの会話に返信しているターンから来ている場合、会話のチャネルで人が 60 秒以内に確認する必要があります。gateway はそのチャネルを、リクエストからではなく、スタッフの実行中ターンについての自身の記録から見つけます。確認を求めるチャネルがない場合(スケジュール実行、委任されたタスク)、高リスクのアクションは拒否されます。auto_confirm_trusted = trueは確認を省略します。
チャットで起動するループはどうなったか
Section titled “チャットで起動するループはどうなったか”以前のバージョンには第 2 の経路もありました。チャネルメッセージが Computer Use の依頼に見える場合(キーワード一覧による判定)、gateway 自身が Anthropic Messages API の computer_20251124 ツールを呼び出し、返されたアクションを実行し、進捗とスクリーンショットをチャネルに投稿するものです。また computer_use_mode = "native" という変種は、ホストのデスクトップを駆動するものとされていました。どちらも削除されました。どちらも、公開されたどのビルドでもセッションを完了できませんでした。v1.66.1 では、コンテナが、何もビルドも公開もしていないイメージを使っており、その Dockerfile は起動できず(snap スタブのブラウザ、--network=none で中断する entrypoint、ウィンドウマネージャなし)、native モードは何かホスト側の操作を行う前に同じコンテナ起動を呼んでいました。
現在は、クリックやスクリーンショットに触れるチャネルメッセージも通常の返信経路に進み、ツールを呼ぶかどうかはスタッフが決めます。Computer Use は Anthropic API キーを読みません(direct-API の返信フォールバックは今も読みます)。
イメージの内容。 container/Dockerfile.computer-use は Debian(debian:trixie-slim)ベースの約 1.09 GB のイメージをビルドします。中身は Xvfb、ウィンドウマネージャ openbox、キオスクモードの Chromium(Debian の実際の chromium パッケージ)、ダッシュボードのライブ表示用にオンデマンドで起動する VNC サーバー(unix socket のみ)、操作とスクリーンショット用の xdotool と scrot、ドメインフィルタ、マスク用ヘルパー duduclaw-eval-dom、computer_navigate が使うヘルパー duduclaw-navigate です。ブラウザとウィンドウマネージャは非特権ユーザー sandbox で動きます。このイメージは 2026-10-01 まで一度も正常に動作していませんでした(Ubuntu のブラウザパッケージはコンテナ内で起動できない snap のスタブで、entrypoint は --network=none で中断し、ウィンドウマネージャもマスク用ヘルパーもありませんでした)。同日に修正され、検証されました。
公開はリリースワークフロー .github/workflows/computer-use-image.yml が担います。git タグ v* または手動実行で動き、ネイティブランナー上で linux/amd64 と linux/arm64 をそれぞれビルドし、push 前に gateway 自身のコンテナフラグで各ビルドをスモークテスト(ウィンドウマネージャの起動、スクリーンショット 1 枚の取得、DOM ヘルパーが [] を返すこと、duduclaw-navigate がネットワークなしできれいに失敗すること)してから、ghcr.io/zhixuli0406/duduclaw-computer-use:<tag> と :latest を公開します。このワークフローが初めて動くのは v1.66.1 の次のリリースタグなので、v1.66.1 以前には公開済みイメージがありません。scripts/release.sh verify はこのイメージを確認しません。
使われるイメージ。 既定は ghcr.io/zhixuli0406/duduclaw-computer-use:v<gateway のバージョン> です。上書きできるのはグローバルキー 1 つだけです:config.toml [computer_use] image = "<ref>"(digest 参照も使えます)。エージェントごとのイメージ設定はありません。[computer_use] セクションが無効な場合(未知のキー、使えない参照、解析できない config.toml)、Computer Use はメッセージ付きで利用不可になり、既定値には戻りません。イメージは自動で pull されません:docker run は --pull never を付け、セッション開始前に存在確認を行います。イメージがない場合、メッセージはイメージ名と 2 つの対処方法を示します:docker pull <image>、またはリポジトリのルートでローカルビルドして上書きキーをローカルタグに向けることです。
docker build -f container/Dockerfile.computer-use -t duduclaw-computer-use:latest .[computer_use]image = "duduclaw-computer-use:latest"アップグレード時の注意:ローカルビルドの duduclaw-computer-use:latest(旧既定値)しかないマシンでは、バージョン付きイメージを pull するか、上書きキーを設定する必要があります。
duduclaw doctor。 「電腦操作」の行は、使われるイメージ、それがローカルにあるか、[capabilities] computer_use = true のスタッフが誰かを表示します。誰も Computer Use を使っていなければ Pass、使っているスタッフがいて、イメージがない、Docker に接続できない、設定が無効のいずれかなら Warn です。いずれかのスタッフがまだ computer_use_mode = "native" のままの場合も、それだけで Warn になり、該当するスタッフを挙げます。また、スタッフごとに、許可リストのうち使えるホストの数と無視された項目の数も表示します。
スクリーンショットのマスク。 スクリーンショットがモデルや監査フォルダーに渡る前に、gateway はヘルパーに input[type=password]、.credit-card、[data-sensitive] 要素の画面上の矩形(固定の既定値で、設定はできません)を問い合わせ、黒く塗りつぶします。ヘルパーは Chromium の DevTools ポート経由で、表示中の唯一のページでこの問い合わせを評価します。このポートはコンテナ内の 127.0.0.1 だけで待ち受けます。ヘルパーの JavaScript はすべて分離ワールド(Page.createIsolatedWorld)で動くため、document.visibilityState、querySelectorAll、getBoundingClientRect を再定義するページでも矩形を変えられません。これはそのようなページで確認しました。Chromium は 0,0 から仮想ディスプレイ全体を覆い、デバイススケールファクタは 1 なので、ページ座標がそのままスクリーンショットのピクセルになります。矩形はデバイスピクセル比で換算し、外側に丸め、各辺を 1 px 広げ、画面内に切り詰めます。テストページでの実測では、覆われなかった機密ピクセルはなく、余分に覆うのは各辺最大 2 px で、ブラウザのズーム後も同様でした。ヘルパーが何らかの理由で失敗した場合(ブラウザが動いていない、表示中のページがないか複数ある、ウィンドウが移動したかディスプレイを覆っていない、visual viewport がズームされている、JavaScript エラー、5 秒の上限、gateway 側の 10 秒タイムアウト)、スクリーンショット全体がマスクされます。ヘルパーは、複数のページが表示されている場合は終了ステータス 3、それ以外の失敗では 1 で終了し、gateway はその理由をこのステータスから決めます。ヘルパーのテキストからは決めません。検出できない範囲:ページの一部ではないブラウザ UI(ズームの吹き出し、権限の確認、自動入力のドロップダウン、alert ダイアログ)、クロスオリジン iframe、shadow DOM 内の内容。スクリーンショット自体は /tmp/duduclaw-root/screen.png に書き込まれます。/tmp/duduclaw-root は root 所有でモード 0700 であり、ブラウザプロセスが起動する前に entrypoint が作成するため、ブラウザユーザーがファイルを事前に作成したり差し替えたりすることはできません。Chromium のログもここに保存されます。
DOM マスクの後、gateway はフォーカス中のウィンドウのタイトルを読み取ります。タイトルに認証情報を示す目印が含まれる場合、スクリーンショット全体がマスクされます。タイトルを読み取れない場合(コマンドエラー、タイムアウト、ゼロ以外の終了コード、UTF-8 でない出力)も、スクリーンショット全体がマスクされます。正常に読み取れた空のタイトルでは、スクリーンショットはそのままです。
全体がマスクされたスクリーンショットは、その旨を伝えます。ツールセッションでは、gateway の応答に fully_masked(true/false)と mask_reason(several_pages、title_sensitive、title_unreadable、helper_failed、injection_suspected、text_unscanned、またはなし。最後の 2 つはインジェクションの疑いによる一時停止を参照)が含まれます。ヘルパーが失敗した場合、タイトルは参照されません。ツールのテキストは AI に、安全のため画像全体が隠されたことと、すべきことを伝えます:複数のウィンドウの場合は computer_navigate をもう一度呼ぶ(余分なウィンドウが閉じられます)。機密性のあるウィンドウ、または読み取れないウィンドウが前面にある場合は、そのウィンドウが前面にある間は画面を表示できません。それ以外の場合は、もう一度スクリーンショットを撮り、繰り返し起きるならセッションを停止して新しく開始します。一部だけマスクされたスクリーンショットとマスクされていないスクリーンショットのテキストは、従来どおりです。
ネットワークとサイト許可リスト。 セッションは、スタッフが agent.toml にサイト許可リストを持っていない限り、ネットワークを持ちません:
[capabilities.computer_use_config]allowed_domains = ["example.com", "docs.example.com"]- 項目は完全一致のホスト名で、前後の空白を除き小文字にしてから比較されます。ワイルドカード(
*.example.com)、IP アドレス、0x7f000001のような数値形式、ポート、パス、URL、user@hostは無視され、最初の 20 個の異なるホストより後の項目も無視されます。サブドメインは親ドメインでは許可されません:example.comはdocs.example.comを許可しません。無視された項目はログに記録され、computer_session_startの結果に件数が示され、duduclaw doctorでも報告されます。配列でない値は空のリストとして読まれます。 - 使える項目がない場合、コンテナは
--network=noneで動き、computer_navigateは、運用者がどの設定を追加すべきかを伝えるメッセージとともに拒否されます。 - セッション開始時に、gateway が各ホストを自分で解決します(ホストごとに 5 秒の上限)。ホストがそのセッションでスキップされるのは、解決できない場合、答えの中に公開アドレスでないものが 1 つでもある場合、または答えに IPv4 アドレスがない場合です。開始結果には、到達できるホストとスキップされたホストが、アドレスなしで列挙されます。
- 解決できたホストについては、コンテナは Docker の既定ブリッジに明示的に接続され(
--network bridge)、各ホストに--add-host <host>:<address>とNET_ADMINが付きます。これは entrypoint が権限を落とす前に送信フィルタを設定できるようにするためです。フィルタ(container/scripts/domain-filter.sh、ピン留めアドレスモード)は、既定ですべての送信トラフィックを破棄し、ピン留めされたアドレスへの TCP 443 だけを許可し、DNS を拒否するため、名前はピン留めされた項目を通してのみ解決されます。loopback は127.0.0.1と::1に限られ、Docker の組み込みリゾルバ127.0.0.11は拒否されます(ユーザー定義の Docker ネットワークでは、これがあらゆる名前に答えてしまい、DNS クエリ経由でデータを外に送れるため)。ブロックされた接続は、タイムアウトを待たされることなく即座に拒否されます:フィルタは自身の TCP リセットと ICMP unreachable の応答を loopback で通すため、許可リスト外のアドレスの 443 番ポート、またはピン留めされたアドレスの 80 番ポートは即座に拒否され、許可リスト外の名前は即座に解決に失敗します。 computer_navigateが受け付けるのは、ユーザー名もパスワードも含まないhttps://の URL で、ポートがないか 443 であり、ホストがこのセッションの到達可能なホストのどれかと完全に一致し(IP アドレスは拒否)、2,000 バイト以下で、空白や制御文字を含まないものだけです。URL は承認を求める前にチェックされます。- ページ上のリンクのうち許可リスト外のサイトへ向かうものは読み込まれず、ブラウザ自身のエラーページが表示されます。別のドメインにリダイレクトするサイトも同様に失敗します。
実際のセッションで確認した内容:https://example.com/ を開いてスクリーンショットで見られること。リストにないホスト、紛らわしいホスト(example.com.evil.test)、http://、443 以外のポート、ユーザー名付きの URL、file:// の拒否。コンテナ内では、許可リストの名前がピン留めされたアドレスに解決され、他の名前は解決されず、他のアドレスとポートは拒否されたこと。許可リスト外のサイトへのリンクをクリックするとブラウザの DNS エラーページが表示されたこと。停止時にコンテナが削除されたこと。
許可リストが守らないもの:
- 許可リストにあるサイトは、AI がそこで入力または送信するものをそのまま受け取ります。許可リストが決めるのはデータの送り先であり、何が送られるかではありません。
- 自分のドメインを経由してプロキシまたはリダイレクトするサイト(翻訳プロキシ、同じホスト上の URL 短縮サービス、検索エンジンのキャッシュ)は、他所の内容を中継したり他所へ中継したりできます。
- アドレスはセッション開始時にピン留めされます。セッション中にアドレスが変わるサイトは、新しいセッションを開始するまで動かなくなります。
- 許可リストはスタッフ単位です。
ブラウザポリシー。 Chromium はマネージドポリシー(container/scripts/chromium-policy.json、/etc/chromium/policies/managed/duduclaw.json としてインストール)付きで動きます。各項目は、現行イメージの Chromium の chrome://policy で受理されていることを確認しました。ページはローカルネットワークや loopback へのアクセスを要求できず、権限の確認も表示されません(ページから 127.0.0.1:9222 への fetch や WebSocket は即座に失敗します)。名前解決は /etc/hosts だけを通ります(組み込み DNS クライアントと DNS-over-HTTPS は無効)。シークレット、ゲスト、ユーザー追加のウィンドウは無効、ファイルダイアログと印刷は無効、ダウンロードとポップアップはブロックされ、通知、位置情報、USB、シリアル、HID、ファイルシステム、direct-sockets、ウィンドウ管理の各権限はブロックされ、音声・動画・画面のキャプチャは無効、パスワードマネージャと自動入力は無効、ブックマークバーとブックマーク編集は無効、新しいタブページは about:blank です。URL ブロックリストは file://、chrome://、chrome-untrusted://、devtools://、view-source:、javascript:// を対象にします。DeveloperToolsAvailability は意図的に設定していません。設定すると、マスク用とナビゲーション用のヘルパーが使う loopback の DevTools プロトコルまで無効になるためで、DevTools のフロントエンドは代わりに devtools:// の項目でブロックしています。
既知の制限:ctrl+t と ctrl+n を止めるポリシーはなく、ctrl+n はアドレスバー付きの通常のウィンドウを開きます(ネットワーク到達範囲は依然としてピン留めされたホストだけです)。複数のページが表示されている間は、スクリーンショットは mask_reason several_pages で全体がマスクされ、ツールのテキストは AI に computer_navigate をもう一度呼ぶよう伝えます。次の computer_navigate が余分なページを閉じ、1 つだけ残します。
その他の送信モード。 オーケストレータには、許可ドメインのリストをコンテナ内で解決する古い送信モード(ALLOWED_DOMAINS。NET_ADMIN はそのときだけ追加)が今もありますが、gateway にこれを有効にするものはありません。どのモードでも、コンテナに loopback 以外の経路がある状態でフィルタがデフォルト拒否ルールを設定できない場合、コンテナはフィルタなしの送信で動くのではなく、起動を拒否します。
用途:人がコンピューターの前でできること全般——ログイン、ドラッグ&ドロップ、視覚的なパターン認識。同時に、圧倒的に最も遅く最も高コストな選択肢です。
未実装、および未検証の項目。
- ドラッグ、マウス移動、ホバーのツールはなく、ファイルのアップロード/ダウンロードのツールもありません。
- Computer Use はホストのデスクトップを決して駆動しません。native モードは削除されました。
- 実際のモデル(Claude Sonnet 5.5、実際の MCP サーバー経由)での確認済み:セッションを開始し、許可リストのページを開き、スクリーンショットからページのテキストを読み取り、スクリーンショットから得た座標でリンクをクリックし、許可リスト外のエラーページを認識し、セッションを停止しました。未検証:実際のチャネルに応答している、gateway が起動したエージェントのターンの中から開始されたセッション。
- チャットでの高リスクの確認は、実際のチャネルでは試していません。WebChat では確認のプロンプトが本人に届かない可能性があります(未検証)。
- Computer Use のキーワードを含むチャネルメッセージが通常の返信経路に進むことは、実際のチャネルでは試していません。
- イメージのワークフローは、今回の変更を含めた GitHub Actions の実行をまだ経ていません。
- 動作確認したのは macOS(arm64)の Docker Desktop だけで、amd64 ホストも、ネイティブ Linux の Docker Engine も試していません。Windows(WSL2)ホストは未検証です。
- 送信フィルタの IPv6 ルールは設定されていますが、トラフィックを流しての確認はしていません(既定のブリッジには IPv6 の経路がありません)。
- ダウンロードと印刷のブロックは、ポリシーが受理されていることと、ショートカットキーで何も開かないことで確認したもので、実際にダウンロードを起こして確認したものではありません。
セキュリティ:デフォルト拒否
Section titled “セキュリティ:デフォルト拒否”L2 より上のすべての層は agent.toml での明示的な承認を必要とします:
[capabilities]computer_use = false # Computer Use セッションと 8 つの computer_* ツールbrowser_via_bash = false # Bash ツール呼び出しからのブラウザ起動allowed_tools = [...] # 許可リストdenied_tools = [...] # 拒否リスト
[capabilities.computer_use_config]allowed_domains = [] # computer_navigate が開けるサイト。空 = ネットワークなしcomputer_use = false(既定)では、gateway はそのスタッフの Computer Use セッションを開始せず、8 つのcomputer_*ツールはtools/listから隠され、直接の呼び出しは拒否されます。判定は fail-closed で、ファイル不在・TOML 破損・キーの型違いはいずれも拒否です。gateway はセッション中もこの設定を読み直し、オフにするとセッションは次の操作で終了します。denied_toolsは CLI に--disallowedToolsとして渡されると同時に MCP ディスパッチャのフロントドアでも強制されます。browser_via_bashはもう環境フラグを設定しません。DUDUCLAW_BROWSER_VIA_BASHを読んでいたbash-gate.shの許可リストは、他のシェルフックとともにba015a48で削除されました。ケイパビリティ自体は有効で、disallowed_tools()とCapabilitiesConfig::sandbox_level()に供給されます。後者は codex / gemini ランタイムがReadOnlyとWorkspaceWriteのどちらのサンドボックスにするかを決める根拠です。- 死んだルーターのフィールドとしてのみ存在した制限ノブ(信頼/ブロックドメイン、セッションあたりのページ上限)は、その形では実装されませんでした。代わりに存在するのは、スタッフ単位のサイト許可リスト(上の
allowed_domains)、セッションのアクション予算、チャットでの高リスク確認、そしてApprovalBrokerとagent.toml [capabilities] approval_required_tools/irreversible_tools/maybe_irreversible_toolsによる承認ゲートで、これは他のツールと同様にcomputer_*ツールにも適用されます。スクリーンショット監査は切り替えではありません:常に書き込まれます(下の監査ログの項を参照)。
おおまかなコスト比較
Section titled “おおまかなコスト比較”| 層 | 起動 | メモリ | JS 実行 | コンテナ要否 |
|---|---|---|---|---|
L1 web_fetch_cached |
~0 ms | ~1 MB | いいえ | 不要 |
L2 web_extract |
~0 ms | ~5 MB | いいえ | 不要 |
| L3 Playwright / Browserbase MCP | 数秒 | 数百 MB | はい | 不要(外部プロセスまたはクラウド) |
| L5 Computer Use | ~10 s | 500 MB+ | はい | 必要 |
L1 で答えが出る問いに L5 を持ち出すのが最も高くつく間違いで、それを避けるのはエージェントの責任です。プラットフォーム側に止める仕組みはありません。
他システムとの連携
Section titled “他システムとの連携”- コンテナ分離 — L5 は専用の Docker コンテナ(既定イメージ
ghcr.io/zhixuli0406/duduclaw-computer-use:v<gateway のバージョン>、自動 pull なし)で動きます。読み取り専用ルートファイルシステム、256 MB の tmpfs/tmp、CPU 1、メモリ 512 MB、プロセス 512(Chromium のスレッドもこの上限に数えられます)。セッションが許可リストのホストを解決できた場合を除き--network=noneで、その場合は--network bridge、ピン留めされたホスト、ドメインフィルタ用のNET_ADMINが追加されます(上のネットワークの段落を参照)。すべての Computer Use コンテナは--security-opt no-new-privileges付きで動きます。このコンテナはcomputer_use_orchestratorが起動するもので、エージェント単位のタスクサンドボックスとは別物です(タスクサンドボックスガイドを参照)。 - セキュリティ防御 — ケイパビリティの強制と監査証跡は 05-security-defense.md を参照。
- 常駐センシング —
http_poll/websocketのティックソースは L1 と同じ SSRF ゲートを共有します。41-resident-sensing.md を参照。 - 監査ログ — 8 つの
computer_*ツールはすべてtool_calls.jsonlに記録され、入力は縮約されます:computer_typeは文字数のみ、computer_navigateはホストとパスの長さ(クエリ文字列なし)、computer_screenshotは画像なしです。web_fetch_cachedとweb_extractは読み取り専用のため記録されません。セッションは、ハッシュチェーン付きの行(tierL5a)を~/.duduclaw/audit/browser/audit.jsonlにも追記し(セッションの開始と終了、fully_maskedとmask_reasonを持つ各スクリーンショット、リスク評価付きの各アクション、ホストとパスを持つ各ナビゲーション(クエリなし)、各拒否。入力したテキストはここでも文字数のみ)、マスク済みのスクリーンショットを~/.duduclaw/audit/browser/screenshots/<agent_id>/に保存します。スクリーンショットはスタッフごとに 7 日間、最大 500 ファイルまたは 200 MiB まで保持され、古いものから削除されます。audit.jsonlは 16 MiB を超えるとaudit.jsonl.oldにローテーションされます。詳細は開発ガイド第 5 節を参照してください。
正直版はルーターの物語ほど格好良くありませんが、運用はしやすい:Web に触れる4つの方法、それぞれのコストとそれぞれのスイッチ、そして自分で選ばなければならないエージェント。ルーティングエンジンを削除した時点で、このページはそれを説明するのをやめる必要がありました。
チャネル確認の保存と照合
Section titled “チャネル確認の保存と照合”高リスク Computer Use の確認は元のアカウントと会話・スレッドへ送信されます。確認 <完全 UUID> または 取消 <完全 UUID> で返信します。質問は 回答 <完全 UUID> <回答> で回答し、ツール実行を許可しません。単独の yes/A/B は要求を選択しません。実行前に画面、ウィンドウ、ポリシーとキャンセル状態を再確認します。再起動後は古い画面の承認を失効させ、レシートのない実行は uncertain として Admin が照合します。対応経路と制限を参照してください。
コンピューター操作ワークスペース(セッション後も残るファイル)
Section titled “コンピューター操作ワークスペース(セッション後も残るファイル)”コンピューター操作のコンテナはセッション終了時に削除されます。社員がまとめた内容を残すには、セッションにコンピューター操作ワークスペースを接続します:computer_session_start に workspace = "new" か既存の ws-… id を渡します。書き込むのは gateway だけです(computer_workspace_write、社員の現在のセッションが接続したワークスペースへ)。computer_workspace_list と computer_workspace_read はセッションなしで使えます。コンテナ内のファイルは /workspace/files に読み取り専用で置かれ、root だけが入れる tmpfs の下にあるため、ブラウザのアカウントは入れません。既定は無効:config.toml [computer_use.workspaces] enabled = true と、社員の [capabilities.computer_use_config] workspace = true が必要です。macOS と Linux のみ対応です。
クォータ、保持期限、同時に 1 セッションだけのリース、運用者コマンド(duduclaw ops computer-workspaces。状態を変える操作はすべて先にダッシュボードで Admin の承認が必要。緊急時はダッシュボードか全体スイッチ)、既知の制限(所有者による分離は 3 つのワークスペースツールにしか成り立たず、Read や Bash を持つ社員には成り立たないことを含む)はコンピューター操作ワークスペースガイドを参照してください。
アイドル保持、ライブ表示、引き継ぎ
Section titled “アイドル保持、ライブ表示、引き継ぎ”人が従業員のコンピューターを見て必要なら操作を引き継げるように、また作業が途切れてもセッションをやり直さずに済むようにする 3 つの追加です。いずれも従業員にできることを狭める方向にだけ働きます。コード:crates/duduclaw-gateway/src/computer_use_sessions/(keepalive.rs、live_view.rs、live_ops.rs、view_ws.rs、rfb.rs)、ダッシュボードは web/src/components/agent/ComputerSessionPanel.tsx。
アイドル保持
Section titled “アイドル保持”[capabilities.computer_use_config]keep_alive_minutes = 30 # 既定 0 = 従来どおり 2 分のアイドルで終了。最大 240takeover_idle_minutes = 10 # 既定 10。最大 60keep_alive_minutes が 0 より大きいと、2 分間アイドルのセッションは終了せず、リーパーがコンテナを一時停止します(docker pause)。従業員の次の computer_* 呼び出し、またはダッシュボードの視聴者が再開させ(docker unpause)、そのまま処理が続きます。keep_alive_minutes を超えて一時停止したセッションは終了します。一時停止中もほかの規則はそのままです:max_session_minutes の期限は進み続け、max_actions も変わらず、脅威レベル RED、チャットの緊急停止、権限の取り消し、ワークスペースの制御権喪失、computer_session_stop で終了します。一時停止中のセッションは枠を保持するため、同時 5 セッションの上限に数えられます。keep_alive_minutes を下げる・外す変更は実行中のセッションにも効きますが、上げる変更は効きません。コンテナが再開できない場合はセッションが終了します(resume_failed)。期限ラベルを過ぎた一時停止中のコンテナは、gateway インスタンスロックを持つ gateway が再開させてから、通常の 10 分の猶予なしに削除します。どの gateway も期限から 10 分後には削除します。一時停止と再開は監査されます(session_pause、session_resume)。
従業員ページに「コンピューター」タブがあります(従業員に computer_use があるときに表示)。実行中・一時停止中・確認待ち・人が操作中のいずれか、使用した操作数、残り時間、アイドル保持の時間、視聴者数を 5 秒ごとに更新して表示します。「見る」で画面を開きます。
誰が何をできるか(ロールと割り当ては、リクエストと視聴接続のたびに users.db から読み直します):
| 管理者 | 従業員に割り当てられたマネージャー | Operator 以上で割り当てられたアカウント | |
|---|---|---|---|
| 状態・視聴・終了 | 可 | 可(割り当てレベルを問わない) | 可 |
| 引き継ぎ・返却・インジェクション疑いからの再開 | 可 | Operator 以上の割り当てが必要 | 不可 |
画面の届き方:誰かが視聴すると、gateway はコンテナ内で x11vnc を起動します(docker exec … duduclaw-vnc start viewonly。新しい 8 文字のランダムなパスワードを stdin で渡します)。待ち受けは root 専用の /tmp/duduclaw-root 内の unix socket だけで、TCP ポートは開きません(5900–5999 で何かが待ち受けていればヘルパーは動作を拒否します)。そのためネットワークからもブラウザの非特権アカウントからも届かず、ホストにもポートは公開されません。ダッシュボードは computer_sessions.view RPC から 30 秒有効・1 回限りのチケットを受け取り、gateway の /ws/computer-view?ticket=… を開きます。gateway はダッシュボードのソケットと同様に Origin を確認し、チケットを消費し、アカウントを読み直して権限を判定し、1 セッションあたり最大 4 人の視聴者を許可して、WebSocket を docker exec -i … duduclaw-vnc-relay(unix socket と stdio の中継)につなぎます。セッションの終了時と、アカウントが権限を失ったとき(30 秒ごとに確認)に接続は閉じます。描画は noVNC です。
127.0.0.1 にポートを公開して gateway が中継する方法も検討しましたが、ホスト上のどのユーザーのどのプロセスでもそのポートに接続できるため採用していません。docker exec の中継には Docker デーモンへのアクセスが必要です。
gateway は視聴側のプロトコル(RFB 3.7/3.8)を読みます:キーボード、マウス、クリップボード、拡張キーのメッセージは引き継ぎ中のアカウントからのものだけを通します。画面サイズの変更(SetDesktopSize。スクリーンショットのマスクを壊します)と xvp のシャットダウン・再起動要求は常に破棄し、認識できない内容が来たら接続を閉じます。誰も引き継いでいない間は VNC サーバー自体も -viewonly で動き、常に -noremote -nocmds -nosel(クリップボードはどちら向きにも交換しない)付きです。視聴接続ごとにブラウザ監査ログへ view_start と view_stop(誰が、どれだけの時間、転送した入力メッセージ数)を書き込みます。画面とキー入力の内容は記録しません。
引き継ぎと返却
Section titled “引き継ぎと返却”「引き継ぐ」を押すと、そのアカウントがセッションの引き継ぎ権を持ちます。保持中は:
- 従業員の
computer_click、computer_type、computer_key、computer_scroll、computer_navigate、computer_workspace_writeはすべてhuman_has_controlで拒否され、待つよう伝えられます。スクリーンショット、状態確認、終了は従来どおりで、スクリーンショットのマスクも従来どおりです。 - ストリームは入力モードと新しいパスワードで再起動し、保持者のキーボードとマウスだけが画面に届きます。
- セッションはアイドルで終了も一時停止もしません。
引き継ぎ権は、「返却」(保持者または管理者)、保持者の入力が takeover_idle_minutes 分ないとき、ストリームを起動できなかったとき、セッションの終了時に終わり、ストリームは表示のみに戻ります。従業員は作業状態(working_state_handoff と同じ保存先)を通じて、構造化された continue の引き継ぎを受け取ります:誰が・どれだけの時間・何回入力したか、終わった理由、相手の任意のメモ(input_guard を通し、疑わしいメモは注記に置き換え)、引き継ぎ前の引き継ぎ(短縮)、そして次の一歩「まずスクリーンショットを撮り、古い座標を使わない」。監査:takeover_start、takeover_end。引き継ぎの時点で最後の確認を通過していた操作は完了し、その後の操作はすべて止まります。
インジェクションの疑いによる一時停止
Section titled “インジェクションの疑いによる一時停止”computer_screenshot のたびに、表示中のページのテキスト(document.body.innerText、最大 32,768 文字)を duduclaw-eval-dom で isolated world から読み、ブロックのしきい値で input_guard にかけます。ブロック相当に該当するとセッションは保留されます:
- そのスクリーンショットと、人が再開するまでのすべてのスクリーンショットが
mask_reasoninjection_suspectedで全体マスクされます。 - 従業員の操作とワークスペースへの書き込みはすべて
injection_suspectedで拒否されます。 - Activity Feed と従業員の通知チャネルへの L3 通知に、該当したルールのカテゴリが書かれます。
- 監査
injection_suspectedにはルールのカテゴリだけを記録し、ページのテキストは記録しません。
「コンピューター」タブの「再開」で解除します(監査 injection_resume)。ページのテキストを読めなかった場合、スクリーンショットは mask_reason text_unscanned で全体マスクされ(すでに全体マスクならその理由のまま)、セッションは保留されません。テキストはスクリーンショットの直後に読むため、その間に変わったページは新しい内容で検査されます。input_guard 自体の限界はそのままです:文の形で照合するため、普通のページでも反応することがあり、巧妙に偽装されたテキストは通ることがあります。
対象外と未検証
Section titled “対象外と未検証”- v1.75.0 のワークフロー は2026-10-18に linux/amd64 と linux/arm64 のビルド・orchestrator フラグでの smoke を完了しました。ただし RFB greeting は認証の証明ではありません。後の実ブラウザー検証で、x11vnc の -localhost が Unix socket の相手名 localhost を IP 許可リストで拒否することが判明しました。未公開の helper は -listen 127.0.0.1 を使い、root 専用 Unix socket、パスワード、TCP 非待受チェックを維持します。
- ローカルの Docker Desktop macOS arm64 で、実際の RFB 認証と再接続、誤ったパスワードの拒否、sandbox の socket 接続拒否、5900–5999 の非待受、停止時の socket 削除を確認しました。Chrome は実 gateway/noVNC 経由で合成ページを表示しました。実サイトのアカウントは使っていません。他のブラウザー、リモート WSS プロキシ、ネイティブ Linux/Windows、一時停止したコンテナの強制削除は未検証です。隔離テストは scripts/live-test/computer-vnc-auth.mjs で、dashboard の依存パッケージ導入後に実行できます。
- VNC 認証は 8 文字のパスワードと DES です。実際の保護は root 専用の unix socket と、gateway の認証済み・フィルタ付きの中継です。パスワードは権限のある視聴者のブラウザに届きます。
- 従業員に割り当てられたマネージャーや Operator には画面全体が見えます(マスクは従業員が受け取るスクリーンショットにのみ適用され、ライブストリームには適用されません)。サイトが表示する内容も含みます。
- 表示のみと入力モードの切り替えは VNC サーバーを再起動するため、視聴中の人はいったん切断されて再接続します。
- アイドル保持、引き継ぎ、視聴者数は gateway プロセス内にあり、gateway を再起動するとセッションは従来どおり終了します。
- 2 つの設定は従業員編集ページのコンピューター操作セクションにあります(管理者のみ、
agents.update経由):アイドル時の保持 0–240 分、引き継ぎのアイドル上限 1–60 分。それ以外の値や型はサーバーが拒否します。