Salta ai contenuti

Rig

Solo fatti. Per il modello che sta dietro, leggi Rig e recinti; per aprire una macchina, segui la guida.

Superficie Guida Backend Valore Uscita in rete contenuta
Terminale (exec) Una shell sul canale exec della VM microVM smolvm Sì
Browser Chromium headless (CDP), Firefox (WebDriver BiDi) o WebKit (W3C WebDriver) microVM smolvm Sì
Computer Desktop X11: input tramite l’hypervisor, cattura tramite l’agente del guest QEMU qemu-hvf (macOS), qemu-kvm (Linux), qemu-tcg (fallback non accelerato, mai scelto automaticamente) Sì
Android Android tramite ADB L’emulatore di Google android-emulator No — l’emulatore gestisce la propria rete
iOS Simulatore iOS tramite WebDriverAgent CoreSimulator ios-simulator No — la rete segue il Mac host

Il terminale e la superficie computer_use condividono il valore di superficie computer; il terminale si distingue per il marcatore dell’immagine exec e non viene mai riutilizzato come desktop pilotabile (né viceversa). Una conversazione riutilizza una sola macchina del terminale.

Motore Valore Protocollo Vista dal vivo Fermoimmagine
Chromium chromium (predefinito) Chrome DevTools Protocol Screencast — il browser invia i frame mentre la pagina si ridisegna JPEG
Firefox firefox WebDriver BiDi Fermoimmagine per polling (BiDi non ha screencast), limitati a 6 fps JPEG
WebKit webkit W3C WebDriver (classico) Fermoimmagine per polling, transcodificati dall’ffmpeg dell’host PNG

Un rig esegue un solo motore per tutta la sua vita. Una conversazione può contenere un rig per motore, ed è questo che rende possibile il confronto affiancato: aprire «Firefox (VM)» accanto a «Chromium (VM)» significa due macchine, e chiuderne una lascia l’altra in esecuzione.

Passa engine a rig.open o allo strumento browser_use. Un nome sconosciuto viene rifiutato invece di essere sostituito con quello predefinito: chiedere un browser e riceverne in silenzio un altro risponde alla domanda sulla compatibilità con la risposta sbagliata.

Limiti specifici di ciascun motore, dichiarati invece che aggirati:

  • Firefox non ha un ricaricamento che aggira la cache (rifiuta del tutto ignoreCache di BiDi), quindi un ricaricamento forzato è un ricaricamento normale.
  • WebKit non ha neppure un ricaricamento che aggira la cache, non ha un flusso della console (l’output viene catturato intercettando console.* dopo ogni navigazione, quindi tutto ciò che viene registrato durante il caricamento va perso — ogni estrazione della console lo dichiara), e richiede ffmpeg sull’host per la sua vista dal vivo.
  • Nessuno dei due espone un albero di accessibilità della piattaforma sul proprio protocollo, quindi extract (a11y) restituisce un’approssimazione derivata dal DOM, etichettata come tale.
  • Nessuno dei due può ricevere un vero rilascio di file; un campo di input file può comunque essere puntato a percorsi interni alla macchina.
Superficie Immagine Recuperata
Terminale ubuntu:24.04, fissata per digest Automaticamente al primo avvio, tramite la rete controllata della macchina stessa
Browser (Chromium, Firefox, WebKit) debian:trixie-slim, fissata per digest, più i pacchetti del motore stesso Automaticamente al primo avvio, poi messa in cache come pacchetto preriscaldato
Computer Un’immagine desktop qcow2 con Chromium, Firefox, WebKit e la stessa base CLI da runner ospitato su GitHub che il terminale preriscalda Importa in Impostazioni → Server → Rig (download fissati per checksum dove pubblicati)
Android Le immagini di sistema di Google Tramite l’SDK Android
iOS Un runtime del simulatore Xcode più WebDriverAgent fissato per checksum Runtime tramite Xcode; bridge di automazione da Impostazioni → Server → Rig

