Перейти к содержимому

Rigs

Только факты. Чтобы понять модель, стоящую за ними, читайте Rigs и изолированные среды; чтобы открыть машину, следуйте руководству.

Поверхность Управляет Бэкенд Значение Ограниченный исходящий доступ
Терминал (exec) Оболочка через exec-канал VM microVM smolvm Да
Браузер Безголовый Chromium (CDP), Firefox (WebDriver BiDi) или WebKit (W3C WebDriver) microVM smolvm Да
Компьютер Рабочий стол X11: ввод через гипервизор, захват через гостевого агента QEMU qemu-hvf (macOS), qemu-kvm (Linux), qemu-tcg (неускоренный запасной вариант, никогда не выбирается автоматически) Да
Android Android через ADB Эмулятор Google android-emulator Нет — эмулятор сам управляет своей сетью
iOS Симулятор iOS через WebDriverAgent CoreSimulator ios-simulator Нет — сеть следует за хостом Mac

Терминал и поверхность computer_use используют одно значение поверхности — computer; терминал отличается маркером своего exec-образа и никогда не переиспользуется как управляемый рабочий стол (и наоборот). Один разговор переиспользует одну терминальную машину.

Движок Значение Протокол Живой просмотр Статичные кадры
Chromium chromium (по умолчанию) Chrome DevTools Protocol Скринкаст — браузер проталкивает кадры по мере перерисовки страницы JPEG
Firefox firefox WebDriver BiDi Опрашиваемые статичные кадры (в BiDi нет скринкаста), не чаще 6 кадров/с JPEG
WebKit webkit W3C WebDriver (классический) Опрашиваемые статичные кадры, транскодируются ffmpeg на хосте PNG

Rig работает с одним движком всю свою жизнь. Один разговор может держать по одному rig на движок, и именно это делает возможным сравнение бок о бок: открыть «Firefox (VM)» рядом с «Chromium (VM)» — это две машины, и закрытие одной оставляет другую работать.

Передайте engine в rig.open или в инструмент browser_use. Неизвестное имя отклоняется, а не подменяется значением по умолчанию: попросить браузер и молча получить другой — значит ответить на вопрос о совместимости неправильным ответом.

Ограничения конкретных движков проговариваются, а не обходятся:

  • Firefox не умеет перезагружаться в обход кэша (он прямо отклоняет ignoreCache из BiDi), поэтому жёсткая перезагрузка — это обычная перезагрузка.
  • WebKit тоже не умеет перезагружаться в обход кэша и не имеет потока консоли (вывод перехватывается хуком на console.* после каждой навигации, поэтому всё, что залогировано во время загрузки, теряется — об этом сообщает каждое извлечение консоли), а для живого просмотра ему нужен ffmpeg на хосте.
  • Ни один из них не отдаёт дерево доступности платформы по своему протоколу, поэтому extract (a11y) возвращает приближение, построенное из DOM, и помечено как таковое.
  • Ни один из них не принимает настоящий сброс файлов; но поле выбора файла всё ещё можно навести на пути внутри машины.
Поверхность Образ Получение
Терминал ubuntu:24.04, закреплён по дайджесту Автоматически при первой загрузке, через собственную сеть машины за шлюзом
Браузер (Chromium, Firefox, WebKit) debian:trixie-slim, закреплён по дайджесту, плюс собственные пакеты движка Автоматически при первой загрузке, затем кэшируется как прогретый пакет
Компьютер Образ рабочего стола qcow2 с Chromium, Firefox, WebKit и тем же базовым набором CLI, как у раннера, размещённого на GitHub, который прогревает терминал Импорт в разделе Настройки → Сервер → Rigs (загрузки, закреплённые по контрольной сумме, где они опубликованы)
Android Системные образы Google Через Android SDK
iOS Среда выполнения симулятора Xcode плюс WebDriverAgent, закреплённый по контрольной сумме Среда выполнения — через Xcode; мост автоматизации — из раздела Настройки → Сервер → Rigs

После первой загрузки каждого вида microVM сервер кэширует предварительно распакованную копию в каталоге данных и загружает из неё каждую следующую машину — включая заранее прогретые образы с уже установленными пакетами. Терминал прогревает базовый набор CLI, как у раннера, размещённого на GitHub (git, gcc, python3, jq, rsync, инструменты для архивов и остальной базовый набор apt; но не Docker и не toolcache языков). Образ «Компьютер» qcow2 запекает те же имена CLI во время сборки образа, рядом с XFCE и тремя браузерами. Каждый браузер прогревает свой движок. Все три движка используют общий базовый образ Debian, но никогда не делят один пакет: кэш привязан к образу, движку и ревизии аудио (audio-v1-<engine>), потому что машина, прогретая для одного движка — или до того, как PulseAudio и ffmpeg вошли в рабочую нагрузку, — не содержит ничего из другого. Пакет терминала по той же причине привязан к ревизии набора инструментов (tools-v1): пакет, прогретый до того, как компилятор стал частью образа, не должен обслуживать машину, у которой он должен быть.

