Ir al contenido

El gate de credenciales

Las herramientas de kovra nunca le entregan un valor sensible a un agente. Pero una credencial puede llegar al modelo por caminos que kovra no controla: usted pega un token en el prompt, o una herramienta que corre el agente — cat, Read, una descarga web, otro servidor MCP — devuelve un archivo o una página que contiene uno. Desde ese momento el valor queda en el contexto del modelo, donde una inyección de prompt puede exfiltrarlo.

El gate de credenciales cierra los caminos más comunes. Claude Code le consulta a kovra antes de que un prompt o la salida de una herramienta lleguen al modelo, y kovra responde desde un servicio local en su equipo:

  • Un prompt que contiene algo con forma de credencial se bloquea completo. Nada de él llega al modelo, y Claude Code le indica el motivo.
  • La salida de una herramienta que contiene una llega al modelo redactada: la credencial se reemplaza por [REDACTED:<regla>] y el resto de la salida queda igual.

Todas las ediciones de kovra llevan el mismo gate.

Ejecute kovra setup en cada proyecto donde use Claude Code (es el mismo comando que conecta kovra por MCP):

zsh
~/my-app % kovra setup
Vault ready; project `my-app`.
Updated .mcp.json (register the kovra MCP server).
Updated CLAUDE.md (insert/update the kovra conventions block).
Updated .claude/settings.json (deny Read of secrets files).
Updated .claude/settings.local.json (install the credential gate's Claude Code hooks).
Credential gate: Claude Code now asks kovra before a prompt or a tool output
reaches the model (it reloads hooks on its own). Notes:
- Hooks fail closed on a crash only from Claude Code 2.1.295 (`onFailure`).
- Recommended: set CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 in your user settings
(~/.claude/settings.json, "env") or your shell (Claude Code 2.1.299+). Then
@file mentions go through Read and the gate. Project settings ignore it, so
kovra setup does not write it.
kovrad: registered to run at login.
Setup complete. Review CLAUDE.md and .mcp.json, then reload your agent to pick up the MCP server.

Agrega dos cosas a la configuración de Claude Code del proyecto:

  • Dos hooks, en .claude/settings.local.json — su configuración personal, que git no versiona: UserPromptSubmit ejecuta kovra hook user-prompt-submit y PostToolUse (todas las herramientas) ejecuta kovra hook post-tool-use, ambos con onFailure: "block": si el hook falla o se demora demasiado, bloquea en lugar de dejar pasar el texto. Desde kovra.app, el comando nombra el kovra de la propia app por su ruta completa, así que no depende de cuál kovra aparece primero en su PATH. Si su repositorio todavía no ignora ese archivo, kovra setup lo agrega a .git/info/exclude.
  • Reglas que deniegan Read, en el .claude/settings.json versionado, sobre los archivos que guardan secretos: .env, .env.*, *.pem y *.key. .env.refs sigue siendo legible: contiene coordenadas, no valores, y el agente lo necesita.

Se conservan sus propias configuraciones, hooks y reglas; las entradas de kovra se reconocen por su comando y se reemplazan en su lugar, así que ejecutar kovra setup otra vez no cambia nada; los hooks que una versión anterior escribió en .claude/settings.json se sacan de ahí. Las reglas que deniegan Read se pueden versionar y compartir con su equipo; los hooks son de cada persona: cada quien que trabaja en el proyecto ejecuta kovra setup una vez. Revise los cambios sin escribir nada con kovra setup --dry-run.

Claude Code recarga los hooks por su cuenta: una sesión abierta los toma sin reiniciarla.

kovra nunca instala los hooks por sí solo. Usted ejecuta kovra setup, de modo que un agente nunca edita su propia configuración de seguridad.

Una mención @file pone un archivo en el prompt sin disparar ningún hook. Con CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 (Claude Code 2.1.299 o posterior), el agente lee el archivo con Read y el gate lo revisa. Defínala en su configuración de usuario — la configuración del proyecto la ignora:

~/.claude/settings.json
{
"env": {
"CLAUDE_CODE_DISABLE_FILE_MENTIONS": "1"
}
}

o expórtela en el perfil de su shell.

El gate reconoce credenciales por su prefijo documentado — GitHub, AWS, Slack, Stripe, OpenAI, Anthropic, Google, encabezados de llaves privadas y muchas más: más de 130 reglas, entre ellas las reglas útiles de gitleaks, cuyo aviso de licencia se incluye con kovra. El texto ya redactado ([REDACTED:…]) nunca vuelve a coincidir. También encuentra una credencial escrita con escapes de JSON (\r\n, \/, \u0067…) en un campo de texto; en ese caso retiene el campo entero, y bloquea el prompt que la contenga.

No reconoce una credencial sin prefijo — una contraseña, una cadena aleatoria suelta. Los patrones no pueden distinguirlas del texto común sin marcar demasiado.

Las reglas corren en kovrad, el servicio de kovra por usuario. En macOS, kovra setup (y la app de la barra de menú) lo registra para que arranque al iniciar sesión, y el hook lo arranca a demanda si no está corriendo. No abre ninguna conexión de red.

zsh
~ % kovra daemon status
kovrad is running
pid: 4182
version: 0.12.1 (protocol 1)
uptime: 3h 12m 5s
connections: 0
gate: 134 rules (set 2); 1840 clean, 1 prompts blocked, 3 outputs redacted
endpoint: /Users/me/.vaults/kovrad.sock

Si kovrad no puede responder, el gate falla cerrado. El hook no deja pasar el texto sin revisarlo:

  • un prompt se bloquea, con un mensaje que nombra la causa y qué hacer;
  • la salida de una herramienta se retiene: el modelo recibe la estructura de la salida con cada campo de texto reemplazado por [REDACTED:kovra-unavailable], y una nota con la misma causa y el mismo remedio.

Un kovrad que está corriendo tiene 5 s para responder, y el hook nunca arranca un segundo kovrad a su lado: una Mac muy cargada vuelve lento a kovrad, no lo detiene. Solo cuando no hay ninguno corriendo, el hook arranca kovrad, dentro de 1 s.

En un release de macOS, el hook además verifica que el proceso que responde en el endpoint de kovrad sea el kovrad firmado por Kaeus antes de confiar en un veredicto; un proceso que tomara el endpoint primero podría, si no, responder “allow” a todo. Si la verificación falla, el gate falla cerrado de la misma forma.

Es incómodo a propósito. El mensaje (en inglés) nombra una de estas causas, y la entrada de auditoría lleva la misma razón:

Razón El mensaje dice Qué hacer
not-running kovrad is not running and did not start Ejecute kovra daemon start y envíe el prompt otra vez.
not-answering kovrad is running but did not answer in time La Mac puede estar muy cargada: espere un momento y envíelo otra vez. Si sigue pasando, reinicie kovrad con kovra daemon stop y kovra daemon start.
busy kovrad is at its connection limit kovrad está atendiendo demasiadas solicitudes a la vez: espere un momento.
untrusted-kovrad the process at kovrad’s endpoint is not kovra’s signed kovrad Detenga el proceso que nombra el mensaje (su pid) y después ejecute kovra daemon start.
newer-kovrad kovrad runs a newer build than this kovra Vea más abajo.
no-vault kovra could not locate its vault Verifique que HOME (o KOVRA_VAULT_DIR, si lo definió) apunte a su vault.
ipc-error, bad-verdict, too-large, malformed-input, event-mismatch kovrad did not answer; kovrad’s answer was not usable; the input is too large to check; the hook input was not as expected; the hook was called for another event Ejecute kovra daemon start y envíe el prompt otra vez.

kovra reinicia kovrad solo hacia un build más nuevo. Si un kovra más viejo (otra copia en su PATH) encuentra corriendo un kovrad más nuevo, lo deja como está y el gate falla cerrado con un mensaje que nombra ambas versiones: apunte los hooks al kovra de kovra.app (ejecute kovra setup con él).

kovra además se niega a entregar valores a una sesión de Claude Code que no tenga los hooks. Dentro de esa sesión se rechazan las operaciones que ponen un valor en stdout o en un proceso:

  • CLI: kovra run, kovra show, kovra code, kovra decrypt, kovra ui (el panel), kovra package, kovra ssh-add y kovra key export;
  • MCP: inject_run y reveal.

Los metadatos siguen funcionando — list, status, fingerprint, doctor —, así que el agente puede seguir entendiendo cómo está cableado su proyecto. Fuera de Claude Code, en su propia terminal, nada cambia. La verificación corre antes de cada una de esas operaciones, de modo que si se quitan los hooks a mitad de una sesión, el efecto es inmediato.

Los hooks tienen que estar en dos lugares: el proyecto donde corre la sesión de Claude Code — kovra lo lee del propio proceso de Claude Code, no de la carpeta donde corre un comando — y, cuando un comando corre en otro proyecto, también en ese. Un agente puede correr un comando en una carpeta cuya configuración escribió él mismo; lo que no puede es mover la sesión. Los hooks en su configuración de usuario (~/.claude/settings.json) cubren todos los proyectos.

El rechazo indica su motivo:

this Claude Code session is not protected by kovra's hooks (kovra's hooks are not
installed), so kovra does not deliver values to it. Run `kovra setup` in
`/Users/me/my-app` (or add the hooks to your user settings); Claude Code picks them up
without a restart