Dopo il primo avvio di ciascun tipo di microVM il server mette in cache una copia già estratta nella directory dei dati e avvia da lì ogni macchina successiva, comprese le immagini preriscaldate con i pacchetti già installati. Il terminale preriscalda un set di strumenti CLI in stile runner ospitato su GitHub (git, gcc, python3, jq, rsync, strumenti per archivi e il resto della base apt; non Docker né le cache degli strumenti dei linguaggi). Il qcow2 del Computer incorpora gli stessi nomi di CLI al momento della build dell’immagine, accanto a XFCE e ai tre browser. Ogni browser preriscalda il proprio motore. Tutti e tre i motori condividono una base Debian ma non condividono mai un pacchetto: la cache è indicizzata per immagine, motore e revisione audio (audio-v1-<engine>), perché una macchina preriscaldata per un motore — o prima che PulseAudio e ffmpeg facessero parte del carico di lavoro — non contiene nulla dell’altro. Il pacchetto del terminale è indicizzato per revisione del set di strumenti (tools-v1) per lo stesso motivo: un pacchetto preriscaldato prima che il compilatore facesse parte dell’immagine non deve servire una macchina che dovrebbe averlo.

Non esiste un’immagine di automazione preconfezionata che valga la pena usare: chromedp/headless-shell di Chromium esclude in compilazione sia ALSA sia PulseAudio, quindi non può mai emettere audio; l’unica per Firefox mantenuta è 33× la dimensione della base Debian e non include comunque socat; per WebKit non ne esiste affatto una mantenuta. Quindi ogni motore si installa al primo avvio (chromium, firefox-esr o webkit2gtk-driver) e, mentre lo fa, raggiunge gli archivi Debian (deb.debian.org, security.debian.org, cloudfront.debian.net). Chromium è il binario della distribuzione avviato come chromium --headless=new.

Chiave di impostazione Sostituisce
rigs.smolvm.execImage L’immagine del Terminale (VM)
rigs.smolvm.browserImage L’immagine del Browser (VM) Chromium

Scritta tramite l’archivio delle impostazioni dello spazio di lavoro riservato agli amministratori (Impostazioni → Server → Rig → Immagini personalizzate (questo spazio di lavoro)). Il valore deve essere un riferimento di registry — percorsi locali, archivi e stringhe che assomigliano a flag vengono rifiutati, sia nel campo sia di nuovo all’avvio. Un valore memorizzato non valido viene ignorato in modo visibile e al suo posto si avvia quello predefinito. Il registry dell’immagine stessa (più i noti companion blob-CDN per Docker Hub, GHCR, Quay, GitLab e GCR) viene ammesso attraverso il varco dell’uscita in rete per il pull.

Contratto dell’immagine del terminale — bash presente; git, curl, socat preinstallati oppure una base apt Debian/Ubuntu (il recinto installa quei tre più un set di strumenti CLI in stile runner ospitato su GitHub al primo avvio: compilatori, Python 3, jq, strumenti per archivi, rsync. Le cache degli strumenti dei linguaggi e Docker non vengono installati).

Contratto dell’immagine del browser — una base apt Debian/Ubuntu (o i pacchetti già installati) con bash e socat, più un binario chromium che il server avvia da sé come chromium --headless=new con DevTools sul loopback del guest 9223, inoltrato alla porta del guest 9222. PulseAudio e ffmpeg vengono installati al primo avvio per la corsia audio. Non costruire FROM chromedp/headless-shell: headless.gn a monte esclude in compilazione sia ALSA sia PulseAudio, quindi quel binario non può emettere audio. La sostituzione si applica solo ai rig Chromium — il comando del carico di lavoro che avvia è quello di Chromium, e passarlo a Firefox o WebKit avvierebbe un binario che non c’è. Un rig Firefox o WebKit la ignora in modo visibile e avvia la propria immagine fissata.

Porte di automazione — ogni macchina browser inoltra esattamente una porta di loopback dell’host alla porta 9222 del guest. Dietro di essa: DevTools di Chromium e l’agente remoto di Firefox stanno sul loopback del guest 9223 con un relay socat davanti (entrambi si legano al loopback incondizionatamente); il driver di WebKit accetta un --host e si lega direttamente alla NIC del guest. Firefox inoltre convalida l’header Host rispetto alla propria porta, quindi il client invia l’authority lato guest anziché quella che ha contattato.

Ogni azione viene analizzata in modo totale: un payload malformato restituisce un messaggio che nomina il campo, mai un’eccezione. I verbi di osservazione (contrassegnati con ○) restano consentiti mentre una persona detiene il controllo; i verbi mutativi vengono rifiutati.

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.

