Cómo impedir con un solo comando que Claude Code, Codex CLI, Cursor y OpenCode lean tu .env
Para impedir que un agente de programación con IA lea tus archivos .env, pon entre el agente y tus archivos una capa que rechace esas lecturas y elimine los secretos de lo que devuelven las herramientas. zn-gate lo hace para Claude Code, Codex CLI, Cursor y OpenCode con un solo comando, npx -y zn-gate init, y un segundo comando, npx -y zn-gate check, confirma que funciona. Es de código abierto (MIT), se ejecuta en tu máquina, no necesita cuenta y por defecto no envía nada a ningún sitio.
Esta guía explica qué cambia el comando en cada agente, cómo verificarlo, qué no cubre y cómo quitarlo.
¿Por qué importa que un agente de programación lea tu .env?
Un agente de programación funciona llamando a herramientas: leer un archivo, buscar en el repositorio, ejecutar un comando de shell. Todo lo que devuelve una herramienta se añade a la conversación, y la conversación es lo que se envía al proveedor del modelo. El problema empieza cuando el archivo que abre el agente es .env.
Un caso concreto. Le preguntas al agente por qué falla una migración de base de datos. Decide revisar la configuración y lee .env. El archivo contiene DATABASE_URL=postgres://app:password@db:5432/main, una clave de pagos y algunos valores más. Esas líneas pasan a formar parte del contexto. Pueden enviarse al proveedor, guardarse en el registro de sesión del agente en disco y repetirse en una respuesta posterior o en un comando que el agente escriba. Tú nunca escribiste la contraseña en un chat y aun así acabó ahí.
Para que ocurra no hace falta un atacante: un agente servicial lee el archivo que explica el problema. El texto que lee en una página web o en una incidencia también puede empujarlo hacia archivos que no tenía motivo para abrir. Pedirle en un prompt que no lea secretos es una petición, no un control. Una comprobación que se ejecuta antes de que la herramienta actúe sí es un control, y eso es lo que añade zn-gate.
¿Qué necesitas antes de empezar?
Necesitas Node.js 18 o posterior. Abre una terminal y ejecuta:
node --version
Si el comando no se encuentra o muestra una versión inferior a 18, instala la versión LTS desde nodejs.org y abre una terminal nueva. El plugin de Claude Code pide algo más: Node.js 20.12 o posterior, Git y Claude Code 2.1.295 o posterior. La versión LTS actual de Node.js cumple los dos requisitos.
Escribe los comandos en tu propia terminal (Terminal en macOS, PowerShell o Símbolo del sistema en Windows), no en el chat de tu agente de programación. init te pide confirmar cada paso y, cuando lo ejecuta un agente de IA o un script, solo muestra los pasos en lugar de ejecutarlos.
¿Cómo proteges con un solo comando todos los agentes de tu máquina?
npx -y zn-gate init
init busca Claude Code, Codex CLI, Cursor y OpenCode. Para cada uno que aún no esté protegido, muestra la configuración que ejecutaría, ejecuta cada paso cuando lo confirmas y termina con una comprobación. Los agentes que no están instalados se omiten.
En Windows, PowerShell a veces se niega con "running scripts is disabled on this system". Usa npx.cmd en lugar de npx (y npm.cmd, zn.cmd para los demás comandos), o ejecuta los mismos comandos desde el Símbolo del sistema.
¿Qué instala init en cada agente?
Cada agente deja que una herramienta externa se enganche de forma distinta, así que init usa un mecanismo diferente para cada uno:
| Agente | Qué se instala | Mecanismo | Qué haces después |
|---|---|---|---|
| Claude Code | El plugin zn-gate del marketplace usezn/zn |
Hooks, entregados por el plugin | Inicia una sesión nueva de Claude Code |
| Codex CLI | Los hooks de zn, con npm install -g zn-gate y zn hooks install codex |
Hooks (hooks.json) |
Abre Codex y aprueba los hooks nuevos con /hooks |
| Cursor | Los hooks de zn, con zn hooks install cursor |
Hooks (hooks.json) |
Reinicia Cursor |
| OpenCode | El plugin de zn, con zn hooks install opencode |
Archivo de plugin (plugins/zn-gate.js) |
Reinicia OpenCode |
Si prefieres hacerlo agente por agente, son los mismos pasos que ejecuta init. Para Claude Code:
claude plugin marketplace add usezn/zn
claude plugin install zn-gate@usezn
Para los otros tres:
npm install -g zn-gate
zn hooks install codex
Sustituye codex por cursor u opencode según el caso. Esto escribe en tu configuración de usuario. Con --project escribe en el proyecto actual (véase la sección de problemas frecuentes).
¿Qué cambia para el agente una vez protegido?
Con el plugin o los hooks activos:
- Al agente se le bloquea la lectura de archivos
.envcon valores reales y de archivos de credenciales, como los de~/.awsy~/.ssh. - Los secretos de lo que devuelven sus herramientas se sustituyen por marcadores o se ocultan antes de que el modelo los vea.
- Un secreto que pegues en un prompt se bloquea antes de enviarse.
- El agente no puede escribir en la carpeta de zn (
~/.zn) ni editar la configuración de hooks y plugins que lo protege. En Cursor esto último es de mejor esfuerzo.
Hasta dónde llega esto depende de lo que permiten los hooks de cada agente, y las diferencias importan:
| Depura la salida de herramientas | Bloquea lecturas de .env |
Bloquea un secreto pegado | |
|---|---|---|---|
| Claude Code | Todas las herramientas, incluidos shell, lectura de archivos, búsqueda y MCP | Sí | Sí |
| Codex CLI | Sí, pero el resultado se sustituye por su texto depurado, y el registro de sesión propio de Codex conserva el original | Sí | Sí |
| Cursor | Solo la salida MCP; la salida del shell y el contenido de archivos no se depuran | Sí | Sí |
| OpenCode | Todas las herramientas | Sí | Sí |
La tabla de compatibilidad completa, con lo que ocurre cuando un hook no puede ejecutarse, está en compat.md. Las guías de instalación por agente empiezan en Instalación.
¿Cómo compruebas que funciona?
npx -y zn-gate check
check crea una carpeta temporal con dos archivos: un .env con una clave falsa y un archivo de texto normal. Para cada agente configurado para tu usuario, pide al hook de zn que deje al agente leer ambos, como lo haría tu agente. En OpenCode, cuyo plugin se ejecuta dentro de OpenCode, comprueba en cambio las reglas de zn. Un agente cuenta como protegido solo cuando el .env se bloquea y el archivo normal se permite.
No se envía nada por la red, no se inicia ningún agente y no cambia ningún ajuste. La carpeta se borra al final. Cada agente recibe uno de tres resultados:
- OK: zn lo está protegiendo. El texto entre comillas es lo que se le dice a tu agente cuando intenta leer el archivo.
- -: zn no está configurado para él, o solo para un proyecto. La línea muestra el comando que lo configura. Un agente que no está instalado aparece como "not found on this machine".
- X ... NOT protected: zn está configurado pero no protegería a ese agente. La línea dice por qué y qué ejecutar.
check termina con código 0 solo cuando todos los agentes configurados para tu usuario están protegidos. Qué significa cada línea: Comprobar que funciona.
¿Cómo sigue tu propia aplicación obteniendo los valores del .env?
Cuando el agente ya no puede leer .env, tu aplicación sigue necesitando sus valores. zn-gate puede guardarlos en una bóveda cifrada en tu máquina y dejar marcadores en el archivo:
npx -y zn-gate unlock
npx -y zn-gate env shadow .env
El primer unlock crea la bóveda. Tras env shadow, el archivo contiene marcadores como __ZN_STRIPE_KEY__ en lugar de los valores, y el comando te indica cómo deshacer el cambio. Para arrancar tu aplicación con los valores reales, permite el programa una vez y ejecútalo a través de zn-gate. Si arrancas tu aplicación con npm run dev:
npx -y zn-gate secret allow STRIPE_KEY --command npm
npx -y zn-gate run --secrets STRIPE_KEY -- npm run dev
--secrets STRIPE_KEY pone el valor real en el entorno de tu aplicación. La mayoría de los cargadores de .env, dotenv entre ellos, respetan una variable que ya está definida, así que tu aplicación recibe el valor y no el marcador. zn te pide confirmar en un cuadro de diálogo del sistema, fuera del agente. En un servidor o por SSH no hay cuadro de diálogo: ejecuta npx -y zn-gate unlock --foreground en una terminal aparte y responde allí. Permite el programa más acotado posible. npm, node, python y otros intérpretes ejecutan cualquier código que reciban, así que permitir uno deja que cada script que ejecute lea el valor, y zn te avisa cuando lo haces. Más en la documentación de la bóveda.
¿Qué puede fallar y cómo se arregla?
El agente bloquea todos los prompts tras actualizar Node.js. El hook apunta al Node.js que había cuando ejecutaste init. Si lo actualizaste, lo moviste o lo quitaste (con nvm, por ejemplo), el hook no puede arrancar. Claude Code 2.1.295 o posterior, Cursor y OpenCode bloquean entonces los prompts en lugar de dejarlos pasar sin comprobar; Codex CLI los deja pasar. Ejecuta npx -y zn-gate check: nombra cada agente que no está protegido y el comando que lo arregla, normalmente npm install -g zn-gate seguido de zn hooks install <agent>.
En Claude Code están instalados el plugin y los hooks. Si ejecutaste zn hooks install claude-code antes de instalar el plugin, ejecuta primero zn hooks uninstall claude-code; de lo contrario se ejecutan los dos conjuntos de hooks.
Ajustes de Claude Code desactivan los hooks. En un equipo gestionado por una organización, Claude Code puede estar configurado para ignorar los hooks que instalas tú (disableAllHooks o allowManagedHooksOnly en los ajustes gestionados), y no te avisa. disableAllHooks en tus propios ajustes tiene el mismo efecto. check lee esos archivos de ajustes y marca al agente como no protegido, y también detecta cuándo el plugin zn-gate está desactivado. npx -y zn-gate status muestra una nota cuando encuentra un ajuste así. Pide a tu administrador que añada zn a los ajustes gestionados. No se leen las políticas enviadas desde claude.ai, la gestión de dispositivos (MDM) ni el registro de Windows.
Codex no parece reaccionar. Los hooks nuevos o modificados solo se ejecutan después de que los apruebes en /hooks.
Instalaste solo para un proyecto. Una instalación con --project escribe archivos como .claude/settings.json, .codex/hooks.json, .cursor/hooks.json o .opencode/plugins/zn-gate.js dentro del proyecto. check lista esa configuración pero no la ejecuta. Un compañero que clona el proyecto sin zn en su PATH no recibe ninguna comprobación de esos archivos, así que instala zn en cada máquina que ejecute el agente.
¿Qué no cubre zn-gate?
- Es una capa, no una garantía. Un secreto con un formato que zn no reconoce, y que no está en la bóveda, aún puede llegar al modelo.
- En Cursor, la salida del shell y el contenido de archivos no se depuran. Un secreto que imprime un comando de shell, o que está en un archivo no bloqueado, llega al modelo. Para reducirlo, usa
env shadowen tus archivos.envy guarda los secretos en la bóveda. - En Codex CLI, los hooks son una protección de apoyo y no un límite de cumplimiento forzoso, que es como Codex describe sus propios hooks. El registro de sesión que Codex guarda en local sigue teniendo la salida original.
- No cubre: el Cursor CLI (
cursor-agent), Hermes Agent, Gemini CLI, Copilot CLI, las sesiones de Claude Code en la nube, Cowork ni las conversaciones de chat. - Mientras la bóveda está desbloqueada, cualquier programa que se ejecute como tu usuario puede pedirle un secreto guardado por su nombre. Bloquéala cuando no la necesites.
- En otros editores (VS Code, Antigravity, Windsurf, Cline, Zed, JetBrains, Continue) zn-gate funciona solo como servidor MCP al que el agente llama cuando decide hacerlo. Allí no puede bloquear lecturas de tus archivos
.env. La configuración está en editores. - El paquete de Python es para agentes que construyes tú, como LangChain, CrewAI, LlamaIndex o llamadas a herramientas a mano. No protege a los agentes de programación.
¿Qué sale de tu máquina?
Nada, por defecto. La salida de las herramientas y los argumentos de sus llamadas no se envían nunca, con ninguna configuración. Un prompt va a la comprobación opcional en la nube solo si defines ZN_CLOUD=1 y ZN_API_KEY, o la opción de clave de API del plugin de Claude Code. Los secretos que detecta zn-gate se quitan antes en local; los datos personales no se quitan en esta vía.
Para ver tu propia configuración, ejecuta:
npx -y zn-gate status
El comando muestra las versiones del paquete y de las reglas, el motor de detección activo, si la comprobación opcional en la nube está activada y si hay una clave de API definida. El envío de estadísticas de uso viene desactivado y tiene su propio estado: zn-gate stats share status. La tabla completa está en Qué sale de tu máquina.
¿Cómo lo quitas?
- Codex CLI, Cursor u OpenCode:
npx -y zn-gate hooks uninstall <agent>, por ejemplonpx -y zn-gate hooks uninstall codex. Añade--projectsi instalaste solo para un proyecto. - Claude Code: abre
/plugindentro de Claude Code y quita zn-gate, o desactívalo conclaude plugin disable zn-gate@usezn. Vuelve a activarlo conclaude plugin enable zn-gate@usezn. Si instalaste los hooks de zn para Claude Code en lugar del plugin, ejecutanpx -y zn-gate hooks uninstall claude-code. - La bóveda:
env shadowmuestra el comando exacto que recupera el.envoriginal,npx -y zn-gate env unshadow .env --backup <name>, donde<name>es el nombre de la copia que indica.
Preguntas frecuentes
¿Cómo impido que Claude Code lea mi archivo .env?
Ejecuta npx -y zn-gate init en tu propia terminal. Para Claude Code instala el plugin zn-gate, que bloquea la lectura de archivos .env con valores reales y de archivos de credenciales. Inicia una sesión nueva de Claude Code y ejecuta npx -y zn-gate check para confirmarlo.
¿zn-gate funciona también con Codex CLI, Cursor y OpenCode?
Sí. Para esos tres, init instala los hooks de zn (en OpenCode, un archivo de plugin). Codex te pide aprobar los hooks nuevos con /hooks. Cursor solo depura la salida de herramientas MCP, así que la salida del shell y el contenido de archivos no se depuran allí.
¿Necesito cuenta, clave de API o conexión de red?
No hace falta cuenta ni clave de API. La instalación descarga el paquete de npm, y el plugin de Claude Code desde GitHub. Después, por defecto nada sale de tu máquina: la salida de las herramientas y los argumentos de sus llamadas no se envían nunca, con ninguna configuración. La comprobación en la nube de los prompts está desactivada salvo que definas ZN_CLOUD=1 y ZN_API_KEY, o la opción de clave de API del plugin de Claude Code.
¿Mi aplicación puede seguir leyendo los valores del .env después de esto?
El agente no puede leer un .env con valores reales, pero tu aplicación sí puede obtenerlos. Ejecuta npx -y zn-gate env shadow .env para mover los valores a la bóveda cifrada local de zn-gate y arranca tu aplicación con el comando run de zn-gate cuando hayas permitido el programa.
¿Cómo sé que la protección está activa de verdad?
Ejecuta npx -y zn-gate check. Crea una carpeta temporal con un .env falso y un archivo normal, pide al hook instalado que lea ambos y da un agente por protegido solo si el .env se bloquea y el archivo normal se permite.
¿Contra qué no protege?
Es una capa, no una garantía. Un secreto con un formato que zn no reconoce y que no está en la bóveda aún puede llegar al modelo, y en Cursor un secreto que imprime un comando de shell no se depura. Las sesiones de Claude Code en la nube, Cowork y el chat no están cubiertos.
¿Cómo desinstalo zn-gate?
En Codex CLI, Cursor u OpenCode ejecuta npx -y zn-gate hooks uninstall seguido del nombre del agente, por ejemplo codex. En Claude Code, quita el plugin en /plugin o ejecuta claude plugin disable zn-gate@usezn.
Para probarlo en tu propio agente, ejecuta npx -y zn-gate init (MIT, local, sin cuenta) y después npx -y zn-gate check. El código está en GitHub y las guías para cada agente empiezan en Instalación.