Skip to content

Audit

The audit is a table, organtia.auditoria, with one row for every operation that writes or tries to. It is not a conversation diary or an access log: it is the list of what was done, who asked for it and with what result.

A row is written every time a tool or a screen modifies data:

Source Examples of herramienta
The CEO’s tools 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
Approvals from the 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
Office screens oficina:puesto_editado, oficina:puesto_desde_perfil, oficina:puesto_ajustado, oficina:recurso_puesto, oficina:recurso_borrado, oficina:memoria_puesto, oficina:memoria_borrada
Settings ui:instrucciones, every time the CEO’s instructions are saved or restored

And a row with resultado = 'error' when one of those operations fails. The failure is written in a separate transaction: if the audit cannot be written, the original error is neither hidden nor turned into a different one.

  • Reads. Querying memory, listing roles, opening a profile page: none of that leaves a row.
  • Sign-ins and sign-outs at the gate. A successful login leaves a line in the process log (Entrada en la oficina: pedro) and updates acceso_usuarios.ultimo_acceso, but it does not write to the audit.
  • Conversations. Messages and replies live in agno.agno_runs. The audit keeps what was written to the business tables, not the text that was spoken.
  • Failures at the model provider. A network outage or an exhausted quota does not generate a row.

Three tables look like this one and are not:

Table What it stores
organtia.conexion_ejecuciones Every call to an external application (connector): action, URL, HTTP code, duration, error.
organtia.ejecuciones Orders to run a project’s tests, with their output and exit code.
agno.agno_runs Each agent’s conversation turns, with their token metrics.
Field Type What it means
id bigserial Operation number, in insertion order.
ocurrido_en timestamptz When it was recorded, with time zone. The CLI shows it trimmed to 19 characters (2026-11-06 09:12:33).
actor text Who asked for it, according to what the code knows about them. adrian by default.
herramienta text What the operation was. It is the tool’s name or the screen’s prefix.
entidad text What kind of thing was acted on: hecho, objetivo, puesto, proyecto, plan, decision, solicitud_recurso, campana, pieza_comunicacion, tarifa, canal, suscriptor, acceso, autonomia, instrucciones, puesto_recurso, puesto_memoria…
entidad_id text The identifier of that thing (the id of the row affected), as text.
resultado text ok or error. The database accepts no other value.
detalle jsonb What it takes to understand the operation. See below.
sesion_id text The session it was done from, if there was one. It can be null.

The value is not an authenticated identity in every case; it is what the code attributes:

Value When it appears
adrian Operations from the CEO’s tools. It is the default value of the table and of the repository.
The owner’s name (OWNER_NAME, Pedro by default) Edits made from the office and approvals made by name: role pages, resources, memory, projects, rates.
propietario Only the saving of the CEO’s instructions (ui:instrucciones).
Value Meaning
api:<usuario> HTTP request made by that person through the gate (for example api:pedro).
An identifier declared by the client The test runner sends its own in the X-Sesion header; it is cleaned and trimmed to 80 characters.
servicio:reloj Call authenticated with INTERNAL_SERVICE_TOKEN by the cron clock.
cron:<name> Turn launched by a cron job; the name is the cron job’s.
CLI session The identifier of the conversation session open in the terminal.
null Operations that do not come from a conversation: saving instructions, editing a page.

In a successful write, the object carries proof that what was written was read back:

{
"verificado": true,
"campos_verificados": ["estado", "titulo"],
"huella": {"contenido": "9f2c..."}
}
  • verificado is always true in ok rows: the repository inserts, reads back and only then confirms.
  • campos_verificados lists the fields that were compared with what was sent.
  • huella is the sha256 of the long text fields (over 40 characters), trimmed to its first 16 hexadecimal characters, so you can check the content did not change without storing the whole text in the log.

In an error row the detail is shorter and carries the message, trimmed to 1000 characters:

{"error": "ErrorVerificacion: el valor releído de «estado» no coincide con el enviado"}

Some operations add their own keys to the detail. Do not count on a fixed shape: look at the keys before reading a specific field.

In the CLI, which is the intended route:

/auditoria # last 15
/auditoria 50 # last 50

The table it prints has five columns: fecha, actor, herramienta, resultado and id (the last one is entidad_id, or — if there is none). The maximum it returns is 200 rows, even if you ask for more.

In the database, read-only:

Ventana de terminal
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"'

Queries that usually come in handy:

-- Everything that happened in a specific session
SELECT ocurrido_en, herramienta, resultado, detalle
FROM organtia.auditoria
WHERE sesion_id = 'api:pedro'
ORDER BY ocurrido_en DESC;
-- Only the failures of the last seven days
SELECT 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;
-- What was touched on a role
SELECT ocurrido_en, herramienta, actor
FROM organtia.auditoria
WHERE entidad = 'puesto' AND entidad_id = '12'
ORDER BY ocurrido_en DESC;

There is no web view of the audit. The office menu has no screen for this: you query it through the CLI or through SQL.

The application role (APP_DB_USER, organtia_app by default) has SELECT and INSERT on the table, and UPDATE, DELETE and TRUNCATE are revoked. That is: the application itself cannot rewrite or delete what it already recorded, not even through a programming error.

There is no automatic purge and no configurable retention: the table grows and does not empty itself. In a heavily used installation it is one of the heaviest; if it ever has to be trimmed, it is done by hand and with a backup first.

A full example, and what each part says:

2026-11-06 09:12:33 | Pedro | oficina:puesto_editado | ok | 12
  • It happened on 6 November 2026 at 09:12:33, server time.
  • The owner asked for it from the office.
  • It was an edit to a role page.
  • It went through and the result was read back.
  • The role affected is 12.

If that same row said error, the detail would carry the message and you would know the page was not saved, even if the interface had shown the notice differently.