Aller au contenu

Rigs

Des faits uniquement. Pour le modèle qui les sous-tend, consultez Rigs et enceintes ; pour ouvrir une machine, suivez le guide.

Surface Pilote Backend Valeur Sortie réseau confinée
Terminal (exec) Un shell sur le canal exec de la VM microVM smolvm Oui
Navigateur Chromium sans interface (CDP), Firefox (WebDriver BiDi) ou WebKit (W3C WebDriver) microVM smolvm Oui
Ordinateur Bureau X11 : entrée via l’hyperviseur, capture via l’agent de l’invité QEMU qemu-hvf (macOS), qemu-kvm (Linux), qemu-tcg (repli non accéléré, jamais choisi automatiquement) Oui
Android Android via ADB L’émulateur de Google android-emulator Non — l’émulateur gère son propre réseau
iOS Le simulateur iOS via WebDriverAgent CoreSimulator ios-simulator Non — le réseau suit le Mac hôte

Le terminal et la surface computer_use partagent la valeur de surface computer ; le terminal se distingue par son marqueur d’image exec et n’est jamais réutilisé comme bureau pilotable (et inversement). Une conversation réutilise une seule machine terminal.

Moteur Valeur Protocole Vue en direct Images fixes
Chromium chromium (par défaut) Chrome DevTools Protocol Screencast — le navigateur pousse les images à mesure que la page se redessine JPEG
Firefox firefox WebDriver BiDi Images fixes par interrogation (BiDi n’a pas de screencast), plafonnées à 6 fps JPEG
WebKit webkit W3C WebDriver (classique) Images fixes par interrogation, transcodées par le ffmpeg de l’hôte PNG

Un rig exécute un seul moteur pendant toute sa vie. Une conversation peut contenir un rig par moteur, ce qui rend possible une comparaison côte à côte : ouvrir « Firefox (VM) » à côté de « Chromium (VM) », c’est deux machines, et fermer l’une laisse l’autre en marche.

Passez engine à rig.open ou à l’outil browser_use. Un nom inconnu est refusé plutôt que remplacé par la valeur par défaut — demander un navigateur et en obtenir silencieusement un autre répond à une question de compatibilité par une mauvaise réponse.

Limites propres à chaque moteur, énoncées plutôt que contournées :

  • Firefox n’a pas de rechargement contournant le cache (il rejette d’emblée ignoreCache de BiDi), si bien qu’un rechargement forcé est un rechargement normal.
  • WebKit n’a pas non plus de rechargement contournant le cache, ni de flux de console (la sortie est capturée en interceptant console.* après chaque navigation, si bien que tout ce qui est journalisé pendant le chargement est manqué — chaque extraction de console le dit), et a besoin de ffmpeg sur l’hôte pour sa vue en direct.
  • Ni l’un ni l’autre n’expose d’arbre d’accessibilité de la plateforme via son protocole, si bien que extract (a11y) renvoie une approximation dérivée du DOM, étiquetée comme telle.
  • Ni l’un ni l’autre ne peut recevoir un vrai dépôt de fichiers ; un champ de fichier peut toujours être pointé vers des chemins à l’intérieur de la machine.
Surface Image Récupération
Terminal ubuntu:24.04, épinglée par empreinte Automatiquement au premier démarrage, via le réseau filtré de la machine elle-même
Navigateur (Chromium, Firefox, WebKit) debian:trixie-slim, épinglée par empreinte, plus les paquets propres au moteur Automatiquement au premier démarrage, puis mise en cache comme pack préchauffé
Ordinateur Une image de bureau qcow2 avec Chromium, Firefox, WebKit et le même socle d’outils CLI de type runner hébergé par GitHub que celui préchauffé par le terminal Importer sous Paramètres → Serveur → Rigs (téléchargements épinglés par somme de contrôle lorsqu’ils sont publiés)
Android Les images système de Google Via le SDK Android
iOS Un runtime Xcode Simulator et un WebDriverAgent épinglé par somme de contrôle Runtime via Xcode ; pont d’automatisation depuis Paramètres → Serveur → Rigs

