跳到內容

機台

這裡只談事實。若要了解背後的概念模型,請參閱機台與 隔離艙;要開啟一台機器,請參閱這篇 指南。

表面 驅動 後端 值 受控的對外連線
終端機 (exec) 在 VM exec 通道上提供的一個 shell microVM smolvm 是
瀏覽器 無介面 Chromium (CDP)、Firefox (WebDriver BiDi) 或 WebKit (W3C WebDriver) microVM smolvm 是
電腦 X11 桌面:輸入經由 Hypervisor,擷取經由客體代理程式 QEMU qemu-hvf (macOS)、qemu-kvm (Linux)、qemu-tcg(未加速的備援,永不自動選用) 是
Android 透過 ADB 的 Android Google 的模擬器 android-emulator 否——模擬器自行管理網路
iOS 透過 WebDriverAgent 的 iOS 模擬器 CoreSimulator ios-simulator 否——網路跟隨主機 Mac

終端機與 computer_use 表面共用 computer 這個表面值;終端機以其 exec 映像標記作為區分,且永遠不會被當成可操控的桌面重複使用(反之亦然)。 一次對話會重複使用同一台終端機機器。

引擎 值 協定 即時畫面 靜態畫面
Chromium chromium(預設) Chrome DevTools Protocol 螢幕串流——瀏覽器在頁面重繪時推送影格 JPEG
Firefox firefox WebDriver BiDi 輪詢式靜態畫面(BiDi 沒有螢幕串流),上限為 6 fps JPEG
WebKit webkit W3C WebDriver(經典版) 輪詢式靜態畫面,由主機的 ffmpeg 轉碼 PNG

一台機台終其一生只執行一個引擎。一次對話可以每個引擎各持有一台 機台,這正是能並排比較的原因:把「Firefox (VM)」開在「Chromium (VM)」 旁邊就是兩台機器,關掉其中一台,另一台仍會繼續執行。

把 engine 傳給 rig.open 或 browser_use 工具。未知的名稱會被拒絕, 而不是退回預設值——要求一種瀏覽器卻默默拿到另一種,等於是用錯誤的答案 回答一個相容性問題。

各引擎特有的限制,會被明說,而不是被繞過:

  • Firefox 沒有可略過快取的重新載入(它會直接拒絕 BiDi 的 ignoreCache), 因此硬性重新載入就等同一般重新載入。
  • WebKit 同樣沒有可略過快取的重新載入,也沒有主控台饋送(輸出是在每次 瀏覽之後藉由掛鉤 console.* 擷取,因此載入期間記錄的任何內容都會漏掉 ——每一次主控台擷取都會註明這一點),而且它的即時畫面需要主機上有 ffmpeg。
  • 兩者都不會透過各自的協定暴露平台無障礙樹,因此 extract (a11y) 會傳回由 DOM 推導出的近似結果,並標明它只是近似。
  • 兩者都無法接受真正的檔案置放;檔案輸入仍可指向機器內部的路徑。
表面 映像 取得方式
終端機 ubuntu:24.04,以摘要鎖定 首次開機時自動取得,經由機器自己受閘門管制的網路
瀏覽器(Chromium、Firefox、WebKit) debian:trixie-slim,以摘要鎖定,外加引擎自己的套件 首次開機時自動取得,之後快取為預熱套件
電腦 一個 qcow2 桌面映像,內含 Chromium、Firefox、WebKit,以及終端機所預熱的同一套 GitHub 託管執行器 CLI 基準線 在 設定 → 伺服器 → 機台 下匯入(有發布的地方會採以總和檢查碼鎖定的下載)
Android Google 的系統映像 透過 Android SDK
iOS 一個 Xcode 模擬器執行環境,外加以總和檢查碼鎖定的 WebDriverAgent 執行環境透過 Xcode;自動化橋接器從 設定 → 伺服器 → 機台 取得

