Auditoría
La auditoría es una tabla, organtia.auditoria, con una fila por cada operación que escribe
o que lo intenta. No es un diario de conversaciones ni un registro de accesos: es la lista de
lo que se hizo, quién lo pidió y con qué resultado.
Qué se registra
Sección titulada «Qué se registra»Se escribe una fila cada vez que una herramienta o una pantalla modifica datos:
| Origen | Ejemplos de herramienta |
|---|---|
| Herramientas del CEO | recordar_hecho, crear_objetivo, actualizar_objetivo, registrar_decision, solicitar_puesto, solicitar_recurso, guardar_plan_estrategico, proponer_campana, crear_pieza_comunicacion, otorgar_acceso, revocar_acceso, marcar_hecho_obsoleto |
| Aprobaciones desde la CLI | cli:puesto_activo, cli:puesto_rechazado, cli:decision_aprobada, cli:solicitud_aprobado, cli:aprobar_plan, cli:conceder_autonomia, cli:revocar_autonomia, cli:crear_proyecto, cli:crear_tarifa, cli:crear_canal, cli:registrar_suscriptor, cli:directrices_puesto, cli:puesto_cesado |
| Pantallas de la oficina | oficina:puesto_editado, oficina:puesto_desde_perfil, oficina:puesto_ajustado, oficina:recurso_puesto, oficina:recurso_borrado, oficina:memoria_puesto, oficina:memoria_borrada |
| Configuración | ui:instrucciones, cada vez que se guardan o se restauran las instrucciones del CEO |
Y una fila con resultado = 'error' cuando una de esas operaciones falla. El fallo se anota
en una transacción aparte: si la auditoría no se puede escribir, el error original no se tapa
ni se convierte en otro distinto.
Qué no se registra
Sección titulada «Qué no se registra»- Las lecturas. Consultar la memoria, listar puestos, abrir una ficha: nada de eso deja fila.
- Las entradas y salidas de la puerta. El login correcto deja una línea en el registro del
proceso (
Entrada en la oficina: pedro) y actualizaacceso_usuarios.ultimo_acceso, pero no escribe en la auditoría. - Las conversaciones. Los mensajes y las respuestas viven en
agno.agno_runs. La auditoría guarda lo que se escribió en las tablas de negocio, no el texto que se habló. - Los fallos del proveedor del modelo. Una caída de red o una cuota agotada no genera fila.
Hay tres tablas que se parecen y no son esta:
| Tabla | Qué guarda |
|---|---|
organtia.conexion_ejecuciones |
Cada llamada a una aplicación externa (conector): acción, URL, código HTTP, duración, error. |
organtia.ejecuciones |
Órdenes de ejecutar pruebas de un proyecto, con su salida y su código de salida. |
agno.agno_runs |
Los turnos de conversación de cada agente, con sus métricas de tokens. |
Los campos
Sección titulada «Los campos»| Campo | Tipo | Qué significa |
|---|---|---|
id |
bigserial | Número de la operación, en orden de inserción. |
ocurrido_en |
timestamptz | Momento del registro, con zona horaria. La CLI lo muestra recortado a 19 caracteres (2026-11-06 09:12:33). |
actor |
text | Quién la pidió, según lo que el código sabe de él. Por defecto adrian. |
herramienta |
text | Qué operación fue. Es el nombre de la herramienta o el prefijo de la pantalla. |
entidad |
text | Sobre qué tipo de cosa se actuó: hecho, objetivo, puesto, proyecto, plan, decision, solicitud_recurso, campana, pieza_comunicacion, tarifa, canal, suscriptor, acceso, autonomia, instrucciones, puesto_recurso, puesto_memoria… |
entidad_id |
text | El identificador de esa cosa (el id de la fila afectada), en texto. |
resultado |
text | ok o error. La base de datos no admite otro valor. |
detalle |
jsonb | Lo que hace falta para entender la operación. Ver abajo. |
sesion_id |
text | La sesión desde la que se hizo, si la había. Puede ser null. |
actor: quién
Sección titulada «actor: quién»El valor no es una identidad autenticada en todos los casos; es lo que el código atribuye:
| Valor | Cuándo aparece |
|---|---|
adrian |
Operaciones de herramientas del CEO. Es el valor por defecto de la tabla y del repositorio. |
El nombre del propietario (OWNER_NAME, por defecto Pedro) |
Ediciones hechas desde la oficina y aprobaciones con nombre: fichas de puesto, recursos, memoria, proyectos, tarifas. |
propietario |
Solo el guardado de las instrucciones del CEO (ui:instrucciones). |
sesion_id: desde dónde
Sección titulada «sesion_id: desde dónde»| Valor | Significado |
|---|---|
api:<usuario> |
Petición HTTP hecha por esa persona a través de la puerta (por ejemplo api:pedro). |
| Un identificador declarado por el cliente | El runner de pruebas manda el suyo en la cabecera X-Sesion; se limpia y se recorta a 80 caracteres. |
servicio:reloj |
Llamada autenticada con INTERNAL_SERVICE_TOKEN por el reloj de los crones. |
cron:<nombre> |
Turno lanzado por un cron; el nombre es el del cron. |
| Sesión de la CLI | El identificador de la sesión de conversación abierta en la terminal. |
null |
Operaciones que no nacen de una conversación: guardar instrucciones, editar una ficha. |
detalle: el contenido
Sección titulada «detalle: el contenido»En una escritura correcta, el objeto trae la prueba de que se releyó lo escrito:
{ "verificado": true, "campos_verificados": ["estado", "titulo"], "huella": {"contenido": "9f2c..."}}verificadoes siempretrueen las filasok: el repositorio inserta, relee y solo entonces confirma.campos_verificadoslista los campos que se compararon con lo enviado.huellaes el sha256 de los campos de texto largos (más de 40 caracteres), recortado a sus 16 primeros caracteres hexadecimales, para poder comprobar que el contenido no cambió sin guardar el texto entero en el registro.
En una fila de error, el detalle es más corto y lleva el mensaje, recortado a 1000 caracteres:
{"error": "ErrorVerificacion: el valor releído de «estado» no coincide con el enviado"}Algunas operaciones añaden claves propias al detalle. No cuentes con una forma fija: mira las claves antes de leer un campo concreto.
Dónde se consulta
Sección titulada «Dónde se consulta»En la CLI, que es la vía prevista:
/auditoria # últimas 15/auditoria 50 # últimas 50La tabla que imprime tiene cinco columnas: fecha, actor, herramienta, resultado e id
(este último es entidad_id, o — si no lo hay). El máximo que devuelve es 200 filas, aunque
pidas más.
En la base de datos, en lectura:
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c " SELECT ocurrido_en, actor, herramienta, entidad, entidad_id, resultado FROM organtia.auditoria ORDER BY ocurrido_en DESC LIMIT 20"'Consultas que suelen servir:
-- Todo lo que pasó en una sesión concretaSELECT ocurrido_en, herramienta, resultado, detalle FROM organtia.auditoria WHERE sesion_id = 'api:pedro' ORDER BY ocurrido_en DESC;
-- Solo los fallos de los últimos siete díasSELECT ocurrido_en, actor, herramienta, detalle->>'error' AS error FROM organtia.auditoria WHERE resultado = 'error' AND ocurrido_en > now() - interval '7 days' ORDER BY ocurrido_en DESC;
-- Qué se tocó de un puestoSELECT ocurrido_en, herramienta, actor FROM organtia.auditoria WHERE entidad = 'puesto' AND entidad_id = '12' ORDER BY ocurrido_en DESC;No hay vista web de auditoría. El menú de la oficina no tiene una pantalla para esto: se consulta por CLI o por SQL.
Inmutabilidad y conservación
Sección titulada «Inmutabilidad y conservación»El rol de la aplicación (APP_DB_USER, por defecto organtia_app) tiene SELECT e INSERT
sobre la tabla, y le están revocados UPDATE, DELETE y TRUNCATE. Es decir: la propia
aplicación no puede reescribir ni borrar lo que ya registró, ni siquiera con un error de
programación.
No hay purga automática ni retención configurable: la tabla crece y no se vacía sola. En una instalación con mucho uso, es una de las que más pesa; si algún día hay que recortarla, se hace a mano y con una copia de seguridad antes.
Cómo leer una fila
Sección titulada «Cómo leer una fila»Un ejemplo completo, y lo que dice cada trozo:
2026-11-06 09:12:33 | Pedro | oficina:puesto_editado | ok | 12- Ocurrió el 6 de noviembre de 2026 a las 09:12:33, hora del servidor.
- Lo pidió el propietario desde la oficina.
- Fue una edición de ficha de puesto.
- Salió bien y se releyó el resultado.
- El puesto afectado es el
12.
Si esa misma fila dijera error, el detalle contendría el mensaje y sabrías que la ficha no
se guardó, aunque la interfaz hubiera mostrado el aviso de otra manera.
