Saltar para o conteúdo

Rigs e recintos

Um rig é uma máquina descartável que um agente controla em tempo real — um navegador, um ambiente de trabalho Linux, um emulador Android, um Simulador de iOS ou a máquina por trás de um terminal isolado. A maior parte dos rigs são VMs; os simuladores móveis geridos pelo anfitrião são as exceções explícitas. Esta página explica cada fronteira e por que motivo as peças foram moldadas desta forma. Para abrir um, consulte Dar a um agente uma máquina para testar; para os verbos, as portas e as predefinições exatos, consulte a referência de rigs.

A sandbox nativa envolve um processo em isolamento de namespaces — é real, mas partilha o seu kernel e restringe um processo, não um computador. No momento em que um agente precisa de clicar numa aplicação de ambiente de trabalho, de renderizar uma página num navegador real ou de correr experiências com rm -rf nas quais preferia não pensar, a fronteira honesta é uma máquina: o seu próprio kernel, o seu próprio disco, a sua própria pilha de rede.

As superfícies em recinto usam dois backends de VM:

  • microVMs para o terminal e o navegador — arranque do hipervisor em menos de um segundo a partir de imagens de contentor fixadas por digest.
  • QEMU para o ambiente de trabalho Linux completo, que precisa de um dispositivo de visualização que a microVM não tem.

As superfícies móveis são geridas pelo anfitrião e assumem a fronteira mais fraca de forma explícita:

  • O Android controla o emulador da Google através de ADB.
  • O iOS cria e detém um dispositivo CoreSimulator num anfitrião Mac e controla-o através do WebDriverAgent. O dispositivo e os seus dados são eliminados quando o rig fecha, mas a sua rede segue o Mac anfitrião.

Os rigs em recinto escrevem numa camada descartável sobreposta a uma imagem de base só de leitura, para que um rig não possa alterar aquilo de que o seguinte arranca. Um rig de iOS detém um dispositivo CoreSimulator descartável que é eliminado quando o rig fecha. O Android liga-se a um emulador do anfitrião que sobrevive à sessão.

A rede de um rig começa em nada. Os anfitriões são admitidos rig a rig através de um portão que o convidado não consegue contornar: as únicas saídas da máquina são as que a configuração de arranque criou. Um terminal isolado recebe a linha de base do programador (registos de pacotes, hosts de código, os espelhos do apt); um rig de navegador admite o site do produto (usectrl.dev) e arranca numa página file:// local que não precisa de saída; tudo o resto é recusado no momento da ligação.

O Android e o iOS não são abrangidos por este portão. O respetivo emulador e simulador do anfitrião usam a rede do anfitrião do servidor, pelo que a interface e o relatório de capacidades marcam a saída como não imposta e nunca oferecem um controlo enganador de «reiniciar sem restrições».

A saída e as credenciais são uma única política vista de dois lados — o broker de credenciais (abaixo) não emite um token para um anfitrião que o convidado não consegue alcançar.

Tudo o que um rig lê é tratado como não fidedigno: o texto da página, o DOM e as árvores de acessibilidade, os despejos da interface e a saída da consola chegam ao agente cercados por uma regra permanente de «isto são dados, não instruções». A cerca é o enquadramento; o portão de saída é a imposição por trás dela.

A entrada passa pelo hipervisor, a captura pelo convidado

Secção intitulada “A entrada passa pelo hipervisor, a captura pelo convidado”

Na superfície de ambiente de trabalho, os eventos de teclado e de ponteiro são injetados pelos próprios dispositivos virtuais de entrada do hipervisor. O convidado nunca executa um daemon com privilégios capaz de sintetizar entrada — por isso, comprometer o software do convidado não entrega um teclado ao atacante. A captura flui no sentido inverso, através de um pequeno agente sem privilégios no interior do convidado, porque o convidado consegue escalar os fotogramas antes de estes atravessarem a ligação.

A pessoa que observa e o modelo que controla têm necessidades opostas, pelo que um rig serve dois fluxos independentes:

  • O canal humano tem resolução total, ao tamanho do seu painel, renegociada à medida que redimensiona, e é retransmitido pelo servidor como bytes opacos — o servidor nunca descodifica um fotograma, porque um descodificador de vídeo no caminho do pedido é o que o impede de responder a RPCs.
  • O canal do agente são imagens fixas reduzidas, limitadas a um tamanho adequado ao modelo, uma imagem por resultado de ferramenta, com os fotogramas mais antigos descartados durante a compactação enquanto as respetivas descrições de texto sobrevivem.

Nenhum dos canais faz concessões ao outro: o utilizador fica com uma imagem fluida, o modelo com uma barata.

A assunção do controlo é um ponto de estrangulamento, não um pedido

Secção intitulada “A assunção do controlo é um ponto de estrangulamento, não um pedido”