Après le premier démarrage de chaque type de microVM, le serveur met en cache une copie pré-extraite dans le répertoire de données et démarre toutes les machines suivantes à partir d’elle — y compris des images préchauffées dont les paquets sont déjà installés. Le terminal préchauffe un socle d’outils CLI de type runner hébergé par GitHub (git, gcc, python3, jq, rsync, les outils d’archivage et le reste du socle apt ; ni Docker ni les caches d’outils par langage). Le qcow2 Ordinateur intègre ces mêmes noms d’outils CLI au moment de la construction de l’image, aux côtés de XFCE et des trois navigateurs. Chaque navigateur préchauffe son moteur. Les trois moteurs partagent une base Debian mais ne partagent jamais un pack : le cache est indexé sur l’image, le moteur et une révision audio (audio-v1-<engine>), car une machine préchauffée pour un moteur — ou avant que PulseAudio et ffmpeg ne fassent partie de la charge de travail — ne contient rien de l’autre. Le pack du terminal est indexé sur une révision d’outillage (tools-v1) pour la même raison : un pack préchauffé avant que le compilateur ne fasse partie de l’image ne doit pas servir une machine censée en avoir un.

Il n’existe aucune image d’automatisation préconstruite qui vaille la peine d’être reprise — le chromedp/headless-shell de Chromium compile ALSA et PulseAudio hors du binaire, il ne peut donc jamais émettre de son ; la seule image Firefox maintenue fait 33× la taille de la base Debian et ne fournit toujours pas socat ; il n’existe aucune image WebKit maintenue. Chaque moteur s’installe donc au premier démarrage (chromium, firefox-esr ou webkit2gtk-driver) et atteint les archives Debian (deb.debian.org, security.debian.org, cloudfront.debian.net) pour ce faire. Chromium est le binaire de la distribution, lancé sous la forme chromium --headless=new.

Clé de réglage Remplace
rigs.smolvm.execImage L’image du Terminal (VM)
rigs.smolvm.browserImage L’image du Navigateur Chromium (VM)

Écrite via le magasin de réglages d’espace de travail réservé aux administrateurs (Paramètres → Serveur → Rigs → Images personnalisées (cet espace de travail)). La valeur doit être une référence de registre — les chemins locaux, les archives et les chaînes ressemblant à des options sont refusés, à la saisie comme au démarrage. Une valeur stockée invalide est ignorée avec un message explicite, et l’image par défaut démarre à la place. Le registre de l’image elle-même (ainsi que les CDN d’objets associés connus pour Docker Hub, GHCR, Quay, GitLab et GCR) est admis par la porte de sortie réseau pour le tirage.

Contrat de l’image Terminal — bash présent ; git, curl, socat préinstallés ou une base apt Debian/Ubuntu (l’enceinte installe ces trois outils plus un socle d’outils CLI de type runner hébergé par GitHub au premier démarrage : compilateurs, Python 3, jq, outils d’archivage, rsync. Les caches d’outils par langage et Docker ne sont pas installés).

Contrat de l’image Navigateur — une base apt Debian/Ubuntu (ou les paquets déjà installés) avec bash et socat, plus un binaire chromium que le serveur lance lui-même sous la forme chromium --headless=new, avec DevTools sur la boucle locale de l’invité 9223, relayé vers le port invité 9222. PulseAudio et ffmpeg sont installés au premier démarrage pour la voie audio. Ne construisez pas FROM chromedp/headless-shell : le headless.gn amont compile ALSA et PulseAudio hors du binaire, celui-ci ne peut donc pas émettre de son. La surcharge ne s’applique qu’aux rigs Chromium — la commande de charge de travail qu’elle démarre est celle de Chromium, et la confier à Firefox ou WebKit lancerait un binaire absent. Un rig Firefox ou WebKit l’ignore avec un message explicite et démarre son image épinglée.

Ports d’automatisation — chaque machine navigateur transfère exactement un port de boucle locale de l’hôte vers le port invité 9222. Derrière ce port : les DevTools de Chromium et l’agent distant de Firefox se trouvent sur la boucle locale de l’invité 9223, avec un relais socat devant (tous deux se lient à la boucle locale sans condition) ; le pilote de WebKit accepte un --host et se lie lui-même à la carte réseau de l’invité. Firefox valide en outre l’en-tête Host par rapport à son propre port, si bien que le client envoie l’autorité côté invité plutôt que celle qu’il a contactée.

Chaque action est analysée de façon totale : une charge utile mal formée renvoie un message nommant le champ, jamais une exception. Les verbes d’observation (marqués ○) restent autorisés tant qu’un humain détient le contrôle ; les verbes modificateurs sont refusés.

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 s), type, wait○ (0–60 s), set_display, clipboard_read○, clipboard_write.