Готового образа автоматизации, который стоило бы взять, нет: chromedp/headless-shell от Chromium собирается без ALSA и PulseAudio, поэтому он в принципе не может издавать звук; единственный поддерживаемый образ Firefox в 33 раза больше базового Debian и всё равно не содержит socat; поддерживаемого образа WebKit нет вообще. Поэтому каждый движок устанавливается при первом запуске (chromium, firefox-esr или webkit2gtk-driver) и делает это, обращаясь к архивам Debian (deb.debian.org, security.debian.org, cloudfront.debian.net). Chromium — это дистрибутивный бинарник, запускаемый как chromium --headless=new.

Ключ настройки Что переопределяет
rigs.smolvm.execImage Образ Terminal (VM)
rigs.smolvm.browserImage Образ Chromium Browser (VM)

Записывается через хранилище настроек рабочего пространства с проверкой администратора (Настройки → Сервер → Rigs → Свои образы (это рабочее пространство)). Значение должно быть ссылкой на реестр — локальные пути, архивы и строки, похожие на флаги, отклоняются, причём и в поле, и повторно при загрузке. Недопустимое сохранённое значение игнорируется с громким предупреждением, и вместо него загружается образ по умолчанию. Собственный реестр образа (а также известные сопутствующие blob-CDN для Docker Hub, GHCR, Quay, GitLab и GCR) допускается через шлюз исходящего доступа для загрузки.

Контракт образа терминала — bash присутствует; git, curl, socat предустановлены или есть базовый образ Debian/Ubuntu с apt (при первом запуске изолированная среда ставит эти три пакета плюс базовый набор CLI, как у раннера, размещённого на GitHub: компиляторы, Python 3, jq, инструменты для архивов, rsync. Toolcache языков и Docker не устанавливаются).

Контракт образа браузера — базовый образ Debian/Ubuntu с apt (или уже установленные пакеты) с bash и socat, плюс бинарник chromium, который сервер сам запускает как chromium --headless=new с DevTools на гостевом loopback 9223, проброшенном на гостевой порт 9222. Для звукового канала при первом запуске устанавливаются PulseAudio и ffmpeg. Не собирайте FROM chromedp/headless-shell: вышестоящий headless.gn собирается без ALSA и PulseAudio, поэтому такой бинарник не может издавать звук. Переопределение применяется только к Chromium-rig — команда рабочей нагрузки, которую он загружает, принадлежит Chromium, и передача её Firefox или WebKit запустила бы бинарник, которого там нет. Rig на Firefox или WebKit игнорирует его с громким предупреждением и загружает свой закреплённый образ.

Порты автоматизации — каждая браузерная машина пробрасывает ровно один loopback-порт хоста на гостевой 9222. За ним: DevTools Chromium и удалённый агент Firefox сидят на гостевом loopback 9223, а перед ними стоит ретранслятор socat (оба безусловно привязываются к loopback); драйвер WebKit принимает --host и сам привязывается к сетевому интерфейсу гостя. Firefox вдобавок проверяет заголовок Host на соответствие своему порту, поэтому клиент отправляет гостевую authority, а не ту, к которой он подключался.

Каждое действие разбирается полностью: некорректная полезная нагрузка возвращает сообщение с именем поля, но никогда — исключение. Наблюдательные глаголы (помеченные ○) остаются разрешёнными, пока управление у человека; изменяющие глаголы отклоняются.

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 (ограничен worktree rig и каталогом данных сервера), start_app, stop_app, clear_app_data, uninstall_app, open_url и shell. shell принимает список argv и выполняется внутри Android, никогда через оболочку хоста; он покрывает диагностику для разработчиков, такую как 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 симулятора, ограниченный worktree rig или каталогом данных сервера), start_app, stop_app, uninstall_app, open_url и spawn. spawn принимает argv и выполняет его внутри симулятора через simctl; он никогда не вызывает оболочку хоста.

Координаты — это точки экрана iOS, а не пиксели снимка. Живой поток сохраняет фиксированный размер симулятора и добавляет поля вместо изменения размера устройства. Передача текста через буфер обмена хост→симулятор и симулятор→хост использует ту же политику разрешений для каждого пользователя и каждого направления, что и другие rig; файлы и изображения не принимаются каналом буфера обмена iOS.