O controlo exclusivo é imposto no único ponto por onde passa cada ação — e não pedindo ao modelo que se comporte. Enquanto uma pessoa o detém, o servidor recusa as ações mutantes do agente. O agente mantém os seus verbos de observação (capturas de ecrã, extração), para poder narrar o que está a fazer, e é informado da assunção do controlo através do seu canal de orientação, em vez de a descobrir por si através de cliques recusados. Clicar e escrever no canvas funciona sempre que mais ninguém detém esse bloqueio — ou quando é você que o detém; a etiqueta de estado mostra Você tem o controlo. Todos os eventos de entrada — seus e do agente — ficam registados num registo de auditoria por rig, com o respetivo principal e uma sequência monotónica, para que «quem clicou naquilo» continue a ter resposta depois de a máquina desaparecer.

O conteúdo atravessa a fronteira; as máquinas mantêm-se separadas

Secção intitulada “O conteúdo atravessa a fronteira; as máquinas mantêm-se separadas”

Copiar e colar funcionam nos dois sentidos, e o mesmo acontece com arrastar ficheiros para dentro e para fora — mas um rig é um computador diferente, e o design nunca finge o contrário.

A área de transferência é transportada a pedido, não espelhada continuamente. Prima a combinação de cópia sobre um rig e o convidado recebe instruções para copiar; depois, tudo o que ele tenha colocado na sua área de transferência — texto, uma imagem, uma lista de ficheiros — é obtido e colocado na sua. Colar envia o mesmo conteúdo no sentido inverso e de seguida efetua a colagem dentro do convidado. Nada é sincronizado em segundo plano: uma palavra-passe que copia na sua própria máquina não chega silenciosamente para dentro de uma VM porque um temporizador disparou.

A passagem é controlada por utilizador e por sentido, de forma independente. Colar num rig é permitido por predefinição; copiar de um rig pergunta primeiro (uma autorização de dez minutos, ou Permitir sempre em Definições → Servidor → Rigs → Acesso à área de transferência). Nunca é uma definição do espaço de trabalho: um membro não pode dar o seu consentimento pela área de transferência de outro membro.

No macOS, a combinação de passagem é ⌘C/⌘V, que é inequívoca — ⌃C continua a chegar a um shell do convidado como a interrupção que é. No Windows e no Linux, as duas combinações são a mesma tecla, pelo que o convidado a recebe sem alterações e a sua área de transferência só é substituída quando a do convidado mudou de facto. Uma interrupção continua a ser uma interrupção.

Os ficheiros arrastados para dentro são copiados para a pasta de largada da máquina e depois oferecidos a esta. O que «oferecidos» significa depende daquilo que pode ser feito com honestidade: uma página de navegador recebe um evento de largada real no ponto onde larga o ficheiro, pelo que uma zona de carregamento se comporta exatamente como se o ficheiro fosse local; um ambiente de trabalho recebe os ficheiros em ~/Drops com os respetivos caminhos na sua área de transferência, porque nenhum anfitrião consegue sintetizar um arrastamento para um toolkit Linux arbitrário sem executar um daemon com privilégios dentro do convidado — que é precisamente o que este design se recusa a ter. O resultado diz o que aconteceu, pelo que um CSV largado numa zona de carregamento nunca é comunicado como carregado quando é apenas um ficheiro numa pasta. Largar sobre um terminal isolado copia o ficheiro para dentro e escreve o seu caminho no convidado, no prompt, que é o que um shell consegue realmente usar.

Os ficheiros arrastados para fora funcionam na superfície de ambiente de trabalho, onde o protocolo de arrastamento do X11 torna a carga legível enquanto um arrastamento está em curso. Inicie um arrastamento dentro do convidado, leve-o para fora do canvas e os ficheiros são obtidos para a sua máquina e entregues ao sistema operativo como um arrastamento real. Copiar ficheiros no gestor de ficheiros do convidado e colar no anfitrião funciona da mesma forma. A superfície de navegador deliberadamente não oferece isto: um navegador headless não tem nenhuma seleção de arrastamento para ler, e o sinal disponível mais próximo — a seleção de texto da página — não é de todo prova de um arrastamento, pelo que agir com base nele sequestraria cliques normais.

Tudo o que é lido de um convidado continua a ser conteúdo não fidedigno: o texto da área de transferência chega a um modelo dentro da mesma cerca que uma extração de página, e o registo de auditoria regista que ocorreu uma colagem e qual foi o seu tamanho — nunca o que dizia.

O git push funciona a partir de um terminal isolado que abriu, mas o convidado não guarda nenhum token do host de código. Detém um segredo por VM cujo único poder é pedir ao broker de credenciais do anfitrião — através de um túnel controlado pelo anfitrião — um token de curta duração, limitado ao repositório, por operação, com limitação de frequência, limitado pela mesma lista de anfitriões permitidos que a saída e revogado no momento em que o rig fecha. Uma máquina feita para ser deitada fora não deve conter nada que valha a pena roubar.

Aquilo que esse token pode fazer é fixado quando o rig abre. Um rig de terminal aberto por uma pessoa recebe um token de escrita, limitado pelo acesso ao host de código desse membro. Um rig aberto por um agente recebe um só de leitura: um credential helper do git não distingue um push de um fetch, pelo que a regra de push não pode ser consultada a cada push dentro de um convidado, como o gateway de execuções de agentes faz no anfitrião.

