Passa al contenuto principale

Sandbox Docker

Obiettivo: eseguire comandi nella sandbox Docker e aprire solo la rete che un progetto richiede.

Prerequisiti​

  • Docker, raggiungibile dalla macchina che esegue il worker.
  • Un manifesto Project con sandbox.image impostato.

Passi​

  1. Esegui i comandi da un agente deep con execute: true, o da un nodo script. Entrambi usano il container che apre il plugin afe-docker (sandboxes/docker).

  2. Fai elencare a un agente acp i nomi delle variabili d'ambiente che gli servono. L'adapter Docker passa ognuna come -e NAME. Il valore si legge dall'ambiente del client docker, e non compare mai in un argv. Token della forge e chiavi del motore non entrano mai in una sandbox.

  3. Esegui un nodo con run: <Script name>. Esegue il command dello Script nella sandbox del progetto, in /workspace. L'artefatto nominato da writes si riempie da ok, exit_code e output, e dalle chiavi di un oggetto JSON sullo stdout. Un'uscita diversa da zero è un esito, non un fallimento.

  4. Usa run: builtin:merge_and_ci per committare il worktree di ogni lane, fondere i branch delle lane in quello di integrazione ed eseguire ci.script nella sandbox di integrazione. L'artefatto porta merged, conflicts, ci_passed e ci_output. Un conflitto di merge o una CI rossa è un esito su cui il flow instrada con when.

  5. Quando un progetto richiede la rete, dichiara sandbox.egress:

    sandbox:
    image: afe-sandbox-acp:test
    egress: ["openrouter.ai"]

    Il motore crea una rete interna, e avvia un proxy squid da docker/egress-proxy con l'allowlist in AFE_EGRESS_HOSTS. Lo collega alla bridge di default, e mette la sandbox sulla rete interna con HTTP_PROXY/HTTPS_PROXY che puntano al proxy. Escono solo gli host in lista. Ogni altro host è rifiutato, e così una connessione diretta che ignora il proxy.

Il container​

Di default una sandbox non ha rete (--network none). Il container è:

  • un container per ticket e scope, con un nome fisso e l'etichetta afe.ticket=<id>. Viene ritrovato dopo un riavvio.
  • per un workspace git, un volume con nome per ticket (afe-<key>-ws) e un solo container per ticket (afe-<key>), condiviso da tutti gli scope (V7b, R14). Per un workspace folder resta il mount della cartella sull'host.
  • niente rete di default, tutte le capability tolte, no-new-privileges. CPU, memoria e processi sono limitati, e gira come utente non-root.
  • montato solo su /workspace. Il socket Docker non è mai montato.
  • limitato alle variabili d'ambiente che un agente acp dichiara per nome. Mai un token della forge, mai una chiave del motore.

Un comando oltre il suo timeout viene ucciso e riportato come scaduto. Sandbox, proxy, rete e volume del workspace portano tutti l'etichetta afe.ticket. Spariscono con il ticket, alla fine (done, failed, cancelled) o su richiesta. Un ticket in pausa tiene il suo container, e così lo ritrova.

Un comando o uno script in volo quando un worker muore viene rieseguito dopo il recupero. I comandi si assumono ripetibili.

Risoluzione dei problemi​

Un comando script o deep si blocca e poi fallisce per timeout Il comando ha superato sandbox.timeoutS (o il timeout del nodo). La sandbox lo uccide e riporta l'esito come scaduto. Non lo riprova da sola. Alza il timeout, o dividi il comando in passi più piccoli.

Una chiamata di rete dalla sandbox fallisce o va in timeout L'host non è in sandbox.egress. Squid nega ogni host che non permette, e una connessione diretta che aggira il proxy non ha una via d'uscita. Aggiungi l'host a egress.

La sandbox è sparita dopo un riavvio, ma il ticket aspetta ancora Il container viene rimosso quando il ticket finisce, non mentre è in pausa. Se la sandbox manca per un ticket che aspetta ancora, controlla il log del worker per una sandbox persa. Il motore rimuove la sandbox e il suo volume, poi rimette in coda il ticket. Un ripristino da checkpoint a quel punto scarta il turno salvato del nodo interrotto.

Vedi anche​