Para resolverlo, ejecute kovra setup en la carpeta que indica el mensaje. Los demás motivos apuntan a configuraciones que desactivan los hooks, o a una sesión que kovra no puede ubicar:

Motivo Qué significa
kovra's hooks are not installed Ninguna configuración — de usuario, de proyecto, local ni administrada — tiene los dos hooks.
hooks are disabled (disableAllHooks) Su configuración define disableAllHooks.
hooks are disabled by managed settings La configuración administrada de su organización desactiva los hooks.
managed settings allow only managed hooks Está definido allowManagedHooksOnly; los hooks de kovra deben estar entonces en la configuración administrada.
the Claude Code process of this session was not found kovra no encontró un proceso de Claude Code por encima del comando, así que no puede saber qué proyecto cargó la sesión — por eso rechaza en lugar de suponer.
managed by an MDM profile kovra cannot read Claude Code se configura con un perfil de administración de dispositivos, que kovra no lee — por eso rechaza en lugar de suponer.

Los hooks de kovra deben ejecutarse de forma síncrona: una entrada marcada con "async": true no retiene el prompt ni la salida mientras kovra los revisa, así que cuenta como ausente.

La salida de reveal también pasa por el gate

Sección titulada «La salida de reveal también pasa por el gate»

La herramienta MCP reveal devuelve a propósito un valor revealable que no es de prod. Si ese valor tiene un prefijo conocido — por ejemplo, un token de GitHub de dev —, el gate lo redacta como cualquier otra salida de herramienta. Para usar ese valor, ejecute el comando con kovra run (o con inject_run por MCP), que se lo entrega al proceso y no al modelo.

