Antes de dejarlo entrar a sus bases
Cada capa existe porque alguien, en teoría, podría abusar de la anterior
Esto no es una lista de buenas intenciones. Detrás de cada capa hay una prueba automatizada, una restricción que vive en la base de datos o una medición real. Y donde algo tiene un límite, lo decimos.
Las siete capas
De la más cercana a su servidor a la más cercana al modelo de lenguaje.
Acceso de solo lectura, verificado por pruebas
En Oracle, PulseDB monitorea con SELECT_CATALOG_ROLE y AUDIT_VIEWER (para leer la auditoría), a través de handlers ORDS pre-aprobados. En PostgreSQL entra con un rol de solo lectura, con pg_monitor y un tiempo máximo por consulta.
- Pruebas automatizadas intentan escribir con ese mismo usuario — un INSERT, un UPDATE, un DROP, incluso escondidos en una CTE o en una función SECURITY DEFINER — y exigen que el motor los rechace.
- Antes de enviar cualquier consulta, un filtro de SQL rechaza lo que no sea de lectura.
- La excepción, dicha de frente: el modo diagnóstico, que usted activa por una ventana que vence sola, desbloquea una cuenta aparte y se conecta directo a la base. En este piloto ese desbloqueo usa una cuenta administrativa; la alternativa acotada —un rol que solo puede bloquear y desbloquear esa cuenta— está diseñada y documentada.
Ninguna fila de sus tablas de negocio sale del servidor
El motor lee el diccionario de datos y las estadísticas — dba_data_files, pg_stat_user_tables, v$session — nunca el contenido de una tabla suya.
- Antes de que un texto salga hacia el modelo de lenguaje, los literales SQL se reemplazan por variables y se filtran secretos, direcciones y datos personales.
- Un ticket de actividad sospechosa guarda la sentencia exacta —el DBA la necesita para decidir—, con las contraseñas enmascaradas, y no pasa por el modelo: se queda en su servidor.
Una frontera de código hacia el modelo de lenguaje, no una promesa
Todo lo que va al modelo pasa por un único punto del código, el guardián de salida, que filtra lo que sale. Y solo tres módulos del backend pueden usar el cliente HTTP: el del modelo, el conector ORDS y el de WhatsApp. Eso no se revisa a mano.
- Una prueba automatizada analiza el árbol de sintaxis de cada archivo del backend con el módulo ast de Python — no una expresión regular que se pueda esquivar con un import indirecto.
- Si un archivo fuera de esa lista importa el cliente HTTP, o si algo llama al modelo sin pasar por el guardián, la prueba falla: test_solo_lista_blanca_importa_httpx y test_solo_egress_guard_llama_a_client_completar.
Una bitácora que se puede verificar, no solo leer
Cada evento de auditoría guarda, junto con sus datos, el hash SHA-256 de la fila anterior y el propio, encadenados desde un génesis fijo. Un trigger a nivel de base de datos bloquea cualquier UPDATE o DELETE sobre esa tabla. Un superusuario podría desactivarlo: para eso está la cadena, que se rompe con cualquier fila alterada o borrada.
- El endpoint /audit/verificar-cadena recomputa la cadena completa y dice en qué fila se rompió, si se rompió.
- Lo probamos alterando una fila a propósito desde una conexión de superusuario que evita el trigger de la aplicación: la verificación detectó la fila comprometida en la primera pasada.
Análisis estático del código y de sus dependencias
bandit revisa el backend en busca de patrones de riesgo; pip-audit compara cada dependencia contra vulnerabilidades conocidas. Última revisión, el 26 de septiembre de 2026: ningún hallazgo de severidad alta en el código, y una sola vulnerabilidad en las dependencias, en pytest, la herramienta de pruebas, que no corre en producción.
- Un falso positivo se documenta con un comentario en la línea exacta que lo produce, nunca se silencia sin dejar constancia.
- Un hallazgo real se corrige — y a veces se revierte: cuando subir cryptography para cerrar siete CVEs rompió en pruebas reales la autenticación del driver de Oracle, se volvió atrás y las pendientes quedaron documentadas, no ocultas. La versión que se usa hoy ya las cierra todas.
Credenciales que se rotan sin tocar el código
Un script cambia la contraseña del usuario de monitoreo directamente en la base de datos.
- Confirma empíricamente que la contraseña anterior ya deja de funcionar y que la nueva sí conecta, antes de darse por terminado.
- La aplicación necesita un reinicio para leer el valor nuevo. Lo documentamos así — no fingimos un hot-reload que no existe.
Continuidad verificada, no asumida
El respaldo diario del repositorio no se da por bueno solo porque el comando terminó sin error.
- Cada semana, un proceso restaura el último respaldo en una base efímera separada, compara el número de filas de las tablas clave contra el original y corre la verificación de la cadena de auditoría sobre esa copia.
- Tiempo medido de recuperación en la última prueba, el 26 de septiembre de 2026: 6,5 segundos.
Además de las siete capas, hay líneas que no se cruzan
Lo que PulseDB no hace, nunca
- No ejecuta un ALTER sobre sus objetos ni sus parámetros, no mata sesiones, no reinicia nada: los agentes no tienen esa herramienta. La única escritura sobre su base es la del modo diagnóstico —desbloquear y volver a bloquear su propia cuenta—, y la activa usted.
- No lee ASH ni AWR si su licencia de Diagnostics Pack no lo permite: lo comprueba antes de cada lectura. El reporte AWR, además, lo pide un administrador y abre una ventana de diagnóstico que se cierra sola.
- No promueve una comprobación nueva a producción sin aprobación humana: pasa veinticuatro horas en cuarentena contra un laboratorio antes de tocar una base real.
- No permite alterar ni borrar una fila de auditoría sin que se note: el trigger que lo impide vive en la base de datos, y si un superusuario lo desactivara, la cadena de hashes lo delata.
- No guarda las contraseñas de sus bases en el repositorio de código. Viven en variables de entorno del servidor, fuera del control de versiones.