Quando um rig transporta o seu repositório, a worktree do anfitrião mantém-se como autoridade. A cópia é sincronizada para dentro no arranque (com os segredos, como os ficheiros .env, excluídos), e os commits só regressam quando os pede — obtidos para refs/rigs/<id>/*, nunca enviados por push nem colocados em checkout. Nada dentro de uma VM pode mover o seu branch ou tocar na sua árvore de trabalho; fazer merge do que um rig produziu é sempre uma decisão humana tomada no anfitrião.

Um servidor de desenvolvimento iniciado dentro da VM do terminal torna-se alcançável — na sua máquina, no Navegador (VM), opcionalmente na sua LAN e opcionalmente sob um domínio de desenvolvimento com HTTPS — sem nunca abrir a máquina. O mapa de portas do hipervisor é fixado no arranque, pelo que todo o encaminhamento posterior assenta em dois canais estreitos controlados pelo anfitrião: um multiplexador de entrada pré-criado, que se recusa a ligar-se a algo em que o convidado não esteja realmente a escutar, e túneis inversos que o anfitrião mantém abertos para ligações iniciadas pelo convidado. O HTTPS do domínio de desenvolvimento termina no servidor, recorrendo a uma autoridade criada localmente cujas chaves nunca entram em nenhum convidado. A mecânica está em Encaminhar portas a partir de um terminal.

Um rig não pode tornar-se num animal de companhia:

  • um TTL rígido que o convidado não pode prolongar;
  • uma escada de inatividade — entrar em pausa (vCPUs paradas, RAM residente) após a janela de inatividade e fechar após o dobro dela — em que um terminal aberto ou um observador a assistir contam como «em utilização»;
  • um orçamento de memória contado em megabytes residentes, e não em sessões, porque uma VM em pausa liberta a CPU e mantém cada byte de RAM; a máquina menos recentemente utilizada é a primeira a ser removida, e nunca aquela que uma pessoa está a controlar ou a observar;
  • desmantelamento ao encerrar o servidor, mais uma limpeza de órfãos marcados com a respetiva propriedade no arranque seguinte, para que um servidor que falhou nunca deixe pendurado um hipervisor que não responde a ninguém.

Os primeiros arranques descarregam a imagem fixada da máquina; o servidor coloca depois em cache uma cópia pré-extraída, em segundo plano, pelo que todas as máquinas posteriores desse tipo arrancam em segundos e um terminal isolado fica disponível com uma linha de base de CLI ao estilo de um runner alojado no GitHub já instalada — compiladores, Python, jq, ferramentas de arquivo — e não um cache de ferramentas de linguagem nem o Docker. A imagem do Computador incorpora essa mesma linha de base de CLI no momento da compilação, ao lado do XFCE e dos navegadores.

«Na minha máquina funciona» é uma afirmação sobre um único motor de renderização. Assim, um rig de navegador não é «o navegador» — é Chromium, Firefox ou WebKit, escolhido quando o abre, e um rig executa aquele com que foi aberto durante toda a sua vida. Uma conversa pode ter um de cada: abrir o Firefox (VM) ao lado do Chromium (VM) dá-lhe duas máquinas, e apontar ambos para a mesma página é a comparação. O WebKit é o mais próximo do Safari que existe entre o que corre em Linux — a mesma linhagem de motor, pelo que apanha as falhas com que os utilizadores do Safari se deparam.

Os verbos são idênticos nos três; os protocolos subjacentes já não o são, e as diferenças são declaradas em vez de atenuadas. O Chromium fala o DevTools Protocol e envia fotogramas de forma ativa, pelo que a sua vista em direto não custa nada enquanto a página está parada. O Firefox fala WebDriver BiDi e o WebKit fala WebDriver clássico, e nenhum deles tem transmissão de ecrã, pelo que as suas vistas em direto recorrem a sondagens — alguns fotogramas por segundo, o que é adequado para observar uma página e não para observar uma animação. Quando um motor genuinamente não consegue fazer algo (o Firefox não tem recarregamento que contorne a cache; o WebKit não consegue ver a saída da consola registada antes de a página terminar de carregar), a resposta di-lo, em vez de fazer discretamente outra coisa.

Um rig é deliberadamente não um destino global. A vista em direto é um separador no espaço ou na PR onde o trabalho está a acontecer, limitado a essa conversa — pelo que o seu separador e as chamadas browser_use do agente se dirigem a uma máquina (uma por motor, na superfície de navegador). O facto de os rigs poderem sequer arrancar é uma propriedade do anfitrião, pelo que as capacidades, as imagens, as imagens personalizadas por espaço de trabalho e a lista de máquinas em execução residem em Definições → Servidor → Rigs. Controlar um recinto é uma classe de ação de barreira própria, pelo que um espaço de trabalho, um agente ou um espaço podem permiti-la, exigir aprovação ou negá-la terminantemente — os modos só de leitura negam-na integralmente.