每一種 microVM 首次開機之後,伺服器會在資料目錄下快取一份預先解出的 複本,之後每一台機器都從它開機——包括已預先預熱、套件已安裝好的映像。 終端機會預熱一套 GitHub 託管執行器風格的 CLI 工具組(git、gcc、 python3、jq、rsync、封存工具,以及 apt 基準線的其餘部分;不含 Docker 或語言工具快取)。電腦的 qcow2 在映像建置時就把同樣的 CLI 名稱 烘焙進去,與 XFCE 及三個瀏覽器並列。每個瀏覽器都會預熱自己的引擎。三個 引擎共用一個 Debian 基底,但絕不共用同一個套件:快取是以映像、引擎以及 音訊修訂版(audio-v1-<engine>)為索引鍵,因為為某個引擎預熱的機器——或 在 PulseAudio 與 ffmpeg 成為工作負載一部分之前預熱的機器——裡面不會有 另一個引擎的東西。基於同樣的理由,終端機套件是以工具組修訂版 (tools-v1)為索引鍵:在編譯器尚未成為映像一部分之前預熱的套件,不得 用來供應一台理應要有編譯器的機器。

沒有任何現成的自動化映像值得採用——Chromium 的 chromedp/headless-shell 把 ALSA 與 PulseAudio 都編譯掉了,因此永遠發不 出聲音;唯一有在維護的 Firefox 映像大小是 Debian 基底的 33 倍,而且仍未 內附 socat;WebKit 則根本沒有有在維護的映像。因此每個引擎都會在首次 啟動時安裝(chromium、firefox-esr 或 webkit2gtk-driver),並在 過程中連上 Debian 封存站(deb.debian.org、security.debian.org、 cloudfront.debian.net)。Chromium 採用的是發行版二進位檔,以 chromium --headless=new 啟動。

設定索引鍵 覆寫項目
rigs.smolvm.execImage 終端機 (VM) 映像
rigs.smolvm.browserImage Chromium 瀏覽器 (VM) 映像

透過受管理員閘門管制的工作區設定儲存區寫入(設定 → 伺服器 → 機台 → 自訂映像(此工作區))。該值必須是登錄庫參照——本機路徑、封存檔與類旗標 字串都會被拒絕,在欄位輸入時會拒絕一次,開機時會再拒絕一次。無效的已 儲存值會被明確地忽略,改為啟動預設值。映像自己的登錄庫(外加 Docker Hub、GHCR、Quay、GitLab 與 GCR 已知的 blob CDN 搭配主機)會為了拉取而 獲准通過對外連線閘門。

終端機映像契約——必須有 bash;預先安裝 git、curl、socat, 或具備 Debian/Ubuntu apt 基底(隔離艙會在首次啟動時安裝那三個,外加 一套 GitHub 託管執行器風格的 CLI 工具組:編譯器、Python 3、jq、封存 工具、rsync。語言工具快取與 Docker 則不安裝)。

瀏覽器映像契約——具備 bash 與 socat 的 Debian/Ubuntu apt 基底 (或已安裝的這些套件),外加一個 chromium 二進位檔,由伺服器自行以 chromium --headless=new 啟動,DevTools 位於客體回送位址 9223,再轉送 到客體連接埠 9222。PulseAudio 與 ffmpeg 會在首次啟動時為音訊通道安裝。 請勿以 FROM chromedp/headless-shell 建置:上游的 headless.gn 把 ALSA 與 PulseAudio 都編譯掉了,因此那個二進位檔發不出聲音。這項覆寫只適用於 Chromium 機台——它所開機執行的工作負載命令是 Chromium 的,若交給 Firefox 或 WebKit 就會啟動一個不存在的二進位檔。Firefox 或 WebKit 機台 會明確地忽略它,並啟動自己以鎖定的映像。

自動化連接埠——每一台瀏覽器機器都只把一個主機回送連接埠轉送到客體 9222。在它背後:Chromium 的 DevTools 與 Firefox 的遠端代理位於客體 回送位址 9223,前面有一道 socat 轉送(兩者都無條件綁定回送位址); WebKit 的驅動程式則接受 --host,自行綁定客體網路介面。Firefox 還會拿 Host 標頭與自己的連接埠比對驗證,因此用戶端送出的是客體端的授權位址, 而不是它所撥接的那一個。

每一個動作都會被完整解析:格式錯誤的承載內容會傳回一則指名欄位的訊息, 絕不會拋出例外。觀察型動詞(標記為 ○)在人類持有控制權時仍可使用; 變更型動詞則會被拒絕。