Le coordinate sono pixel del guest nella modalità corrente del guest; ogni screenshot indica la dimensione del display. Le combinazioni di tasti usano la grafia X11 (ctrl+s, alt+Tab, Return).

clipboard_read accetta un selection: clipboard (ciò che riempie ctrl+C, il valore predefinito), primary (selezione-per-copiare di X11) o xdnd (un trascinamento in corso). Un risultato vuoto è una risposta normale per tutti e tre. clipboard_write accetta solo text — immagini e file viaggiano sulla corsia dei file, perché gli argomenti di un’azione vengono memorizzati nel log di audit e i byte delle immagini non hanno nulla a che fare lì. Il contenuto degli appunti torna delimitato come dato non attendibile.

navigate (solo http/https), reload (hard opzionale), stop_loading, click (selettore o coordinata), fill, type, key (nomi dei tasti DOM), scroll, mouse_move, drag, left_mouse_down, left_mouse_up, extract○ (a11y | dom | console), screenshot○ (full_page opzionale), set_viewport (min 320×240), history, wait_for○ (selettore, 0,1–30 s), clipboard_read○, clipboard_write.

Il contenuto estratto della pagina torna delimitato come dato non attendibile.

Il browser ha esattamente un blocco di appunti, quindi clipboard_read non accetta selection. Entrambi i verbi degli appunti richiedono un contesto sicuro: una pagina servita su http:// semplice non ha alcuna API degli appunti, e il verbo lo dichiara invece di segnalare appunti vuoti — lì usa type, che inserisce al cursore senza passare dagli appunti.

tap, swipe (50–5000 ms), type, key (alias back, home, recents, enter, delete, backspace, tab, escape, search, menu, volume_up, volume_down, power, o qualsiasi KEYCODE_*), screenshot○, ui_dump○, install_apk (limitato al worktree del rig e alla directory dei dati del server), start_app, stop_app, clear_app_data, uninstall_app, open_url e shell. shell accetta un elenco argv e viene eseguito dentro Android, mai tramite una shell dell’host; copre diagnostiche per sviluppatori come logcat, dumpsys e pm grant.

mobile_use non ha verbi per gli appunti. Da Android 10 solo l’app in primo piano può leggere gli appunti.

tap, swipe (50–5000 ms), type, key (enter, backspace, tab, escape, i tasti freccia o un singolo carattere stampabile; modificatori opzionali command, control, option e shift), home, lock, unlock, screenshot○, ui_dump○, install_app (una directory .app del simulatore limitata al worktree del rig o alla directory dei dati del server), start_app, stop_app, uninstall_app, open_url e spawn. spawn accetta argv ed esegue dentro il simulatore tramite simctl; non invoca mai una shell dell’host.

Le coordinate sono punti dello schermo iOS, non pixel dello screenshot. Il flusso dal vivo mantiene la dimensione fissa del simulatore e lo incornicia con bande nere invece di ridimensionare il dispositivo. Il trasferimento di testo negli appunti dall’host al simulatore e viceversa usa la stessa policy di autorizzazione per utente e per direzione degli altri rig; file e immagini non sono accettati dalla corsia degli appunti di iOS.

Ogni rig crea un dispositivo CoreSimulator e una sessione WebDriverAgent di cui è proprietario. Chiuderlo ferma WebDriverAgent, spegne ed elimina quel dispositivo e rimuove la sua voce dal registry. I rig iOS vengono eseguiti solo su un server macOS con Xcode, un runtime iOS installato e il bridge di automazione fissato. Non espongono audio, microfono, rilascio di file, shell dell’host né alcuna garanzia di rete isolata.