Les coordonnées sont exprimées en pixels de l’invité, dans le mode actuel de celui-ci ; chaque capture d’écran indique la taille de l’écran. Les combinaisons de touches utilisent la notation X11 (ctrl+s, alt+Tab, Return).

clipboard_read prend une selection : clipboard (ce que remplit ctrl+C, la valeur par défaut), primary (la sélection pour copie X11) ou xdnd (un glisser en cours). Un résultat vide est une réponse normale pour les trois. clipboard_write ne prend que text — les images et les fichiers passent par la voie des fichiers, car les arguments d’une action sont conservés dans le journal d’audit et les octets d’image n’y ont rien à faire. Le contenu du presse-papiers revient encadré comme donnée non fiable.

navigate (http/https uniquement), reload (hard facultatif), stop_loading, click (sélecteur ou coordonnée), fill, type, key (noms de touches DOM), scroll, mouse_move, drag, left_mouse_down, left_mouse_up, extract○ (a11y | dom | console), screenshot○ (full_page facultatif), set_viewport (min 320×240), history, wait_for○ (sélecteur, 0,1–30 s), clipboard_read○, clipboard_write.

Le contenu de page extrait revient encadré comme donnée non fiable.

Le navigateur n’a qu’un seul presse-papiers, donc clipboard_read ne prend pas de selection. Les deux verbes de presse-papiers exigent un contexte sécurisé : une page servie en http:// simple n’a aucune API de presse-papiers, et le verbe le signale plutôt que de rapporter un presse-papiers vide — utilisez type dans ce cas, qui insère au niveau du curseur sans passer par un presse-papiers.

tap, swipe (50–5000 ms), type, key (alias back, home, recents, enter, delete, backspace, tab, escape, search, menu, volume_up, volume_down, power, ou tout KEYCODE_*), screenshot○, ui_dump○, install_apk (limité à l’arbre de travail du rig et au répertoire de données du serveur), start_app, stop_app, clear_app_data, uninstall_app, open_url, et shell. shell prend une liste d’arguments et s’exécute dans Android, jamais via un shell de l’hôte ; il couvre des diagnostics de développeur tels que logcat, dumpsys et pm grant.

mobile_use n’a pas de verbes de presse-papiers. Depuis Android 10, seule l’application au premier plan peut lire le presse-papiers.

tap, swipe (50–5000 ms), type, key (enter, backspace, tab, escape, les touches fléchées ou un caractère imprimable ; modificateurs facultatifs command, control, option et shift), home, lock, unlock, screenshot○, ui_dump○, install_app (un répertoire .app de simulateur limité à l’arbre de travail du rig ou au répertoire de données du serveur), start_app, stop_app, uninstall_app, open_url, et spawn. spawn prend des arguments et s’exécute dans le simulateur via simctl ; il n’invoque jamais un shell de l’hôte.

Les coordonnées sont des points d’écran iOS, et non des pixels de capture d’écran. Le flux en direct conserve la taille fixe du simulateur et l’affiche avec des bandes plutôt que de redimensionner l’appareil. Le transfert de texte par presse-papiers de l’hôte vers le simulateur et du simulateur vers l’hôte applique la même politique d’autorisation par utilisateur et par sens que les autres rigs ; les fichiers et les images ne sont pas acceptés par la voie du presse-papiers iOS.

Chaque rig crée un appareil CoreSimulator et une session WebDriverAgent qui lui appartiennent. Sa fermeture arrête WebDriverAgent, éteint et supprime cet appareil, et retire son entrée du registre. Les rigs iOS ne s’exécutent que sur un serveur macOS doté de Xcode, d’un runtime iOS installé et du pont d’automatisation épinglé. Ils n’exposent ni audio, ni microphone, ni dépôt de fichiers, ni shell de l’hôte, ni garantie de réseau confiné.

