Deja que el Agent ejecute código sin dejar que destruya tu máquina
Hace unos meses vi a un agent decidir que la forma más limpia de "resetear la base de datos de pruebas" era un rm -rf en una ruta que había construido a partir de una variable que resultó estar vacía. El comando que propuso fue rm -rf /$DIR, y $DIR era nada. Me di cuenta porque estaba en modo predeterminado y preguntó primero. Hice clic en No, arreglé el script y fui a hacerme un café con las manos temblando ligeramente.
Eso fue en mi laptop real. La misma máquina que tiene mis claves SSH, mi ~/.aws/credentials, mi clave restringida de Stripe en un .env a dos directorios de distancia, y la única copia de un proyecto paralelo que no había pusheado aún. El agent no era malicioso. Simplemente estaba equivocado, como un junior rápido y confiado está equivocado, excepto que escribe a 200 comandos por minuto y nunca se cansa lo suficiente como para ir más despacio.
Lo que tardé demasiado en interiorizar: el peligro no es que el agent se vuelva pícaro. Es la aburrida intersección de un comando confiado y equivocado, una credencial en una variable de entorno, y una conexión de red que puede llegar al internet abierto. No necesitas un villano. Necesitas un accidente y una ruta de exfiltración, y la mayoría de las configuraciones incluyen ambas por defecto.
La versión corta: el aislamiento es un espectro, no un interruptor. La capa más barata son los propios prompts de permiso del agent. La más fuerte es una microVM con su propio kernel. Para el trabajo diario con código que escribiste, quieres protecciones a nivel de OS más compuertas de permisos. Para cualquier cosa que no escribiste tú — un repo aleatorio, un script scrapeado, código que generó otro agent — quieres un límite real que el sistema operativo no pueda ser convencido de sortear. A continuación está toda la escalera y en qué escalón me encuentro realmente.
El modelo del accidente, no el modelo del villano
Modela esto honestamente con amenazas y la imagen se simplifica. Hay tres cosas que pueden salir mal cuando un agent ejecuta código:
- Destruye el estado local. Borra archivos, sobreescribe lo incorrecto, hace force-push sobre tu rama.
- Lee un secreto y lo envía a algún lugar. Hace grep en
~/.ssh, encuentra un token en una variable de entorno, lo envía concurla un dominio. Este es el que la gente subestima. - Ejecuta código que nunca fue tuyo confiar. El script post-install de una dependencia, un payload generado, un repo que clonaste para "echar un vistazo."
Los prompts de permiso abordan (1) y parcialmente (3). Hacen casi nada para (2), porque para cuando se ejecuta un comando, el secreto ya está en el entorno del proceso. Entonces el movimiento no es "encontrar la herramienta que resuelva todo." Es: apilar una capa barata que detenga la destrucción tonta, luego añadir un límite real debajo para los casos en que no basta con ser convencido por un prompt.
Escalón uno: las propias compuertas de permisos del agent
Los agentes de programación ya incluyen un modelo de permisos, y es el confinamiento más barato que tienes. Claude Code, por ejemplo, tiene distintos modos de permisos: default (las lecturas corren libres, todo lo demás pide confirmación), acceptEdits (las ediciones de archivos y un puñado de comandos del sistema de archivos como mkdir, mv, cp se auto-aprueban dentro de tu directorio de trabajo), plan (solo investigación, sin ediciones), auto (un modelo clasificador separado revisa cada acción y bloquea escaladas), dontAsk (solo se ejecutan herramientas pre-aprobadas, para CI bloqueado), y bypassPermissions — el flag --dangerously-skip-permissions, sobre el cual la documentación es directa: "Solo usa este modo en entornos aislados como contenedores, VMs o dev containers sin acceso a internet."
Estos modos son reales y útiles, pero entiende qué son: una decisión tomada antes de que se ejecute el comando, basada en el string del comando y, en el modo auto, el juicio de un modelo. Eso es una capa de política, no una pared. Puede estar equivocado, y un comando creativo puede hacer más de lo que su nombre sugiere. Confío en el modo plan para la exploración y default para cualquier cosa que toque la infraestructura, pero no trato a ninguno de ellos como un límite de seguridad para código en el que no confío. (He escrito sobre Claude Code hooks como barandillas en otro lugar — la misma advertencia: un hook es política aplicada en tu proceso, no aislamiento.)
Escalón dos: protecciones a nivel de OS con las que el modelo no puede discutir
Este es el escalón que la mayoría de los builders en solitario se saltan, y es el que tiene la mejor relación esfuerzo-seguridad. La idea: dejar que el sistema operativo aplique un límite de sistema de archivos y red en cada comando shell y sus hijos, independientemente de lo que el modelo haya decidido ejecutar.
Claude Code incluye esto como una herramienta Bash con sandbox. En macOS usa el framework integrado Seatbelt — no hay nada que instalar. En Linux y WSL2 usa bubblewrap para el aislamiento del sistema de archivos más socat para enrutar el tráfico a través de un proxy. Por defecto, los comandos con sandbox solo pueden escribir en tu directorio de trabajo y el directorio temporal de la sesión, y el acceso a la red es denegado por defecto: "ningún dominio está pre-autorizado. La primera vez que un comando necesita un nuevo dominio, Claude Code pide aprobación."
La razón por la que esto supera a un prompt de permisos: la documentación lo dice claramente. "El sistema operativo aplica el límite del sandbox en el proceso en ejecución, por lo que se mantiene independientemente de lo que el modelo haya elegido ejecutar e incluso si un comando permitido hace más de lo que su nombre sugiere." Esa es la diferencia entre política y aplicación.
Dos advertencias que vale la pena leer dos veces, ambas directamente de la documentación. Primero, la política de lectura predeterminada aún permite leer archivos de credenciales — "este valor predeterminado aún permite leer archivos de credenciales como ~/.aws/credentials y ~/.ssh/." Tienes que añadirlos a denyRead tú mismo. Segundo, el proxy de red aplica la lista de permitidos por nombre de host y "no termina ni realiza inspección TLS en el tráfico saliente," por lo que un permiso amplio como github.com puede convertirse en una ruta de exfiltración mediante domain fronting. El sandboxing efectivo, en sus palabras, "requiere tanto aislamiento del sistema de archivos como de la red" — amplía un lado y puedes deshacer silenciosamente el otro.
Mi postura: actívalo, deniega lecturas en tus directorios de credenciales y mantén la lista de dominios permitidos ajustada. Es gratuito y detiene los accidentes más comunes. Pero es una capa de endurecimiento en tu máquina real, no una sala limpia.
Escalón tres: contenedores
Un contenedor es el siguiente paso obvio, y para trabajo confiable pero desordenado es suficiente. Ejecutas el agent dentro de Docker, montas solo el directorio del proyecto, pasas solo las variables de entorno que ese proyecto necesita, y el radio de explosión es el contenedor. Elimina el contenedor, el daño desaparece.
La limitación honesta es la que los contenedores siempre tuvieron: comparten el kernel del host. Un contenedor son namespaces y cgroups, no una máquina separada. Para código que escribiste, eso es suficiente — el riesgo realista es un accidente, y el límite del namespace contiene bien los accidentes. Para código genuinamente no confiable, un exploit del kernel dentro del contenedor alcanza el host: una clase de ataque real, aunque poco común. También hay una opción intermedia — gVisor de Google intercepta las syscalls de un contenedor en un kernel en espacio de usuario antes de que lleguen al host, reduciendo la superficie de ataque sin una VM completa. Más fuerte que un contenedor simple, más débil que la virtualización de hardware, con un costo de I/O medible.
La configuración práctica de contenedor para un agent: un dev container que ejecuta el agent como un usuario no root, con el proyecto montado y nada más. Ese detalle de no-root importa — Claude Code se niega a iniciar en modo bypass-permissions como root, y la configuración del dev container existe en parte para darte un usuario no root para que las ejecuciones autónomas sean seguras para dejar solas.
Escalón cuatro: microVMs, la pared real
Cuando necesitas ejecutar código en el que no confías, el estándar de oro en 2026 es una microVM: una máquina virtual real con su propio kernel, que arranca en milisegundos, con el modelo de dispositivo reducido a casi nada. Firecracker, el VMM de código abierto que AWS construyó para Lambda y Fargate, es el que todos usan. Toda su filosofía de diseño es una pequeña superficie de ataque — "excluye dispositivos innecesarios y funcionalidad orientada al guest para reducir la huella de memoria y la superficie de ataque de cada microVM," arranca un kernel mínimo sin BIOS y sin modelo de dispositivo completo, y ejecuta cada guest en virtualización de hardware KVM.
El argumento de seguridad es estructural: cada carga de trabajo obtiene su propio kernel en un límite de virtualización de hardware, por lo que un exploit del kernel dentro de una microVM no puede llegar al host ni a una VM vecina. Ese es el límite que un contenedor no puede darte, porque el contenedor no tiene kernel propio. El costo solía ser el tiempo de inicio; los arranques de ~125ms de Firecracker eliminaron gran parte de eso.
Casi con certeza no querrás operar Firecracker tú mismo. Para eso están los servicios alojados.
Escalón cinco: servicios de sandbox alojados
Si "darle al agent un computador que no es mío" suena bien, eso es un sandbox alojado, y el espacio maduró mucho el año pasado.
Vercel Sandbox quedó disponible de forma general el 30 de enero de 2026 (anuncio). Ejecuta cada sandbox en una microVM Firecracker con su propio sistema de archivos y red, en Amazon Linux 2023, con runtimes node26/node24/node22/python3.13 y acceso sudo. El argumento de venta para el problema de la fuga de secretos: el código en un sandbox está aislado de tu infraestructura, por lo que no puede llegar a las variables de entorno, conexiones a bases de datos o recursos en la nube de tu proyecto — ese aislamiento es el producto, no una configuración que debes recordar activar. Lo manejas con un SDK de JS o Python, o una CLI. La persistencia (guardado automático y reanudación) está activa por defecto desde el lanzamiento GA.
E2B es el otro obvio, apuntado directamente a ejecutar código generado por IA. Es código abierto bajo Apache-2.0, tiene SDKs de Python y JavaScript, usa un Dockerfile para construir una plantilla de microVM (no para ejecutar un contenedor), y — la parte que me gusta — es autohospedable mediante Terraform en AWS, GCP, Azure o tus propias máquinas Linux. Así que puedes empezar en su nube y llevarlo internamente después sin reescribir.
El intercambio es el habitual: latencia, costo por ejecución y un viaje de ida y vuelta de red entre tu agent y el código. Para una herramienta que ejecuta fragmentos enviados por usuarios o generados por agents, ese es un precio justo por un límite de kernel limpio y aislamiento automático de secretos. Para editar tu propio repo todo el día, es excesivo y la fricción te molestará.
Lo que realmente ejecuto
Aquí está la configuración concreta, mapeada a los dos casos que importan.
Para mi propio código, día a día: Claude Code en mi máquina real, en modo default o plan, con el sandbox del OS activado. La configuración innegociable es denegar lecturas en los directorios de credenciales y mantener la lista de dominios permitidos corta:
{
"sandbox": {
"enabled": true,
"filesystem": { "denyRead": ["~/.aws", "~/.ssh", "~/.config/gh"] },
"allowedDomains": ["registry.npmjs.org", "github.com"]
}
}
Los secretos viven en un .env que el directorio de trabajo del agent no puede leer, y elimino las credenciales del proveedor de los subprocesos (CLAUDE_CODE_SUBPROCESS_ENV_SCRUB) para que un curl errante en un script de construcción no pueda capturar mi clave de API del entorno. Esto es endurecimiento, no una pared, y lo trato como tal.
Para cualquier cosa que no escribí yo — un repo que estoy auditando, código generado, una dependencia en la que no confío, un agent ejecutándose desatendido durante la noche — va a una microVM. En la práctica eso significa un sandbox alojado (Vercel Sandbox o E2B) para que el límite del kernel y el aislamiento de secretos vengan gratis, o un dev container ejecutando el agent como no-root con bypassPermissions dentro del contenedor donde no hay nada valioso que estropear. La regla que sigo: la fortaleza del límite debe coincidir con cuánto confío en el código, no con cuán conveniente es el límite.
El error que cometí al principio fue tratar esto como una decisión — "¿el sandbox está activado o desactivado?" Son dos. El código confiable en mi máquina quiere protecciones baratas que detengan accidentes. El código no confiable quiere una pared que el modelo no pueda ser convencido de cruzar. Elige el escalón que coincida con la confianza, deniega la lectura de tus secretos de todas formas, y puedes dejar que el agent corra sin vigilar cada comando — que era exactamente el punto de contratar a un agent.