Каждый rig создаёт собственное устройство CoreSimulator и сессию WebDriverAgent. При его закрытии останавливается WebDriverAgent, устройство выключается и удаляется, а его запись в реестре удаляется. iOS-rig работают только на сервере macOS с Xcode, установленной средой выполнения iOS и закреплённым мостом автоматизации. Они не предоставляют ни звука, ни микрофона, ни сброса файлов, ни оболочки хоста, ни гарантии изолированной сети.

Инструмент Описание
rig_list Перечисляет rig рабочего пространства с поверхностью, статусом, дисплеем и контроллером
rig_close Уничтожает rig и отбрасывает его диск
Фаза Значение
provisioning Загрузка; деталь статуса дословно передаёт текущий шаг
ready Доступен и принимает действия
parked Припаркован по простою: vCPU остановлены, RAM остаётся резидентной; следующее действие его будит
closing Завершает работу
closed Исчез; запись сохраняется для журнала аудита
failed Умер или так и не поднялся; отличается от других, чтобы интерфейс мог предложить повтор
Причина закрытия Когда срабатывает
requested Человек или агент попросил
idleTimeout Простой дольше 2× окна простоя или вытеснение из-за памяти
ttlExpired Истёк жёсткий срок жизни
conversationEnded Разговор-владелец завершился
workspaceGone Рабочее пространство удалено или утрачено членство
serverShutdown Сервер останавливается
backendFailure Гипервизор под ним умер
Значение Терминал Браузер Компьютер Мобильные
Память 512 МБ 2048 МБ (Chromium); 2560 МБ (Firefox, WebKit) 4096 МБ 4096 МБ
vCPU 2 2 4 4
Жёсткий TTL 8 ч 2 ч 2 ч 2 ч
Окно простоя 45 мин 15 мин 15 мин 15 мин
Экран при загрузке — 1280×800 1280×800 1080×1920
  • Статичные кадры канала агента ограничены 1280×800; человеческий канал согласует размер вплоть до 2560×1600.
  • Бюджет резидентной памяти хоста для всех rig по умолчанию — 12 ГБ, считается в резидентных мегабайтах (припаркованная VM всё ещё удерживает свою RAM). Вытеснение идёт по принципу «дольше всего не использовался» и никогда не забирает машину, которой кто-то управляет, которую смотрит или в которой открыт терминал.
  • Открытый терминал или канал наблюдения удерживает rig от парковки и сбора по простою; жёсткий TTL всё равно действует.
Порт Где Зарезервирован для
7911 Терминальный гость Мультиплексор портов, через который идёт каждый проброс
9222, 9223 Гостевой браузер Ретранслятор и конечная точка автоматизации (WebKit привязывается к 9222 напрямую и не использует ретранслятор)
80, 443 Гостевой loopback браузера Каналы HTTP/HTTPS dev-доменов
  • Обнаружение опрашивает слушающие TCP-порты терминального гостя примерно каждые 4 с, на стороне сервера.
  • На каждый rig автоматически пробрасывается не более 16 портов; остальные перечисляются в журнале, но не пробрасываются.
  • Проброс на стороне хоста привязывается к loopback, предпочитая собственный номер порта гостя; Открыть в локальной сети добавляет порт в локальной сети, назначенный ОС.
  • Dev-домены должны соответствовать name.test или name.localhost (нижний регистр, метка не длиннее 63 символов). Один домен соответствует одному порту.
  • HTTPS dev-домена использует локальный на сервере удостоверяющий центр (EC P-256, в <dataDir>/rigs/tls/, ключи с правами 0600 и никогда не в госте) с одним wildcard-сертификатом для *.test / *.localhost. Нет openssl на хосте — нет канала HTTPS; обычный HTTP всё равно маршрутизируется.
Поверхность Разрешённые хосты по умолчанию
Терминал Базовый набор разработчика песочницы (хосты кода, реестры пакетов, провайдеры моделей) + зеркала apt Ubuntu + собственный хост кода worktree
Браузер usectrl.dev (сайт продукта) — плюс архивы Debian при первой загрузке каждого движка, пока он ставит свои пакеты. Домашняя страница — локальный документ file://, и исходящий доступ ей не нужен
Компьютер Пусто — запрещено всё — пока вызывающий не предоставит хосты
Мобильные Не применяется (сетью управляет эмулятор)

Каждая microVM вдобавок получает хосты реестра своего образа, и только для загрузки. Набор разрешённых хостов брокера учётных данных выводится из того же списка: токен не выпускается для хоста, до которого гость не может добраться.

Копирование и вставка в обе стороны, а также перетаскивание файлов внутрь и наружу. Доступно человеку, управляющему rig; агент получает буфер обмена через глаголы выше и вообще не получает передачу файлов (у него уже есть оболочка внутри изолированной среды).

