El miedo es razonable. Le das a un agente acceso a tu terminal y a tus archivos, y en algún momento vas a estar en otra pestaña mientras corre algo que no leíste. La reacción normal es una de dos: aprobar cada paso a mano —y perder toda la velocidad— o apagar los avisos y rezar.
Hay un tercer camino y es el que usa la gente que de verdad deja a Claude Code trabajando: escribir una lista de lo que puede hacer sin preguntar y otra de lo que no puede hacer nunca. La velocidad del piloto automático, con paredes.
Lo que hace que esto funcione y no sea un placebo es una regla del sistema: las prohibiciones le ganan a todo. No es una recomendación al modelo, es el programa el que bloquea.
Cómo se decide cada acción
Hay tres listas y se evalúan siempre en el mismo orden: primero deny, luego ask, luego allow. Gana la primera que coincida, y qué tan específica sea la regla no cambia el orden.
La consecuencia práctica importa mucho: una prohibición amplia como Bash(aws *) bloquea todo lo que coincida, incluso si tienes un permiso más fino como Bash(aws s3 ls). Una regla de prohibición no admite excepciones adentro. Si quieres permitir un pedazo, no lo prohíbas entero.
Y algo que conviene tener claro antes de empezar: estas reglas las impone Claude Code, no el modelo. Lo que escribas en tu CLAUDE.md orienta lo que Claude intenta hacer, pero no es un límite. El límite son estas listas.
Dónde se escriben
En un archivo de configuración, bajo la llave permissions. El del proyecto vive en .claude/settings.json y se puede subir al repositorio para que todo el equipo lo herede; el tuyo personal, en ~/.claude/settings.json.
Para verlas y editarlas sin abrir el archivo está el comando /permissions, que además te dice de qué archivo viene cada regla. Y cuando apruebas algo con "sí, no vuelvas a preguntar", la regla se guarda sola en .claude/settings.local.json.
La sintaxis, en cinco reglas
- Sin paréntesis, aplica a toda la herramienta.
Bashson todos los comandos,WebFetchtodas las descargas. En la lista de prohibidos, un nombre pelado así hace que Claude ni siquiera vea la herramienta. - Con paréntesis, aplica a un caso.
Bash(npm run build)es ese comando exacto. Los comodines van con*y pueden ir al principio, en medio o al final:Bash(npm *),Bash(git * main),Bash(* --version). - El espacio antes del asterisco importa.
Bash(ls *)exige una frontera de palabra: coincide conls -lapero no conlsof.Bash(ls*), sin espacio, coincide con los dos. El sufijo:*es equivalente al espacio más asterisco. - Los archivos usan patrones tipo gitignore.
Read(.env)equivale aRead(**/.env)y agarra cualquier.enva cualquier profundidad. Un~/al principio ancla en tu carpeta de usuario y un//doble ancla en la raíz del sistema. Ojo: una sola diagonal, comoEdit(/src/**), no es ruta absoluta —ancla en la carpeta del archivo de configuración que la define. - Solo
ReadyEditaceptan rutas. Si escribes una regla con ruta paraWriteoGlob, se acepta pero nunca se consulta, y te avisa al arrancar. UsaEdit(docs/**)en lugar deWrite(docs/**). UnReadprohibido bloquea también editar y crear en esa ruta.
La lista de prohibidos que le pongo a cualquier proyecto
Esta es la base. No depende del lenguaje ni del tipo de trabajo.
{
"permissions": {
"deny": [
"Read(.env)",
"Read(**/.env.*)",
"Read(~/.ssh/**)",
"Read(**/credentials/**)",
"Edit(**/.git/**)",
"Bash(git push *)",
"Bash(rm -rf *)",
"Bash(curl *)",
"Bash(wget *)"
],
"ask": [
"Bash(git commit *)",
"Bash(npm publish *)"
],
"allow": [
"Bash(npm run test *)",
"Bash(npm run build)",
"Bash(git diff *)",
"Edit(src/**)",
"WebFetch(domain:claude.com)"
]
}
}
Dos decisiones de ahí valen explicación.
Por qué prohibir curl y wget. Tratar de restringir una URL desde una regla de Bash no funciona: se rompe con las opciones antes de la dirección, con https en vez de http, con un redireccionamiento, con la URL guardada en una variable. La forma que sí sostiene es cerrar las herramientas de red de la terminal y dejar que las descargas pasen por WebFetch, que sí filtra por dominio: WebFetch(domain:claude.com) permite ese sitio, WebFetch(domain:*.tuempresa.com) permite subdominios.
Por qué ask y no deny en los commits. Prohibir un commit es molesto y termina en que lo desactivas. Ponerlo en ask te da un momento de revisión sin bloquear el flujo. La lista de en medio es la que la mayoría no usa y es la más útil.
El paso que todo mundo se salta: entender hasta dónde llegan estas reglas. Las prohibiciones de lectura y edición cubren las herramientas de archivo de Claude y los comandos de terminal que sabe reconocer, como cat, head o sed. No cubren un subproceso que abre archivos por su cuenta: si Claude escribe un script de Python que lee tu .env y lo corre, la regla de Read no lo detiene. Para un límite que aplique a todos los procesos, lo que necesitas es el modo de aislamiento del sistema, no la lista de permisos.
El detalle que salva
Claude Code entiende los operadores del shell. Una regla como Bash(comando-seguro *) no autoriza comando-seguro && otro-comando: cada parte de un comando compuesto tiene que coincidir por su cuenta con alguna regla, y los separadores que reconoce incluyen &&, ||, ; y las tuberías.
Donde sí hay que tener cuidado es con los ejecutores de entorno. docker exec, npx y compañía corren lo que venga después, así que un permiso de la forma Bash(loquesea run *) autoriza cualquier cosa que siga. Si quieres permitir trabajo dentro de uno de esos, escribe la regla con el comando interno incluido, uno por cada cosa que quieras permitir.
Cómo lo armo en la práctica
No te sientes a escribir cuarenta reglas hoy. El camino que funciona es al revés: pega la lista de prohibidos de arriba en tu .claude/settings.json y trabaja normal durante una semana aprobando a mano. Cada vez que apruebes el mismo comando por tercera vez, muévelo a allow. En dos semanas tienes una lista hecha con tus datos reales, no con los que imaginaste.
El error que veo seguido es el opuesto: alguien se emociona, permite Bash completo para dejar de ver avisos, y se queda sin ninguna pared. Si vas a permitir todo, al menos deja la lista de prohibidos puesta —es lo único que sigue aplicando aunque el resto esté abierto. Para el siguiente paso, dejarlo trabajando solo con esas paredes ya puestas, sigue con deja de ser niñera de Claude.