Sandbox y seguridad
Por qué importa aquí el aislamiento
Sección titulada «Por qué importa aquí el aislamiento»Un agente lee archivos, ejecuta comandos de shell y llama a API externas en tu nombre. El fallo que te importa rara vez es malicia: es un agente que malinterpreta una ruta y reescribe tu checkout de trabajo, o uno que hace push de una rama que aún no habías terminado de revisar. Así que la pregunta que el producto tiene que responder no es «¿es confiable el agente?», sino «cuál es el radio de impacto cuando se equivoca».
Control Center responde eso por capas y las capas son independientes a propósito. Si falta una, las demás siguen en pie. Eso importa, porque la capa más externa —el sandbox del sistema operativo— es la única condicional: depende de que el host ofrezca un backend, y en Windows ninguno lo hace.
Qué limita realmente a un agente
Sección titulada «Qué limita realmente a un agente»Cinco cosas limitan la ejecución de un agente en cualquier host, haya sandbox o no. Ninguna es una frontera del kernel; todas son reales. Donde el host ofrece un backend, el sandbox del sistema operativo se sitúa por encima de ellas, y la sección «Qué añade el sandbox y dónde se detiene», más abajo, explica qué cambia eso.
Su propio worktree de copia en escritura. Cada conversación aprovisiona su
propio checkout de cada repositorio vinculado, clonado mediante copia en escritura
con el binario nativo rift que se incluye, en lugar de copiarse byte a byte, así
que el aislamiento es lo bastante barato como para ser la opción predeterminada.
El repositorio registrado que agregaste al espacio de trabajo no es el directorio
en el que trabaja el agente: dos conversaciones que tocan el mismo repositorio no
pueden ver las ediciones de la otra y ninguna está editando tu checkout.
Vale la pena ser precisos con el nivel de detalle: el worktree es por
conversación, no por agente. Dos agentes de la misma conversación reciben
directorios de trabajo overlay distintos que comparten el repos/ de esa
conversación —que es justo lo que se busca, ya que colaboran en un mismo
cambio—, mientras que sus directorios privados de agente permanecen separados. La
copia en escritura es el único backend allí donde se distribuye rift: un sistema
de archivos que no puede hacer reflink, o un directorio de datos en un volumen
distinto al del repositorio, hace fallar el aprovisionamiento en vez de recurrir a
otra cosa. Windows es la excepción: allí no hay backend de copia en escritura, así
que git worktree es el backend, no una degradación silenciosa.
Confinamiento de rutas en las herramientas de archivos del arnés integrado.
Las herramientas read, write, edit, apply_patch, search, find y
search_files del arnés resuelven cada ruta contra el directorio de trabajo de la
ejecución más las raíces compartidas —el directorio repos/ de la conversación,
donde viven los worktrees, y el directorio de adjuntos del espacio cuando
existe— y rechazan todo lo que quede fuera. Las rutas absolutas solo se aceptan si
ya estaban dentro. Un enlace simbólico dentro del worktree que apunte fuera de él
también se resuelve y se rechaza. Por eso un agente no puede usar write sobre
~/.ssh aunque no haya ningún sandbox de por medio.
La superficie de herramientas del modo. El modo
de una conversación decide qué herramientas se materializan siquiera para la
ejecución. En los modos de solo lectura (plan, review, orchestrate), las
herramientas que mutan simplemente no están en el registro y la política de
comandos de shell de esos modos niega sin más todo comando que mute —git commit,
git push, npm install, docker y el resto— en lugar de preguntar por ellos.
Una herramienta que no está presente no se puede llamar, lo cual es una garantía
más fuerte que la de una herramienta presente y condicionada.
Las barandillas de acciones unificadas. Cada herramienta que muta declara las
clases de efecto que puede producir, y el motor de políticas resuelve cada una
como permitir, preguntar o denegar antes de que se ejecute la llamada. Un prompt
sin ningún aprobador conectado se resuelve como denegado: una pregunta que nadie
puede responder nunca es un sí. Consulta
Barandillas para ver el modelo de
resolución.
Credenciales del broker y un entorno saneado. Las credenciales se emiten en cada lanzamiento y se revocan cuando termina la ejecución, y el token de GitHub que tiene una ejecución puede leer pero no hacer push; consulta «Los push pasan por Control Center» más abajo. ACP, el
bash del arnés y Claude Code bajo el wrapper nativo reciben un entorno al que se
le quitaron los vectores de inyección del enlazador dinámico y del intérprete
(LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND y
similares) antes de que arranque el proceso. Claude Code a través de
NoSandboxAdapter hereda el entorno del host.
Dónde se debilitan esos límites
Sección titulada «Dónde se debilitan esos límites»Las herramientas de archivos del arnés se ejecutan en el proceso del servidor, no
en un proceso hijo, así que ningún perfil de sandbox podría limitarlas aunque se
aplicara uno: solo bash pasa por el ejecutor de comandos con sandbox. Su
confinamiento es la comprobación de rutas descrita antes, de modo que esa
comprobación es determinante y no una segunda línea de defensa.
La herramienta bash es el otro punto donde conviene ser precisos. Sus comandos
se evalúan contra la política de comandos del modo y luego se lanzan con un
entorno saneado y el worktree como directorio de trabajo. Donde existe un backend,
ese lanzamiento va envuelto, así que es el sandbox lo que impide que un comando de
shell escriba fuera del worktree; donde no existe —Windows, o Linux sin bwrap y
socat—, la política de comandos es lo único que separa al comando del resto del
disco. En el modo chat, la política niega sin más una lista fija (escalada de
privilegios, destrucción del sistema y del disco, escape de contenedores y de
espacios de nombres) y pregunta ante mutaciones de estado remoto (git push,
gh pr create, publicación e instalación de paquetes). Todo lo que no está en ninguna
de las dos listas se ejecuta. Los pipelines, las cadenas y los subshells se
analizan y cada subcomando se evalúa por separado, y gana la decisión más
restrictiva.
Claude Code, cuando se usa como adaptador, es el caso en el que el sandbox carga
con el mayor peso. Control Center lo lanza con --dangerously-skip-permissions,
porque un claude -p no interactivo se bloquearía para siempre en su propia
solicitud de aprobación. Sus llamadas de lectura, escritura y edición ocurren dentro de su propio proceso y Control Center nunca las ve. Sus llamadas a Bash se envían, antes de ejecutarse, a un hook del servidor que clasifica la línea de comandos y pregunta a la regla correspondiente, y las herramientas mcp__* que sirve Control Center pasan por el control como siempre. Donde existe un backend, esas llamadas invisibles
siguen ocurriendo dentro del wrapper del sandbox, así que el sistema operativo es
la frontera que las rodea. Donde no existe, no hay ninguna. Cada adaptador lo
declara en su matriz de aplicación; consulta la matriz de honestidad de los
adaptadores.
Los push pasan por Control Center
Sección titulada «Los push pasan por Control Center»No hay indicadores de permisos por agente. Si un agente puede crear un commit, hacer push, abrir una pull request, escribir en un rastreador o llegar a la red lo deciden las barandillas —permitir, preguntar primero o denegar—, y las mismas reglas se aplican sea cual sea el adaptador que ejecute al agente.
Un push es la acción que tiene que aguantar incluso frente a un agente que
intenta esquivar una regla, así que se aplica por lo que el agente tiene y no
por lo que ejecuta. Con una GitHub App configurada, una ejecución en un worktree
de GitHub recibe un token de instalación con alcance de repositorio que puede
leer contenido y trabajar con pull requests, pero no hacer push. La
configuración de git de la ejecución reescribe cada URL de push de GitHub
(https://github.com/, git@github.com:, ssh://git@github.com/) hacia la
pasarela de ejecuciones de agentes, un endpoint de loopback en cc_server.
La pasarela lee qué refs actualiza el push, pregunta a la regla Enviar a un
remoto —así que se aplica una regla limitada a ramas, como «denegar los push
a main»— y solo entonces reenvía el push a GitHub con un token de escritura
que acuña para ese fin y que nunca entrega al agente. Un push rechazado aparece
en la propia salida de git:
! [remote rejected] main -> main (Control Center policy: …)La pasarela solo reenvía push al repositorio propio del worktree o a un repositorio de GitHub vinculado al espacio de trabajo. Los fetch van directamente a GitHub con el token de lectura.
Los demás comandos de shell encuentran una puerta más blanda. La herramienta
bash del arnés y, mediante un hook, la herramienta Bash de Claude Code
clasifican cada línea de comandos y preguntan a la regla que le corresponde:
git commit, merge, rebase, cherry-pick, revert y am son Crear un
commit; gh pr create es Abrir una pull request; gh pr merge,
review, comment y close son Publicar una revisión o fusionar;
gh issue create, edit, comment y close son Escribir en un rastreador
externo; las instalaciones de paquetes son Instalar un paquete; curl y
wget son Acceder a la red. Esto reconoce la forma habitual de escribir una
acción, y un agente decidido a ocultarla (eval, un archivo de script, una
llamada directa a la API) puede hacerlo. Los push no dependen de ello.
En un sandbox no se coloca ninguna clave de API de tickets. Los agentes escriben tickets con las herramientas MCP de Control Center, que preguntan a Escribir en un rastreador externo.
La regla Acceder a la red se lee una sola vez, cuando arranca la ejecución.
La red de un sandbox está activada o desactivada, así que denegar la
desactiva, mientras que permitir y preguntar primero la dejan activada; las
herramientas web_fetch y web_search del arnés siguen preguntando en cada
llamada. Las credenciales se revocan cuando la ejecución se desmonta, junto con
cualquier token de escritura que haya acuñado la pasarela, así que un
identificador no sobrevive a la ejecución.
Qué añade el sandbox y dónde se detiene
Sección titulada «Qué añade el sandbox y dónde se detiene»La enumeración SandboxBackend tiene tres valores: native (Seatbelt en macOS,
bubblewrap en Linux y WSL2), none y microvm. El envoltorio de comandos de
agentes solo usa los dos primeros: microvm es el backend de VM aislada para
terminales interactivos y rigs, no una mejora silenciosa de un envoltorio de
bash. Entender la forma nativa explica qué ganas allí donde se aplica y qué
nunca te iba a dar.
cc_server sondea el host una vez al arrancar y, donde un backend responde,
alimenta ambos puntos de enganche que tiene la ruta de despacho, porque los
transportes no comparten uno solo. El primero (SandboxPort) envuelve la CLI de
Claude Code. El segundo (SandboxManager) envuelve los adaptadores ACP y la
herramienta bash del arnés integrado. Alimentar solo uno dejaría a los demás
lanzando sin envoltorio, que es lo que solía pasar. En un host sin backend, o con
--sandbox off, todos lanzan sin envoltorio y el servidor indica en qué caso
estás en su línea de arranque. ACP y el bash del arnés siguen saneando el
entorno; Claude Code a través de NoSandboxAdapter hereda el entorno del host.
Una superficie se queda deliberadamente al margen: el terminal alojado en el
servidor acepta un argumento de backend por sesión y cc_server lo construye con
el shell del host como valor predeterminado, así que un terminal que abras desde
la aplicación se ejecuta sin sandbox a menos que un cliente indique native de
forma explícita. Es un shell real en el host del servidor, no una ejecución de
agente.
En Linux, bubblewrap crea un espacio de nombres de montaje, así que el agente ve
realmente solo lo que se montó dentro de él. En macOS, sandbox-exec aplica un
perfil de Seatbelt que es permisivo de forma predeterminada: un perfil
totalmente de denegación por defecto vuelve inutilizable un shell interactivo,
porque el número de llamadas dyld, XPC y Mach que habría que enumerar es
impracticable. Por eso el perfil de macOS parte de (allow default) y recorta
denegaciones: lecturas de rutas secretas, una política de escritura reajustada a
denegar con permisos explícitos por encima, bloqueo de unlink en cada ruta
denegada y sus ancestros para que un rename no pueda eludir una denegación de
escritura, denegaciones de exec para binarios siempre peligrosos y para cualquier
cosa copiada a un directorio con permiso de escritura, y red restringida a los
puertos del proxy de loopback más DNS.
La consecuencia práctica: en Linux se estrecha la visión que el agente tiene del sistema de archivos; en macOS el agente todavía puede leer buena parte del host, y tus checkouts registrados están protegidos por denegaciones explícitas de escritura y de renombrado en lugar de por invisibilidad. Ambos son aislamiento a nivel de espacios de nombres, no una frontera de seguridad del kernel; los propios backends lo dicen en sus notas de capacidades.
El conjunto de montajes bind es donde se traza la frontera entre agentes. El
despacho con overlay (Claude Code y ACP) monta hasta cuatro rutas, resueltas por
ejecución: el directorio de trabajo overlay del agente en lectura y escritura, el
directorio de configuración del agente en solo lectura, el directorio repos/
compartido de la conversación en lectura y escritura cuando existe, y el
directorio de adjuntos del espacio en solo lectura cuando existe. El directorio de
un agente hermano nunca se monta. El envoltorio de bash del arnés es más
estrecho: solo monta con bind el directorio de trabajo de la ejecución. Esta es la
misma forma que la comprobación de rutas en proceso del arnés aplica por software,
así que ambas coinciden, pero solo la comprobación de rutas cubre las herramientas
de archivos del propio arnés, que nunca llegan a un espacio de nombres de montaje.
Tus checkouts registrados se suman además al conjunto de denegación de escritura,
en todos los modos y en las tres rutas envueltas (Claude Code, ACP, el bash del
arnés), y cada regla se emite tanto en su forma literal como en su forma resuelta
por enlaces simbólicos. La segunda forma no es una sutileza: el kernel compara una
regla de denegación con la ruta resuelta, y macOS hace que la resolución sea el
caso habitual y no el exótico —/tmp en realidad es /private/tmp,
/var/folders/… en realidad es /private/var/folders/…—, así que un checkout al
que se llegara por cualquiera de las dos vías habría sido escribible mientras el
perfil parecía prohibirlo.
La red, bajo un sandbox, es una lista de permitidos de dominios por
sesión que aplican los proxies de loopback en proceso. La salida es de denegación
predeterminada: con la red desactivada, los proxies siguen arrancando, pero su
lista de permitidos está vacía, así que se rechaza toda conexión saliente. Con
ella activada, el resolutor construye la lista de permitidos a partir de una
línea base curada —proveedores de API de LLM, alojamiento de git, registros de
paquetes, el catálogo de modelos— más los dominios de GitHub, y compara el host
de destino de cada conexión con esa lista. Los comodines funcionan
(*.example.com coincide con subdominios, no con el dominio raíz). Una lista
aparte de siempre denegados prevalece sobre la lista de permitidos y cubre los
endpoints de metadatos de la nube y los sumideros de telemetría. Lo que no es
configurable es la propia lista: la especificación incluye un campo
egressAllowlist para dominios adicionales, pero nada en la ruta de despacho
lo rellena, así que en la práctica cada ejecución recibe la línea base más
GitHub, y nada más.
En macOS, las denegaciones son observables: un monitor intercepta el registro del
sistema en busca de denegaciones de Sandbox, filtra el ruido conocido, deduplica
las tormentas de reintentos y expone cada una como un SandboxViolation que indica qué se intentó y qué se bloqueó. En Linux, la ruta del
envoltorio analiza stderr en busca de EPERM, Operation not permitted y
Permission denied, y emite el mismo tipo de evento: no hay interceptación del
registro del sistema. Todos ellos llegan al flujo de eventos de la propia
ejecución, así que una llamada al sistema denegada aparece en la transcripción y
no solo en un registro. La telemetría solo existe mientras un sandbox nativo está
envolviendo realmente el proceso; en un host sin backend no hay nada que observar.
Tampoco tiene relación con las barandillas: una violación proviene de una llamada al sistema denegada, nunca de una consulta de regla.
Las confirmaciones vienen de las barandillas, no del sandbox
Sección titulada «Las confirmaciones vienen de las barandillas, no del sandbox»El sandbox no tiene ninguna vía de aprobación. Lanza, ejecuta, emite eventos y se desmonta; no hay ningún sitio en él donde una persona pueda responder a una pregunta. Cuando ves una confirmación en un espacio —aprobar este push, aprobar este borrado—, la planteó el motor de barandillas (o la política de comandos del arnés) antes de que la acción llegara siquiera al runtime.
Este orden es deliberado. Una aprobación que ocurriera después de la llamada al sistema sería un registro de auditoría, no un control. También es la razón de que la regla de fallo cerrado viva en las barandillas: sin ningún aprobador conectado, la decisión se toma antes de que se ejecute nada y es una denegación.
Diagnóstico
Sección titulada «Diagnóstico»Diagnosticar un agente es el único sitio donde se indica qué se puede comprobar a mano.
Conceptos relacionados
Sección titulada «Conceptos relacionados»- Rigs y recintos: el escalón por encima del sandbox, para cuando el trabajo necesita una máquina desechable completa con una frontera del kernel, y no un proceso envuelto
- Barandillas: la capa de políticas por encima de la base del sistema operativo y el origen real de las aprobaciones
- Ciclo de vida del despacho de un agente: dónde encaja el aislamiento en el flujo de despacho
- Modos: cómo un modo estrecha la superficie de herramientas y la política de comandos
- Backends de sandbox: los backends y su configuración
Guías relacionadas
Sección titulada «Guías relacionadas»- Configurar políticas de sandbox: reglas de acciones, aplicación de los push y postura del sistema de archivos
- Diagnosticar un agente: qué cubren las comprobaciones de diagnóstico