screenshot○, cursor_position○, mouse_move, left_click, right_click, middle_click, double_click, triple_click, left_mouse_down, left_mouse_up, left_click_drag, scroll, key, hold_key(0–60 秒), type, wait○(0–60 秒), set_display, clipboard_read○, clipboard_write.

座標是客體目前模式下的客體像素;每一張螢幕擷取畫面都會註明顯示器大小。 按鍵組合採用 X11 的拼法(ctrl+s、alt+Tab、Return)。

clipboard_read 接受一個 selection:clipboard(ctrl+C 所填入的內容, 預設值)、primary(X11 的選取即複製)或 xdnd(正在進行中的拖曳)。 三者傳回空結果都是正常的答案。clipboard_write 只接受 text——影像與 檔案走的是檔案通道,因為動作的引數會被保存到稽核日誌,而影像位元組沒有 理由出現在那裡。剪貼簿內容回來時會圍上柵欄,標示為不受信任的資料。

navigate(僅限 http/https), reload(hard 為選用), stop_loading, click(選擇器或座標), fill, type, key(DOM 按鍵名稱), scroll, mouse_move, drag, left_mouse_down, left_mouse_up, extract○(a11y | dom | console), screenshot○(full_page 為選用), set_viewport(最小 320×240), history, wait_for○(選擇器, 0.1–30 秒), clipboard_read○, clipboard_write.

擷取到的頁面內容回來時也會圍上柵欄,標示為不受信任的資料。

瀏覽器恰好只有一個剪貼簿,因此 clipboard_read 不接受 selection。 兩個剪貼簿動詞都需要安全情境:以純 http:// 提供的頁面根本沒有 剪貼簿 API,而該動詞會明說這件事,而不是回報一個空的剪貼簿——在那裡請 改用 type,它會在游標插入點直接輸入,不經過剪貼簿。

tap, swipe(50–5000 毫秒), type, key(別名 back, home, recents, enter, delete, backspace, tab, escape, search, menu, volume_up, volume_down, power,或任何 KEYCODE_*), screenshot○, ui_dump○, install_apk (僅限於機台的工作樹與伺服器資料目錄), start_app, stop_app, clear_app_data, uninstall_app, open_url 與 shell。 shell 接受一個 argv 清單,並在 Android 內執行,絕不經過主機 shell; 它涵蓋 logcat、dumpsys 與 pm grant 等開發者診斷工具。

mobile_use 沒有剪貼簿動詞。自 Android 10 起,只有取得焦點的應用程式 可以讀取剪貼簿。

tap, swipe(50–5000 毫秒), type, key(enter, backspace, tab, escape、方向鍵或一個可列印字元;可選用 command, control, option 與 shift 修飾鍵), home, lock, unlock, screenshot○, ui_dump○, install_app(僅限於機台工作樹或伺服器資料目錄 中的模擬器 .app 目錄), start_app, stop_app, uninstall_app, open_url 與 spawn。spawn 接受 argv,並透過 simctl 在模擬器內執行;它絕不會叫用主機 shell。

座標是 iOS 螢幕的點,不是螢幕擷取畫面的像素。即時串流會維持模擬器的 固定大小,並在其周圍補上黑邊,而不是調整裝置大小。主機到模擬器與模擬器 到主機的文字剪貼簿傳輸,採用與其他機台相同、依使用者且依方向的權限政策; 檔案與影像不為 iOS 剪貼板通道所接受。

每一台機台都會建立一個自己擁有的 CoreSimulator 裝置與 WebDriverAgent 工作階段。關閉機台時會停止 WebDriverAgent、關閉並刪除該裝置,並移除它的 登錄項目。iOS 機台只能在具備 Xcode、已安裝 iOS 執行環境與已鎖定自動化 橋接器的 macOS 伺服器上執行。它們不提供音訊、麥克風、檔案置放、主機 shell,也不保證封閉式網路。