El gate reduce las veces en que una credencial llega al modelo por accidente. No es una frontera frente a un agente que se proponga evadirlo. En concreto:

  • Disco y telemetría. Un prompt bloqueado igual se escribe en la transcripción de la sesión en disco, y la telemetría de Claude Code ve la salida de una herramienta antes de que el gate la reemplace. Claude Code también guarda en la transcripción el resultado estructurado original de una herramienta MCP: el servidor MCP de kovra redacta sus resultados antes de devolverlos, pero los de otro servidor MCP quedan en disco tal como ese servidor los devolvió.
  • Caminos que no disparan ningún hook: las menciones @file (salvo que las desactive como se indica arriba), el modo shell ! de la CLI y los comandos que fallan — cuando un comando termina con error, Claude Code le envía toda su salida al modelo sin consultar al hook.
  • Lo que escribe el modelo y los comandos que arma.
  • Credenciales sin prefijo — contraseñas y secretos sueltos.
  • Una credencial usada como nombre de campo en la salida de una herramienta (una lista indexada por datos, como nombres de archivo). Los nombres de campo son la estructura de la salida, que Claude Code verifica, así que el gate los deja como están y registra el hallazgo en el log de auditoría de kovra.
  • Configuración que kovra no ve. Los hooks configurados solo desde la consola de Claude Code o con --settings no están en disco, así que la verificación de sesión los da por ausentes y rechaza; en Windows, donde kovra no lee la carpeta del proceso de Claude Code, una sesión iniciada desde otra carpeta puede cargar una configuración de proyecto distinta de la que encuentra kovra; y un agente puede quitar las variables que kovra usa para detectar una sesión, fijar CLAUDE_PROJECT_DIR o CLAUDE_PID para que kovra lea la configuración de otro proyecto, o lanzar su comando bajo un programa propio llamado claude.
  • Versiones anteriores de Claude Code. Antes de 2.1.295, un hook que falla deja pasar el texto.
  • Aprobaciones. Un agente nunca aprueba: kovra approve se niega dentro de una sesión de Claude Code, y en una Mac toda aprobación se lee y se aprueba en la ventana de revisión de kovra — el sensor de Touch ID dentro de ella, o un código y el diálogo de macOS en una Mac sin Touch ID. Donde no se puede mostrar esa ventana (por SSH), lo que necesita su aprobación va a su teléfono vinculado, en la app de aprobación de kovra (kovra Mobile, próximamente); sin teléfono vinculado, se rechaza. La llave maestra y la vinculación se aprueban solo en la Mac.

Mantenga los valores sensibles donde corresponde: guárdelos con kovra add, refiérase a ellos por su coordenada y úselos a través de kovra run.

Cada veredicto con hallazgo, cada respuesta de falla cerrada y cada rechazo se escriben en el registro de auditoría — los ids de regla, la herramienta y el id de sesión, nunca el texto ni el valor:

Acción Cuándo
gate-block Se bloqueó un prompt.
gate-redact Se redactó la salida de una herramienta.
gate-fail-closed El gate no pudo revisar el texto y el hook falló cerrado; reason= nombra la causa (tabla de arriba).
session-refused Se negó un valor a una sesión sin los hooks.

Los veredictos limpios solo se cuentan, en kovra daemon status.