Outil Description
rig_list Liste les rigs d’un espace de travail avec leur surface, leur état, leur affichage et leur contrôleur
rig_close Détruit un rig et abandonne son disque
Phase Signification
provisioning Démarrage ; le détail d’état reprend l’étape en cours telle quelle
ready Joignable et acceptant des actions
parked Mis en veille : vCPU arrêtés, RAM toujours résidente ; l’action suivante le réveille
closing Arrêt en cours
closed Disparu ; la ligne subsiste pour la piste d’audit
failed Mort ou jamais démarré ; distinguable pour que l’interface puisse proposer un nouvel essai
Motif de fermeture Déclenché quand
requested Une personne ou un agent l’a demandé
idleTimeout Inactif au-delà de 2× la fenêtre d’inactivité, ou évincé pour libérer de la mémoire
ttlExpired La durée de vie maximale a expiré
conversationEnded La conversation propriétaire s’est terminée
workspaceGone L’espace de travail a été supprimé ou l’appartenance a été perdue
serverShutdown Le serveur s’arrête
backendFailure L’hyperviseur est tombé sous lui
Valeur Terminal Navigateur Ordinateur Mobile
Mémoire 512 Mo 2048 Mo (Chromium) ; 2560 Mo (Firefox, WebKit) 4096 Mo 4096 Mo
vCPU 2 2 4 4
TTL maximal 8 h 2 h 2 h 2 h
Fenêtre d’inactivité 45 min 15 min 15 min 15 min
Affichage au démarrage — 1280×800 1280×800 1080×1920
  • Les images fixes de la voie de l’agent sont plafonnées à 1280×800 ; la voie humaine négocie jusqu’à 2560×1600.
  • Le budget de mémoire résidente de l’hôte pour tous les rigs est par défaut de 12 Go, compté en mégaoctets résidents (une VM en veille conserve sa RAM). L’éviction suit la règle du moins récemment utilisé et ne prend jamais une machine que quelqu’un pilote, regarde ou dans laquelle un terminal est ouvert.
  • Un terminal ouvert ou une voie d’observation active empêche la mise en veille et la récupération pour inactivité du rig ; le TTL maximal s’applique toujours.
Port Où Réservé à
7911 Invité terminal Le multiplexeur de ports par lequel passe chaque transfert
9222, 9223 Invité navigateur Le relais et le point de terminaison d’automatisation (WebKit se lie directement à 9222 et n’utilise aucun relais)
80, 443 Boucle locale de l’invité navigateur Les voies HTTP/HTTPS des domaines de développement
  • La découverte interroge côté serveur les ports TCP en écoute de l’invité terminal toutes les ~4 s.
  • Au plus 16 ports sont transférés automatiquement par rig ; au-delà, ils sont listés dans le journal sans être transférés.
  • Le transfert côté hôte se lie à la boucle locale, en préférant le numéro de port de l’invité lui-même ; Partager sur le réseau local ajoute un port LAN attribué par le système.
  • Les domaines de développement doivent correspondre à name.test ou name.localhost (en minuscules, étiquette de 63 caractères au maximum). Un domaine correspond à un port.
  • Le HTTPS des domaines de développement utilise une autorité de certification locale au serveur (EC P-256, sous <dataDir>/rigs/tls/, clés en 0600 et jamais dans un invité) avec un certificat feuille générique pour *.test / *.localhost. Sans openssl sur l’hôte, pas de voie HTTPS ; le HTTP simple continue d’être routé.
Surface Liste d’autorisation par défaut
Terminal Le socle développeur du bac à sable (forges, registres de paquets, fournisseurs de modèles) + les miroirs apt d’Ubuntu + l’hôte de forge de l’arbre de travail lui-même
Navigateur usectrl.dev (le site du produit) — plus les archives Debian au premier démarrage de chaque moteur, pendant qu’il installe ses paquets. La page d’accueil est un document file:// local et n’a besoin d’aucune sortie réseau
Ordinateur Vide — tout refuser — jusqu’à ce qu’un appelant accorde des hôtes
Mobile Non appliquée (réseau géré par l’émulateur)

Chaque microVM reçoit en outre les hôtes de registre de son image, pour le tirage uniquement. L’ensemble d’hôtes autorisés du courtier d’identifiants est dérivé de la même liste d’autorisation : aucun jeton n’est émis pour un hôte que l’invité ne peut pas atteindre.

Le copier-coller dans les deux sens, ainsi que le glisser-déposer de fichiers dans un sens comme dans l’autre. Disponible pour une personne qui pilote un rig ; un agent obtient le presse-papiers via les verbes ci-dessus et aucun transfert de fichiers (il dispose déjà d’un shell à l’intérieur de l’enceinte).

Le franchissement du presse-papiers se fait par utilisateur et par sens. Le collage de l’hôte vers le rig est autorisé par défaut ; la copie du rig vers l’hôte demande confirmation (une autorisation de dix minutes, ou Toujours autoriser sous Paramètres → Serveur → Rigs → Accès au presse-papiers). Les deux choix sont indépendants et ne constituent jamais un réglage d’espace de travail.