工具 說明
rig_list 列出某工作區的機台,附帶表面、狀態、顯示器與控制者
rig_close 銷毀一台機台並捨棄其磁碟
階段 意義
provisioning 正在開機;狀態詳細資料會逐字帶有目前步驟
ready 可連線並接受動作
parked 已閒置停放:vCPU 已停止,RAM 仍常駐;下一個動作會喚醒它
closing 正在關閉
closed 已消失;該列會為了稽核軌跡而保留下來
failed 已死亡或從未啟動成功;可加以區別,讓 UI 能提供重試
關閉原因 觸發時機
requested 有人或代理要求
idleTimeout 閒置超過閒置時間窗的 2 倍,或因記憶體而被驅逐
ttlExpired 硬性存續時間用盡
conversationEnded 擁有它的對話結束
workspaceGone 工作區被刪除或失去成員資格
serverShutdown 伺服器正在停止
backendFailure 其下的 Hypervisor 死亡
值 終端機 瀏覽器 電腦 行動
記憶體 512 MB 2048 MB(Chromium);2560 MB(Firefox、WebKit) 4096 MB 4096 MB
vCPU 2 2 4 4
硬性 TTL 8 小時 2 小時 2 小時 2 小時
閒置時間窗 45 分鐘 15 分鐘 15 分鐘 15 分鐘
開機顯示 — 1280×800 1280×800 1080×1920
  • 代理通道的靜態畫面上限為 1280×800;人類通道則可協商到最高 2560×1600。
  • 主機為所有機台設定的常駐記憶體預算預設為 12 GB,以常駐 MB 計算 (已停放的 VM 仍持有它的 RAM)。驅逐採最近最少使用,且絕不帶走有人正在 操控、觀看,或有終端機開啟的機器。
  • 開啟的終端機或觀看通道會讓機台免於被停放與閒置回收;硬性 TTL 仍然 適用。
連接埠 位置 保留用途
7911 終端機客體 每一次轉送所乘載的連接埠多工器
9222, 9223 瀏覽器客體 自動化轉送與端點(WebKit 直接綁定 9222,不使用轉送)
80, 443 瀏覽器客體回送位址 開發網域的 HTTP/HTTPS 通道
  • 探索會在伺服器端每約 4 秒輪詢終端機客體正在監聽的 TCP 連接埠。
  • 每台機台最多自動轉送 16 個連接埠;更多會列在日誌中,而不會轉送。
  • 主機端的轉送會綁定回送位址,並優先採用客體自己的連接埠號;在區域 網路分享會加上一個由作業系統指派的區域網路連接埠。
  • 開發網域必須符合 name.test 或 name.localhost(小寫,標籤最長 63 個字元)。一個網域對應一個連接埠。
  • 開發網域的 HTTPS 使用一個伺服器本機的 CA(EC P-256,位於 <dataDir>/rigs/tls/ 下,金鑰權限 0600 且絕不進入任何客體),並為 *.test / *.localhost 帶上一張萬用字元葉憑證。主機上沒有 openssl 就沒有 HTTPS 通道;純 HTTP 仍會路由。
表面 預設允許清單
終端機 沙箱開發者基準線(程式碼託管平台、套件登錄庫、模型供應商)+ Ubuntu apt 鏡像站 + 工作樹自己的程式碼託管平台主機
瀏覽器 usectrl.dev(產品網站)——外加每個引擎首次開機時、安裝其套件期間的 Debian 封存站。首頁是本機的 file:// 文件,不需要對外連線
電腦 空——全部拒絕——直到呼叫端授予主機為止
行動 未強制執行(由模擬器管理網路)

每個 microVM 另外還會取得其映像的登錄庫主機,且僅限於拉取之用。憑證仲介 的允許主機集合也是從同一份允許清單推導而來:客體連不到的主機,不會為它 簽發任何權杖。

複製/貼上的兩個方向,以及把檔案拖進與拖出。提供給操控機台的人;代理則 透過上述動詞取得剪貼簿,而且完全沒有檔案傳輸(它在隔離艙內已經有一個 shell)。

剪貼簿的跨越依使用者且依方向。主機貼到機台預設允許;機台複製到主機則會 提示(十分鐘的授權,或在設定 → 伺服器 → 機台 → 剪貼簿存取下選擇 一律允許)。這兩個選擇彼此獨立,且永遠不會是工作區設定。