Strumento Descrizione
rig_list Elenca i rig di uno spazio di lavoro con superficie, stato, display e controller
rig_close Distrugge un rig e ne scarta il disco
Fase Significato
provisioning In avvio; il dettaglio di stato riporta il passo corrente alla lettera
ready Raggiungibile e in grado di accettare azioni
parked In pausa per inattività: vCPU ferme, RAM ancora residente; l’azione successiva la risveglia
closing In spegnimento
closed Sparito; la riga sopravvive per il log di audit
failed Morto o mai avviato; distinguibile così l’interfaccia può offrire un nuovo tentativo
Motivo di chiusura Attivato quando
requested Una persona o un agente l’ha chiesto
idleTimeout Inattivo oltre 2× la finestra di inattività, o espulso per memoria
ttlExpired La durata massima è scaduta
conversationEnded La conversazione proprietaria è terminata
workspaceGone Lo spazio di lavoro è stato eliminato o l’appartenenza è andata persa
serverShutdown Il server si sta arrestando
backendFailure L’hypervisor è morto sotto di essa
Valore Terminale Browser Computer Mobile
Memoria 512 MB 2048 MB (Chromium); 2560 MB (Firefox, WebKit) 4096 MB 4096 MB
vCPU 2 2 4 4
TTL rigido 8 h 2 h 2 h 2 h
Finestra di inattività 45 min 15 min 15 min 15 min
Display all’avvio — 1280×800 1280×800 1080×1920
  • I fermoimmagine della corsia dell’agente sono limitati a 1280×800; la corsia umana negozia fino a 2560×1600.
  • Il budget di memoria residente dell’host per tutti i rig è di 12 GB per impostazione predefinita, contato in megabyte residenti (una VM in pausa conserva comunque la sua RAM). L’espulsione è least-recently-used e non sottrae mai una macchina che qualcuno sta guidando, guardando o in cui ha un terminale aperto.
  • Un terminale o una corsia di osservazione aperti bloccano il rig impedendone la sosta e la raccolta per inattività; il TTL rigido si applica comunque.
Porta Dove Riservata a
7911 Guest del terminale Il multiplexer delle porte su cui viaggia ogni inoltro
9222, 9223 Guest del browser Il relay e l’endpoint di automazione (WebKit si lega direttamente a 9222 e non usa relay)
80, 443 Loopback del guest del browser Le corsie HTTP/HTTPS del dominio di sviluppo
  • La scoperta interroga le porte TCP in ascolto del guest del terminale ogni ~4 s, lato server.
  • Al massimo 16 porte vengono inoltrate automaticamente per rig; le altre sono elencate nel log, non inoltrate.
  • L’inoltro lato host si lega al loopback, preferendo il numero di porta del guest stesso; Condividi sulla rete locale aggiunge una porta LAN assegnata dal sistema operativo.
  • I domini di sviluppo devono corrispondere a name.test o name.localhost (minuscolo, etichetta di massimo 63 caratteri). Un dominio mappa su una porta.
  • L’HTTPS del dominio di sviluppo usa una CA locale del server (EC P-256, sotto <dataDir>/rigs/tls/, chiavi 0600 e mai in un guest) con un unico certificato foglia wildcard per *.test / *.localhost. Nessun openssl sull’host significa nessuna corsia HTTPS; l’HTTP semplice viene comunque instradato.
Superficie Allowlist predefinita
Terminale La base da sviluppatore del sandbox (forge, registry dei pacchetti, fornitori di modelli) + i mirror apt di Ubuntu + l’host forge del worktree stesso
Browser usectrl.dev (il sito del prodotto) — più gli archivi Debian al primo avvio di ogni motore, mentre installa i suoi pacchetti. La pagina iniziale è un documento file:// locale e non richiede alcuna uscita in rete
Computer Vuota — nega tutto — finché un chiamante non concede host
Mobile Non applicata (rete gestita dall’emulatore)

Ogni microVM riceve inoltre gli host del registry della sua immagine, solo per il pull. L’insieme degli host consentiti del broker delle credenziali deriva dalla stessa allowlist: nessun token viene coniato per un host che il guest non può raggiungere.

Copia e incolla in entrambe le direzioni, e trascinamento di file dentro e fuori. Disponibile a una persona che guida un rig; un agente ottiene gli appunti attraverso i verbi precedenti e nessun trasferimento di file (ha già una shell dentro il recinto).

Il passaggio degli appunti è per utente e per direzione. L’incolla dall’host al rig è consentito per impostazione predefinita; la copia dal rig all’host chiede conferma (un’autorizzazione di dieci minuti, oppure consenti sempre in Impostazioni → Server → Rig → Accesso agli appunti). Le due scelte sono indipendenti e non sono mai un’impostazione dello spazio di lavoro.