Voie Route Transporte
Presse-papiers GET/POST /rig/clipboard/<rigId> Du texte, un PNG/JPEG (≤16 Mo) et les chemins invité des fichiers présents dans le presse-papiers
Fichiers POST /rig/files/<rigId> Jusqu’à 64 fichiers, ≤256 Mo chacun et ≤512 Mo par dépôt, avec un point de dépôt optionnel en pixels de l’invité
Fichiers GET /rig/files/<rigId>?p=<base64url path> Les octets d’un fichier, en sortie
  • HTTP, pas RPC. La socket RPC ferme la connexion sur une trame entrante de plus de 256 Ko — une protection anti-DoS adaptée aux commandes JSON et fatale pour une capture d’écran collée. Les octets empruntent le même genre de route que celle déjà utilisée par les trames des rigs et l’audio des réunions.
  • L’authentification repose sur la cible signée rig-files:<workspaceId>/<rigId> plus l’appartenance à l’espace de travail — une cible à part, afin qu’une URL émise pour regarder une machine ne puisse pas aussi y écrire des fichiers.
  • La prise de contrôle s’applique. L’écriture dans le presse-papiers et le dépôt de fichiers sont des actions modificatrices et sont refusées tant que quelqu’un d’autre détient le contrôle ; la lecture du presse-papiers et l’extraction d’un fichier relèvent de l’observation et ne le sont pas.
  • Où atterrissent les dépôts : ~/Drops (bureau), ~/drops (terminal — à côté de l’arbre de travail, jamais dedans, pour qu’un dépôt n’apparaisse jamais comme une modification non suivie), /tmp/cc-drops (navigateur). Le serveur choisit le répertoire ; un appelant ne peut pas le nommer. Les fichiers existants ne sont jamais écrasés — un nom en collision arrive sous la forme 2-name.ext.
  • Ce que l’on annonce à l’invité : une page de navigateur reçoit un vrai événement de dépôt au point demandé (Input.dispatchDragEvent) ; un bureau reçoit les fichiers et leurs URI dans son presse-papiers, car aucun hôte ne peut synthétiser un glisser XDND vers une boîte à outils arbitraire sans un démon privilégié dans l’invité. La réponse indique laquelle des deux possibilités s’est produite.
  • Le glisser vers l’extérieur ne concerne que le bureau et lit la XdndSelection de l’invité pendant qu’un glisser est en cours. Une charge utile identique à ce que l’invité détenait déjà au début de l’appui est traitée comme une sélection obsolète d’un glisser terminé, et non comme une nouvelle — X n’a pas d’étape « le glisser est terminé ».
  • Exigences relatives à l’image : l’image de bureau doit être en protocole 2 ou ultérieur (construite après l’arrivée de la prise en charge du presse-papiers). Une image plus ancienne répond 404 et l’application invite à la reconstruire plutôt que de signaler une machine en panne.
  • Le journal d’audit consigne la forme, jamais le contenu : les lignes clipboard_read, clipboard_write, drop_files et read_file portent des compteurs, des tailles et des chemins. Un presse-papiers est l’endroit où des identifiants vivent quelques secondes à la fois, et rien de tout cela n’est écrit.
  • Les outils de rig déclarent la classe d’actions enclosureControl (plus networkEgress et processSpawn sur les outils d’utilisation ; mobile_use et ios_use déclarent aussi packageInstall). rig_close déclare enclosureControl ; rig_list n’en déclare aucune. Autorisée par défaut ; les modes en lecture seule la refusent catégoriquement ; le curseur d’autonomie par espace peut la ramener à « demander d’abord ».
  • Événements de domaine : RigClosedEvent (avec son motif de fermeture), RigControlChanged (prise de contrôle / restitution), RigReaped (éviction pour inactivité, TTL ou mémoire, avec l’agent pilote). L’ouverture d’un rig n’est pas un événement. Voir événements de domaine.
  • Stockage : rig_sessions et rig_action_log dans la base de données de chaque espace de travail. Le journal d’actions consigne chaque événement d’entrée avec son principal et une séquence monotone par rig ; le texte saisi est réduit à une longueur et un hachage avant d’être écrit.
  • Répertoire de données : rigs/images/ (images qcow2 importées), rigs/smolvm/ (état d’exécution par rig), rigs/smolvm-packs/ (packs de machines pré-extraits), rigs/tls/ (l’autorité de certification des domaines de développement), rigs/run/ (surcouches et amorces QEMU).
  • Routes : la vue en direct est un onglet dans un espace ou une page de PR ; /settings/server/rigs est l’écran de réglages des Rigs.