通道 路由 承載內容
剪貼簿 GET/POST /rig/clipboard/<rigId> 文字、一個 PNG/JPEG(≤16 MB),以及剪貼簿上檔案的客體路徑
檔案 POST /rig/files/<rigId> 最多 64 個檔案,每個 ≤256 MB 且每次置放 ≤512 MB,可選擇附上以客體像素為單位的置放點
檔案 GET /rig/files/<rigId>?p=<base64url path> 一個檔案的位元組,往外讀出
  • 走 HTTP,不是 RPC。 RPC 通訊端會在收到超過 256 KB 的輸入影格時 關閉連線——這道 DoS 防護對 JSON 命令是對的,但對一張貼上的螢幕擷取 畫面卻是致命的。位元組走的是機台影格與會議音訊早已使用的那類路由。
  • 驗證用的是已簽署的目標 rig-files:<workspaceId>/<rigId>,加上 工作區成員資格——這是一個它自己的目標,因此為了觀看某台機器而簽發的 URL,無法同時把檔案寫入其中。
  • 接手同樣適用。 寫入剪貼簿與置放檔案都是變更型動作,在別人持有控制權 時會被拒絕;讀取剪貼簿與讀出檔案則是觀察型動作,不受此限。
  • 置放的落點: ~/Drops(桌面)、~/drops(終端機——在工作樹旁, 絕不放在其中,因此置放的檔案永遠不會以未追蹤變更的形式出現)、 /tmp/cc-drops(瀏覽器)。目錄由伺服器挑選;呼叫端無法指定。現有檔案 絕不會被覆寫——名稱衝突時會以 2-name.ext 落腳。
  • 告知客體的內容: 瀏覽器頁面會在所要求的位置得到真正的置放事件 (Input.dispatchDragEvent);桌面則在其剪貼簿上拿到這些檔案及其 URI, 因為沒有主機能在沒有客體內特權常駐程式的情況下,把 XDND 拖曳合成進 任意工具組。回覆會說明發生的是兩者中的哪一種。
  • 向外拖曳僅限桌面,並在拖曳進行中讀取客體的 XdndSelection。若承載 內容與按下開始時客體早已持有的內容完全相同,就會被視為某次已完成拖曳 所留下的過期選取範圍,而不是新的選取範圍——X 沒有「拖曳結束了」這種 步驟。
  • 映像需求: 桌面映像必須是協定 2 或更新版本(在剪貼簿支援推出之後 建置)。較舊的映像會回 404,應用程式會請你重建它,而不是回報機器壞了。
  • 稽核日誌記錄的是形狀,絕不是內容:clipboard_read、 clipboard_write、drop_files 與 read_file 各列只帶有計數、大小與 路徑。剪貼簿正是憑證每次停留幾秒鐘的地方,而其中沒有任何內容會被寫 下來。
  • 機台工具會宣告 enclosureControl 動作類別(使用類工具另會宣告 networkEgress 與 processSpawn;mobile_use 與 ios_use 也會宣告 packageInstall)。rig_close 宣告 enclosureControl;rig_list 不宣告任何類別。預設允許;唯讀模式會整批拒絕;各空間的自主性旋鈕可以 將它降級為先詢問。
  • 領域事件:RigClosedEvent(附其關閉原因)、 RigControlChanged(接手/交還)、RigReaped(閒置、TTL 或記憶體 驅逐,附帶操控中的代理)。開啟機台不是事件。 請見領域事件。
  • 儲存:每個工作區資料庫中的 rig_sessions 與 rig_action_log。 動作日誌會記錄每一個輸入事件、其主體,以及每台機台單調遞增的序號; 輸入的文字在寫入之前會先遮除為長度與雜湊。
  • 資料目錄:rigs/images/(匯入的 qcow2 映像)、rigs/smolvm/ (各機台的執行階段狀態)、rigs/smolvm-packs/(預先解出的機器 套件)、rigs/tls/(開發網域 CA)、rigs/run/(QEMU 覆疊層與 種子)。
  • 路由:即時畫面是空間或 PR 頁面上的一個分頁; /settings/server/rigs 是機台設定畫面。