Una capa útil, con bordes conocidos
ai-jail está hecho para frenar los errores de un agente de confianza y mantener el daño dentro del proyecto: el directorio del proyecto es el radio de impacto, y tu home, tus claves y la red quedan fuera de él. Es un sandbox de procesos que comparte tu kernel, así que no es una frontera contra código hostil ni contra un atacante decidido, y no reemplaza a una máquina virtual desechable.
Para quien firma la aprobación, y después para el ingeniero de seguridad.
Lo que está hecho para detener
Un agente de confianza igual escribe mal un comando, ejecuta un script que sube por encima de la raíz del proyecto o se deja engañar por una página web o un paquete. Esto es lo que entonces intenta alcanzar, y no encuentra.
↔ Desliza hacia un lado para ver el diagrama completo
Fuera de alcance
- Tu directorio home y las credenciales que contiene. El agente recibe un home nuevo y vacío, así que
~/.sshy~/.awsno existen ahí dentro. - Los secretos de tu shell. ai-jail reduce las variables de entorno a una lista de permitidos pequeña, así que los tokens exportados se quedan fuera. La única excepción es la clave de API del propio harness que arrancas, por ejemplo
ANTHROPIC_API_KEYpara Claude, copiada por defecto junto con su login;--no-agent-statelo impide. - La red. Está cerrada, con dos excepciones que pasan por el proxy filtrado: una lista corta de registries de paquetes cuando las toolchains están activadas, lo normal en Linux, y, para un agente conocido, el host de su propia API, en ambas plataformas. Los hosts de telemetría siguen cerrados.
--no-networkcierra ambas cosas. - La memoria compartida del host, tu servidor gráfico y tu portapapeles. La salida del agente pasa por un filtro que descarta las secuencias de terminal de portapapeles y de consulta.
- Escribir en tu terminal. Linux bloquea la llamada
TIOCSTI, que falsifica pulsaciones de teclas, y macOS limita el control de terminal a la única terminal que ai-jail creó para esa ejecución. - Sus propias reglas. Un archivo
.ai-jaildentro de un repositorio no es de confianza: puede endurecer el sandbox y nunca abrirlo.
Dónde termina el muro
La jaula y tu sistema se apoyan en el mismo kernel. Un bug en ese kernel, en un driver o en el backend del sandbox es una forma de rodear el muro, y ai-jail no puede hacer nada al respecto. Una máquina virtual trae su propio kernel, y por eso es la frontera más fuerte.
↔ Desliza hacia un lado para ver el diagrama completo
Lo que esto significa para tus datos
- Secretos dentro del proyecto. Un archivo
.enven el repositorio se puede leer salvo que lo enmascares. Mira cómo enmascarar archivos. - Las máscaras cubren solo las rutas que existen cuando arranca el sandbox. Un archivo con secretos creado después, durante la sesión, no queda oculto.
- Un host permitido es de confianza por completo. Permitir
github.comcon--allow-hostdeja que el agente haga push a cualquier repositorio de ahí, no solo a los tuyos. --networkabre toda la red, así que todo lo que el agente pueda leer, tu proyecto incluido, puede enviarlo fuera.- ai-jail monta el login del propio agente por defecto, en lectura y escritura, así que un agente engañado puede usarlo mientras dure el arranque. El proyecto lo documenta como una concesión por comodidad;
--no-agent-stateda una ejecución sin sesión. - El git del host confía en el
.git/del proyecto, que es escribible. Un programa nombrado en.git/config, comocore.fsmonitorocore.pager, o dejado en.git/hooks/corre como tú, fuera del sandbox, la próxima vez que un comando git que coincida se ejecute en el host; paracore.fsmonitorbasta un simplegit status, incluso uno que tu editor lance en segundo plano, y nada aparece engit diff.--lockdownlo cierra al dejar el proyecto en solo lectura, y--map .gitmantiene el repositorio en solo lectura en una ejecución normal, a cambio de no poder hacer commit desde dentro. Después de una sesión que te sorprenda, lee.git/configy lista.git/hookscon herramientas de archivos simples antes de ejecutar git en el host, porque el propiogit config --listarranca el pager configurado. --masky--deny-pathcambian solo lo que ve el sandbox. Cualquier cosa fuera del sandbox que actúe por el agente sigue leyendo los archivos reales: el daemon de Docker bajo--docker, un servidor MCP alcanzado por un socket mapeado o un servicio detrás de--forward-port.
Para el ingeniero de seguridad
- Vulnerabilidades del kernel y de los drivers. Cada proceso aislado habla con el mismo kernel que el resto de tu máquina.
- Bugs en tu emulador de terminal, sobre todo con
--terminal-passthrough, que apaga el filtro de salida. - Defectos en el propio backend del sandbox: bubblewrap, Landlock y seccomp en Linux,
sandbox-execen macOS. - Landlock, las reglas del propio kernel, solo valla los sockets Unix abstractos y las señales en kernels 6.12 o más nuevos, en modo best-effort. Los kernels más antiguos se quedan solo con las reglas de sistema de archivos y de red, así que con
--x11,--networko--systemd-userlos sockets abstractos y las señales del host siguen al alcance. - Canales laterales y algunas clases de comunicación entre procesos.
- La salida filtrada es solo TCP. No hay UDP, así que un programa que lo necesite no funciona; tampoco puede sacar nada por ahí.
- El DNS bajo
--allow-host. En Linux, las consultas de nombres dentro del sandbox fallan, así que no hay canal DNS hacia fuera. En macOS el resolver del sistema no está vallado, así que queda un canal DNS de poco ancho de banda, la misma brecha que Anthropic documenta para su propio sandbox-runtime. - El proxy ve el host, el puerto y el momento de cada conexión. Lo que viaja dentro queda entre los dos extremos. Cómo está construida la salida filtrada.
- Las credenciales fantasma trasladan la confianza al propio ai-jail. Con
--secret, las peticiones al host vinculado pasan por ai-jail en claro para que pueda insertar la clave real, y las vuelve a cifrar al salir. Los túneles hacia cualquier otro host siguen siendo opacos. Solo cubre a los clientes que pueden hablar HTTP plano con el proxy para ese host. - Credenciales que pasas con
--github,--aws,--kube,--gcloudo--docker-config. Solo lectura protege el archivo contra cambios, no la credencial contra el uso: cualquier cosa dentro del sandbox puede actuar como tú en ese servicio mientras siga siendo válida. - En macOS,
sandbox-execestá marcado como obsoleto por Apple y no equivale al aislamiento de Linux. Mira las diferencias en macOS.
Interruptores que abren el muro
Todo lo de abajo está apagado hasta que lo activas, salvo dos interruptores que vienen activados por defecto: --toolchains en Linux y --agent-state en todas partes. La mayoría le da al agente un acceso real. Cómo configurarlos está en la página Configurar.
Mostrar todos los interruptores y sus consecuencias
| Interruptor | Lo que puede hacer entonces cualquier cosa dentro del sandbox |
|---|---|
--network | Enviar todo lo que pueda leer a cualquier sitio, sin restricción de tráfico. El valor por defecto permite solo el host de la API de un agente conocido y, en Linux, los registries de paquetes; --allow-host es la alternativa más estricta. |
--allow-host | Alcanzar exactamente los hosts que listas, y sus subdominios, por TCP a través de un proxy integrado. Cada host de la lista es de confianza por completo. Tu lista se suma al host de la propia API del agente; --no-network con una lista te deja solo esa lista. |
--forward-port | Usar por completo un servicio del loopback de tu host. Lo que acepte el servidor MCP, el socket del editor o la base de datos en ese puerto, el agente puede pedirlo, sin lista de permitidos, sin inspección y sin registro de auditoría. Todos los demás puertos del host siguen cerrados. Solo Linux, y se rechaza con --network. |
--toolchains | Activado por defecto en Linux. Ejecutar tus toolchains: ~/.cargo/bin y ~/.rustup en solo lectura, una caché de dependencias propia de la jaula en ~/.local/share/ai-jail/cache/ que sobrevive entre ejecuciones, y salida filtrada hacia los registries de paquetes. Las cachés reales del host nunca se montan. Apagado bajo --lockdown y con --no-toolchains. |
--secret | Nada nuevo. El valor real de esa clave nunca entra al sandbox: el agente ve un marcador de posición, y el proxy pone el valor real solo en las peticiones HTTP plano al único host al que la vinculaste. El propio proceso de ai-jail ve esa petición en claro. |
--audit-log | Nada nuevo. Este interruptor no debilita nada: escribe una línea por arranque en un archivo con modo 0600 fuera del sandbox, así que el agente nunca lo ve. |
--docker | Actuar como root en tu máquina a través del daemon de Docker. |
--kvm | Abrir /dev/kvm, la interfaz de virtualización del kernel, para que QEMU, Firecracker o el emulador de Android corran a velocidad de hardware. Solo en Linux. Los guests solo tienen la red del sandbox, y no se pasa ningún /dev/net/tun. |
--x11 | Registrar pulsaciones de teclas y tomar capturas de otras ventanas de X11. |
--systemd-user | Pedirle a tu gestor de usuario de systemd que ejecute servicios en el host. |
--inherit-env | Leer todo el entorno de tu shell, secretos incluidos. Para las claves de API, --env-from-file pasa un solo archivo en su lugar. |
--agent-state | Activado por defecto. Usar el login guardado del agente, por ejemplo el ~/.claude de Claude o los ~/.local/share/devin y ~/.config/devin de Devin, montados en lectura y escritura, más su clave de API copiada de tu shell si está definida, para que arranque con la sesión iniciada. Cualquier cosa dentro del sandbox puede usar esas credenciales mientras dure el arranque. --no-agent-state da una ejecución aislada y sin sesión; apagado bajo --lockdown. |
--github | Actuar como tú en GitHub. El directorio de configuración de gh se monta en solo lectura, y el token activo de github.com se lee de tu llavero en el host y se reenvía como GH_TOKEN para ese arranque, nunca por la línea de comandos ni por disco. La solo lectura protege el archivo; el token sigue funcionando mientras sea válido. |
--aws | Actuar como tú en AWS con ~/.aws, montado en solo lectura. |
--kube | Actuar como tú en todos los clústeres de ~/.kube, montado en solo lectura. |
--gcloud | Actuar como tú en Google Cloud con ~/.config/gcloud, montado en solo lectura. |
--docker-config | Usar tus logins de registries de contenedores en ~/.docker/config.json, montado en solo lectura. |
--audio | Grabar y reproducir audio mientras esté activado. |
--no-private-home | Ver tu directorio home real. Es una excepción amplia; mejor mapea la única ruta que necesitas. |
Ante la duda, se niega a arrancar
Un sandbox que arranca en silencio más débil de lo que pediste es peor que uno que se detiene con un error. En estos casos ai-jail se detiene.
- Un archivo de configuración que existe y es inválido o ilegible.
- Un
.ai-jailde proyecto que es un symlink. Un~/.ai-jailglobal que es un symlink se sigue solo hasta un archivo regular del que eres dueño, que nadie más puede escribir y que está fuera del proyecto. --lockdowncuando Landlock, las reglas de sistema de archivos del propio kernel, falta, es parcial o no se puede aplicar.--lockdown --no-landlocktambién se rechaza.--allow-tcp-port. Esa interfaz no puede restringir UDP, así que el flag se acepta por compatibilidad, el arranque falla y el error apunta a--allow-host.--networkjunto con--allow-host, o--browsercon--allow-host. Los dos se contradicen, así que ai-jail se niega a arrancar.--forward-portjunto con--network, donde el loopback del host ya está al alcance; fuera de Linux; y para el puerto 0 o un puerto por debajo de 1024, que el sandbox no puede enlazar. Una entradaforward_portsen la tabla base del archivo global tropieza con las dos primeras en cada arranque con navegador y en cada arranque en macOS.- Un archivo de
--env-from-fileque es un symlink, que puede leer su grupo o todo el mundo, que pertenece a otra persona o que está dentro del proyecto. - Un
BWRAP_BINque apunta a un binario de bubblewrap que alguien distinto de root pudo haber reemplazado. Se ignora con una advertencia, y sin un bubblewrap de confianza el arranque falla. - Un overlay que no se puede montar: falta el origen, el destino se solapa con otro mapeo o falla el almacenamiento. Desde la 2.4 el arranque aborta en vez de caer a un bind simple de lectura y escritura sobre los archivos reales.
Cómo se revisa el proyecto
Un solo mantenedor lleva el proyecto de forma abierta. Abajo está lo que se revisa y lo que no.
- 23
- tests de integración de escape del sandbox que intentan escribir en
/usr, leer~/.ssh, llamar aptraceybpf, y esperan fallar - 959
- tests unitarios en el código fuente en la versión 2.9.0
- 0
- peticiones de red al arrancar. La comprobación de actualizaciones es opt-in con
--update-check
Correcciones de seguridad recientes
- 2.9.0:
--x11por sí solo deja pasar X11. Antes, sin--displayno hacía nada, en silencio. - 2.9.0: el guardado automático ya no escribe en el archivo de proyecto opt-ins que la carga no confiable rechaza después, así que un archivo guardado nunca avisa en la siguiente ejecución.
- 2.9.0: macOS: Codex vuelve a arrancar bajo Seatbelt con un
psshim privado en lugar delpssetuid del host. - 2.8.0: macOS: el perfil de seatbelt conserva el login de Claude Code en el Keychain y permite el socket de control de Codex, así que ambos arrancan con la sesión iniciada.
- 2.8.0: las peticiones de posición del cursor de un lanzador las responde la terminal virtual, así que los lanzadores ya no se quedan colgados.
- 2.6.2:
--lockdownsiempre aísla el namespace de red, incluso con--network. Antes, ese par contradictorio dejaba el namespace compartido. - 2.6.2: los valores de
--envy--env-from-fileya no van en la línea de comandos del lanzador, donde cualquier proceso que corra como tú podía leerlos. Pasan por un archivo en memoria. - 2.4.0: los metadatos de worktree de un repositorio ya no pueden montar un directorio cualquiera del host en lectura y escritura.
- 2.4.0: una entrada duplicada en el entorno ya no cuela el valor real de una clave
--secretdentro del sandbox. - 2.4.0: un comando de navegador en un
.ai-jailde proyecto no confiable ya no desactiva el lockdown. - 2.4.0:
TIOCSTIse compara como lo lee el kernel, en 32 bits, así que una variante con el bit alto ya no pasa el seccomp. - 2.4.0: Landlock valla los sockets Unix abstractos y las señales en kernels 6.12 o más nuevos.
- 2.4.0: macOS: el acceso al terminal se limita a los terminales de la propia ejecución, y no a todos los
/dev/ttys*. - 2.4.0: macOS: las rutas con
--maskse deniegan también para escritura, además de lectura. - 2.4.0: el filtro de salida ya no se descompone con un ESC doble, que podía colar una escritura en el portapapeles.
- 2.4.0: los atajos de prueba del proxy quedan fuera de los builds de release.
Cómo se hace una versión
- Los tags de versión están firmados, y CI verifica cada uno contra una huella de clave fijada en el repositorio.
- Cada GitHub Action está fijada a un SHA de commit, y las actions están restringidas a una lista de permitidos.
- Las compilaciones usan una toolchain de Rust fijada y dependencias con
--locked. - Los binarios de macOS están firmados y notarizados por Apple.
- Cada archivo comprimido viene con un checksum SHA-256, y el job de publicación los vuelve a comprobar.
Lo que el documento de versiones dice que todavía falta
- Los secretos de firma y publicación todavía se guardan a nivel de repositorio y deberían pasar a los entornos de publicación.
- Todavía ningún ruleset obliga a que cada tag
v*esté firmado. - Las versiones inmutables (immutable releases) no están activadas.
- La publicación en crates.io todavía usa un token. El trusted publishing está planeado.
- Con un solo mantenedor no hay un segundo revisor para los cambios ni para las versiones.
Reportar una vulnerabilidad
No abras un issue público. Usa el reporte privado de vulnerabilidades de GitHub, incluye los pasos para reproducirla y las versiones afectadas, y da tiempo para coordinar una corrección.
Preguntas frecuentes
¿El agente puede escaparse?
¿Es seguro ejecutarlo con la red activada?
--network, todo lo que el agente puede leer, lo puede enviar, y ai-jail no está hecho para impedir que un atacante decidido lo haga. El punto medio es --allow-host: el agente llega solo a los hosts que listas, y cada uno es de confianza por completo. Enmascara primero los secretos dentro del proyecto. Mira cómo enmascararlos.¿Ha sido auditado?
Pon a tu agente tras las rejas
ai-jail es un único binario que no necesita daemon ni root. Añades una palabra delante del comando que ya usas.
brew tap akitaonrails/tap && brew install ai-jailai-jail claude