Il modello di sicurezza
Perché un segreto non entra mai in una sandbox, in un container o nell'ambiente di un agente.
Il motore si fida della configurazione e di niente di ciò che esegue. Un modello, un comando, un tool o un agente esterno può essere sbagliato o ostile. Un segreto che ne raggiunge uno è un segreto perso. Ogni regola qui sotto nasce da questo.
Il token
Un repository privato si clona con il token nominato da Workspace.tokenEnv. Il token arriva
solo al processo git sull'host, tramite il suo ambiente e GIT_ASKPASS. Non compare mai in un
argv, in un URL del remoto scritto su disco, in un log, in un evento o in una sandbox.
Anche il push è un passo deterministico del motore sull'host. builtin:open_pr spinge il branch
di integrazione con lo stesso GIT_ASKPASS, e la forge apre la pull request (vedi
Git e pull request).
La sandbox
Nessun comando generato da un agente gira fuori dalla sandbox Docker. Il container:
- non ha rete per impostazione predefinita.
- ha tutte le capability rimosse e
no-new-privileges, con limiti di CPU, memoria e pids. - gira come utente non root.
- monta
/workspacecome unico percorso scrivibile, e non monta mai il socket Docker.
Non riceve nessuna variabile d'ambiente e nessun segreto, tranne i nomi di variabile che un
agente acp dichiara. L'adattatore Docker passa -e NAME e legge il valore dal proprio ambiente,
così il valore non entra mai in un argv.
L'egress
L'egress è una allowlist. Una sandbox senza sandbox.egress mantiene --network none. Con
l'egress entra in una rete interna per ticket e raggiunge solo gli host elencati, attraverso un
proxy squid attaccato al bridge predefinito.
Ogni altro host viene rifiutato, e così una connessione diretta che ignora il proxy. La sandbox,
il proxy e la rete portano l'etichetta afe.ticket, e spariscono con il ticket (vedi
Sandbox Docker).
La forge e gli agenti
I token di git e della forge non entrano mai nei container. Il token arriva solo al processo
git sull'host, tramite il suo ambiente e GIT_ASKPASS. Non compare mai in un argv, in un URL
del remoto scritto in .git/config, in un log, in un evento o in un output restituito.
Il token della forge (un PAT GitHub fine-grained nominato da Workspace.tokenEnv) raggiunge
l'API GitHub solo nell'header Authorization. Push, pull request e merge sono passi
deterministici del motore sull'host. Gli agenti non ricevono mai gh, CLI di forge o token di
forge, e un merge avviene solo dopo l'approvazione di una persona.
I percorsi dei tool
.. e i percorsi assoluti vengono rifiutati. Ogni componente del percorso è aperto dalla radice
del nodo con dir_fd e O_NOFOLLOW. Ogni symlink su un percorso viene rifiutato, anche uno che
punta dentro la radice, o uno creato dentro la sandbox mentre una chiamata è in corso.
La sessione del browser
POST /auth/login confronta il token con hmac.compare_digest, e imposta un cookie di sessione
firmato con una chiave derivata dal token. Il cookie è HttpOnly, SameSite=Strict, Path=/, e
Secure dietro HTTPS. Niente è tenuto lato server, un nuovo token termina ogni sessione, e il
cookie non arriva mai in un log.
L'handshake di /rpc accetta il cookie solo quando l'Origin della richiesta è quello del
server. La CLI continua a usare il bearer token (vedi Web UI).
Operations
/livez, /readyz e /metrics non chiedono nessun token, quindi si legano a 127.0.0.1 a meno
che --ops-host dica altro. La readiness nomina la dipendenza che fallisce, mai il suo testo di
errore. Nessuna etichetta di metrica porta un id di ticket, un input o un segreto.
I valori degli header OTLP finiscono solo nella richiesta, mai in uno span, in un errore o in un log.
I segreti
Nessun segreto è scritto in un manifest, in un log, in un evento o in una metrica. Un manifest
nomina la variabile d'ambiente che lo contiene (apiKeyEnv, tokenEnv). Le stringhe di
connessione sono lette all'avvio e mai ripetute. I valori che sembrano segreti vengono tolti dalla
memoria e redatti dagli errori.