Пересечение буфера обмена настраивается для каждого пользователя и каждого направления. Вставка хост→rig разрешена по умолчанию; копирование rig→хост запрашивает подтверждение (разрешение на десять минут или «всегда разрешать» в разделе Настройки → Сервер → Rigs → Доступ к буферу обмена). Эти два выбора независимы и никогда не являются настройкой рабочего пространства.

Канал Маршрут Переносит
Буфер обмена GET/POST /rig/clipboard/<rigId> Текст, одно изображение PNG/JPEG (≤16 МБ) и гостевые пути файлов в буфере обмена
Файлы POST /rig/files/<rigId> До 64 файлов, ≤256 МБ каждый и ≤512 МБ на один сброс, с необязательной точкой сброса в гостевых пикселях
Файлы GET /rig/files/<rigId>?p=<base64url path> Байты одного файла, обратно наружу
  • HTTP, а не RPC. Сокет RPC закрывает соединение при входящем фрейме больше 256 КБ — защита от DoS, которая правильна для JSON-команд и фатальна для вставленного снимка экрана. Байты идут по тому же виду маршрута, что уже используют кадры rig и звук встреч.
  • Аутентификация — это подписанная цель rig-files:<workspaceId>/<rigId> плюс членство в рабочем пространстве; отдельная цель, поэтому URL, выпущенный, чтобы смотреть машину, не может ещё и писать в неё файлы.
  • Перехват управления действует. Запись в буфер обмена и сброс файлов — изменяющие операции, и они отклоняются, пока управление у кого-то другого; чтение буфера обмена и вычитывание файла — наблюдение, и они не отклоняются.
  • Куда попадают сбросы: ~/Drops (рабочий стол), ~/drops (терминал — рядом с worktree, но никогда внутри него, поэтому сброс никогда не появляется как неотслеживаемое изменение), /tmp/cc-drops (браузер). Каталог выбирает сервер; вызывающий не может его назвать. Существующие файлы никогда не перезаписываются — конфликтующее имя попадает как 2-name.ext.
  • Что сообщается гостю: страница браузера получает настоящее событие сброса в запрошенной точке (Input.dispatchDragEvent); рабочий стол получает файлы плюс их URI в своём буфере обмена, потому что ни один хост не может синтезировать перетаскивание XDND в произвольный инструментарий без привилегированного демона внутри гостя. Ответ сообщает, что именно из двух произошло.
  • Перетаскивание наружу работает только на рабочем столе и читает XdndSelection гостя, пока перетаскивание активно. Полезная нагрузка, идентичная тому, что гость уже держал в момент начала нажатия, считается устаревшим выделением от завершённого перетаскивания, а не новым, — в X нет шага «перетаскивание завершено».
  • Требования к образу: образ рабочего стола должен быть протокола 2 или новее (собран после появления поддержки буфера обмена). Старый образ отвечает 404, и приложение говорит пересобрать его, а не сообщает о сломанной машине.
  • Журнал аудита фиксирует форму, но никогда содержимое: записи clipboard_read, clipboard_write, drop_files и read_file несут количества, размеры и пути. Буфер обмена — это место, где учётные данные живут по несколько секунд, и ничего из этого не записывается.
  • Инструменты rig объявляют класс действий enclosureControl (плюс networkEgress и processSpawn у инструментов использования; mobile_use и ios_use также объявляют packageInstall). rig_close объявляет enclosureControl; rig_list не объявляет ничего. Разрешён по умолчанию; режимы только для чтения запрещают его целиком; регулятор автономии пространства может понизить его до «Спросить сначала».
  • Доменные события: RigClosedEvent (с причиной закрытия), RigControlChanged (перехват управления / возврат управления), RigReaped (сбор по простою, TTL или из-за памяти, с указанием управляющего агента). Открытие rig не является событием. См. доменные события.
  • Хранилище: rig_sessions и rig_action_log в базе данных каждого рабочего пространства. Журнал действий записывает каждое событие ввода с его субъектом и монотонно возрастающим номером для каждого rig; введённый текст перед записью обезличивается до длины и хеша.
  • Каталог данных: rigs/images/ (импортированные образы qcow2), rigs/smolvm/ (состояние среды выполнения каждого rig), rigs/smolvm-packs/ (предварительно распакованные пакеты машин), rigs/tls/ (удостоверяющий центр dev-домена), rigs/run/ (оверлеи и seed-файлы QEMU).
  • Маршруты: живой просмотр — это вкладка в пространстве или на странице PR; /settings/server/rigs — экран настроек Rigs.