# Seguridad

> Dónde queda la clave del banco, cómo se cifra, cómo se aísla cada organización, qué guarda la auditoría y qué se puede revocar al instante.



Conectar el SII o un banco significa entregarle a Connect una credencial real, y evaluar eso exige respuestas concretas: dónde queda la clave, quién puede leerla, qué pasa si algo se compromete. Esta página responde en ese orden. Cada afirmación describe comportamiento del sistema, no intención.

## La credencial nunca te toca [#la-credencial-nunca-te-toca]

El diseño parte por sacar la credencial de tu camino. Con el [enlace hosted](/docs/operar/enlace-hosted), la persona que tiene la clave la escribe en una página de `connect.emisso.ai`, nunca en tu aplicación: tu producto emite el enlace y recibe el resultado (`connection_id`, estado), pero la clave no pasa por tu frontend ni por tu backend, y el contexto de tu agente tampoco la ve.

Del lado de Connect, la credencial se cifra para la organización dueña de la conexión y queda en el vault. En tiempo de llamada se resuelve del lado del servidor, en el momento exacto en que un conector la necesita para abrir sesión contra la fuente. De ahí no sale: no entra al contexto de un LLM ni aparece en un tool result, y ningún log la registra cruda. La misma regla cubre el material derivado de la credencial, como las sesiones de portal que algunos bancos exigen: se resuelven server-side y jamás viajan en una respuesta.

## Cifrado [#cifrado]

El modelo es envelope encryption por organización. Cada organización tiene su propia clave de datos (DEK); las credenciales se cifran con AES-256-GCM bajo esa DEK, y la DEK se guarda a su vez envuelta por una clave maestra (KEK) que no vive en la base de datos, sino en un vault administrado. El material cifrado lleva versión de clave, la costura que permite rotar sin re-cifrar a ciegas.

Hay una sola implementación de cifrado para todo el sistema: los campos sensibles del plano persistido y los secretos de webhook se sellan con el mismo tronco (la misma DEK por organización, el mismo sellado autenticado). Un secreto de webhook, igual que una credencial, solo se muestra al crearse o rotarse; ninguna lectura posterior lo devuelve.

## Aislamiento multitenant [#aislamiento-multitenant]

Toda fila de datos de negocio lleva su `organization_id`, y sobre eso corren dos guardias independientes:

1. **La aplicación filtra siempre por organización.** Es la guardia primaria: cada consulta lleva el filtro explícito.
2. **La base impone Row Level Security como respaldo, con default deny.** El contexto de organización se fija por transacción; si no se fijó, la política no calza con ninguna fila y la consulta devuelve cero resultados. Un filtro olvidado en el código degrada a «no ves nada», no a una fuga.

El runtime se conecta con un rol propio, sin privilegio de saltarse RLS y sin permiso de DELETE sobre las tablas; los roles administrativos con bypass no participan del camino de ejecución. En el dominio del SII y los bancos, el diseño trata una fuga entre organizaciones como un riesgo existencial; por eso las guardias son dos.

## Redacción y auditoría [#redacción-y-auditoría]

Toda llamada a una tool deja exactamente una fila en la [bitácora](/docs/conceptos/bitacora), con actor, conexión, resultado y latencia. Del input, la fila guarda solo un `input_digest`: el hash SHA-256 del input ya redactado, donde los campos con forma de secreto (token, password, credential y similares) se reemplazan antes de calcular el hash. El input crudo no se almacena.

La bitácora es append-only: no existe camino de UPDATE ni de DELETE sobre ella, ni siquiera para los roles del sistema. Los fallos de autenticación ocurren antes de conocer la organización, así que van a un registro de eventos de seguridad separado, nunca a la bitácora de un tenant. Y los [webhooks](/docs/operar/webhooks) siguen la misma disciplina de redacción: sus payloads se limitan a códigos del catálogo y conteos.

## Revocación [#revocación]

Cuando algo se compromete, lo que importa es cuánto tarda en dejar de funcionar:

* **Una API key** se revoca al instante desde el dashboard y deja de autenticar en la siguiente petición. Es el freno de emergencia: a propósito, cualquier miembro de la organización puede accionarlo, sin esperar a un admin.
* **El acceso de un agente MCP** se revoca desde [Agentes](https://connect.emisso.ai/agentes) y deja de autenticar en la siguiente petición: la revocación cierra todas las sesiones que esa persona abrió con ese cliente, incluido el refresh token, así que el agente no puede volver solo. Como con una API key, cualquier miembro de la organización puede accionarlo.
* **Un enlace de conexión** se revoca desde el dashboard; desde ese momento responde el mismo 404 que cualquier enlace inválido.
* **Una conexión** se deshabilita completa: sus tools dejan de resolver (`connection_disabled`) y la sincronización programada se detiene con ella.
* **Rotar la credencial de una conexión** invalida además la sesión de portal derivada de la clave anterior: la sesión vieja se expira en el acto, así que no queda material capaz de verificar en verde una credencial que ya no existe.
* **El secreto de un webhook** se rota con una ventana de 24 horas en que conviven ambos, para no coordinar despliegues bajo presión.

En todos los casos la revocación es un cambio de estado, nunca un borrado: el rastro de lo que esa pieza hizo queda íntegro en la auditoría.

## El modelo de amenaza del enlace [#el-modelo-de-amenaza-del-enlace]

El enlace hosted es la superficie más expuesta del sistema (una URL que recolecta claves bancarias), y por eso tiene su análisis propio. El resumen:

| Riesgo                          | Qué lo acota                                                                                                  |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Fuerza bruta del token          | 256 bits de entropía, tope de intentos, expiración corta y el mismo 404 para todos los fallos                 |
| Reuso del enlace                | Un solo uso, garantizado por la base de datos y no por un chequeo previo en código                            |
| Clickjacking y exfiltración     | `frame-ancestors` por sesión con orígenes exactos, y un `postMessage` que nunca lleva credencial ni token     |
| Reenvío del enlace a un tercero | Expiración, tope de intentos y notificación: la fila en la bitácora más el webhook `connect_session.consumed` |

La tabla completa, con el ciclo de vida y la semántica de cada estado, está en [enlace hosted](/docs/operar/enlace-hosted).

## Próximos pasos [#próximos-pasos]

* El flujo que mantiene la clave fuera de tu aplicación, paso a paso: [enlace hosted](/docs/operar/enlace-hosted).
* Qué guarda exactamente cada fila de auditoría: [la bitácora](/docs/conceptos/bitacora).
* Qué es una conexión y cómo se administra su ciclo de vida: [conexiones](/docs/conceptos/conexiones).
* Los topes que protegen la superficie: [límites](/docs/operar/limites).