Corsia Percorso Trasporta
Appunti GET/POST /rig/clipboard/<rigId> Testo, un PNG/JPEG (≤16 MB) e i percorsi nel guest dei file negli appunti
File POST /rig/files/<rigId> Fino a 64 file, ≤256 MB ciascuno e ≤512 MB per rilascio, con un punto di rilascio opzionale in pixel del guest
File GET /rig/files/<rigId>?p=<base64url path> I byte di un file, in uscita
  • HTTP, non RPC. Il socket RPC chiude una connessione su un frame in ingresso superiore a 256 KB: una protezione anti-DoS giusta per i comandi JSON e fatale per uno screenshot incollato. I byte viaggiano sullo stesso tipo di percorso già usato dai frame dei rig e dall’audio delle riunioni.
  • L’autenticazione è il target firmato rig-files:<workspaceId>/<rigId> più l’appartenenza allo spazio di lavoro — un target a sé, quindi un URL coniato per guardare una macchina non può anche scrivervi file.
  • La presa in carico si applica. Scrivere negli appunti e rilasciare file sono operazioni mutative e vengono rifiutate mentre qualcun altro detiene il controllo; leggere gli appunti e prelevare un file sono osservazione e non lo sono.
  • Dove finiscono i rilasci: ~/Drops (desktop), ~/drops (terminale — accanto al worktree, mai al suo interno, così un rilascio non appare mai come modifica non tracciata), /tmp/cc-drops (browser). La directory la sceglie il server; un chiamante non può nominarla. I file esistenti non vengono mai sovrascritti — un nome in conflitto finisce come 2-name.ext.
  • Cosa viene detto al guest: una pagina del browser riceve un vero evento di drop nel punto richiesto (Input.dispatchDragEvent); un desktop riceve i file più i loro URI nei propri appunti, perché nessun host può sintetizzare un trascinamento XDND in un toolkit arbitrario senza un demone privilegiato dentro il guest. La risposta indica quale dei due casi si è verificato.
  • Il trascinamento verso l’esterno è solo per il desktop e legge la XdndSelection del guest mentre un trascinamento è in corso. Un payload identico a ciò che il guest aveva già quando è iniziata la pressione viene trattato come una selezione obsoleta di un trascinamento concluso, non come uno nuovo — X non ha un passaggio «il trascinamento è finito».
  • Requisiti dell’immagine: l’immagine desktop deve essere protocollo 2 o successivo (costruita dopo l’introduzione del supporto agli appunti). Un’immagine più vecchia risponde 404 e l’app dice di ricostruirla invece di segnalare una macchina guasta.
  • Il log di audit registra la forma, mai il contenuto: le righe di clipboard_read, clipboard_write, drop_files e read_file riportano conteggi, dimensioni e percorsi. Gli appunti sono il posto in cui le credenziali vivono qualche secondo alla volta, e nulla di ciò viene scritto.
  • Gli strumenti dei rig dichiarano la classe di azioni enclosureControl (più networkEgress e processSpawn sugli strumenti use; mobile_use e ios_use dichiarano anche packageInstall). rig_close dichiara enclosureControl; rig_list non ne dichiara alcuna. Consentita per impostazione predefinita; le modalità di sola lettura la negano in blocco; la manopola di autonomia per spazio può declassarla a richiesta preventiva.
  • Eventi di dominio: RigClosedEvent (con il suo motivo di chiusura), RigControlChanged (presa in carico / restituzione), RigReaped (inattività, TTL o espulsione per memoria, con l’agente che la guidava). L’apertura di un rig non è un evento. Vedi eventi di dominio.
  • Archiviazione: rig_sessions e rig_action_log nel database di ogni spazio di lavoro. Il log delle azioni registra ogni evento di input con il suo principal e una sequenza monotona per rig; il testo digitato viene ridotto a una lunghezza e a un hash prima di essere scritto.
  • Directory dei dati: rigs/images/ (immagini qcow2 importate), rigs/smolvm/ (stato di runtime per rig), rigs/smolvm-packs/ (pacchetti macchina già estratti), rigs/tls/ (la CA del dominio di sviluppo), rigs/run/ (overlay e seed di QEMU).
  • Percorsi: la vista dal vivo è una scheda in una pagina di spazio o di PR; /settings/server/rigs è la schermata delle impostazioni dei Rig.