# ¿Pueden las marcas de tiempo de auditoría de los agentes sobrevivir a los cambios de reloj?

La hora civil es una prueba, no una garantía de ordenación. Un agente puede hacer dos llamadas perfectamente legítimas mientras el reloj del host retrocede, repite una hora durante el horario de verano o se corrige después de salir de un periodo largo de suspensión. Si el visor de auditoría ordena esas llamadas solo por la hora mostrada, puede contar una historia convincente pero falsa.

Usa un número de secuencia para responder «¿qué registro aceptó primero este diario?». Usa una marca de tiempo civil para responder «¿qué hora del calendario indicó el registrador?». Son preguntas distintas. Los equipos tienen problemas cuando fingen que un solo campo responde a ambas.

En los agentes autónomos, esa diferencia importa. Un revisor puede necesitar establecer si un comando SSH siguió a una aprobación, si una llamada HTTP se reintentó o si una revocación ocurrió antes de la siguiente acción. La respuesta debe resistir un reloj defectuoso en un portátil y un cambio de hora complicado, no limitarse a verse ordenada en una tabla.

## Las marcas de tiempo no pueden establecer un orden fiable de los eventos

Una marca de tiempo civil no puede demostrar que un evento ocurrió antes que otro cuando el reloj puede cambiar. Solo informa de la lectura del reloj observada cuando el software escribió el registro.

Considera esta secuencia de una máquina que se adelantaba diez minutos hasta que la sincronización horaria la corrigió:

```text
seq 841  2026-11-03T14:10:12.481Z  agent requested deploy status
seq 842  2026-11-03T14:00:13.107Z  agent requested deploy status
seq 843  2026-11-03T14:00:14.052Z  approval recorded
```

Una ordenación por marca de tiempo coloca 842 y 843 antes que 841. El diario aceptó primero 841. Ninguna de las dos vistas es un error tipográfico. Responden a preguntas diferentes.

Esto importa incluso cuando las marcas de tiempo aumentan. Un reloj puede avanzar despacio, saltar hacia delante o ser corregido por un usuario. Dos registros con una hora civil creciente pueden estar más separados o más próximos de lo que sugieren sus valores. Una marca de tiempo proporciona una coordenada observada en una escala de tiempo civil. No ofrece una medición ininterrumpida de la duración.

RFC 3339 deja clara la parte del almacenamiento: las marcas de tiempo necesitan un desplazamiento cuando interviene la hora local, y UTC expresado con `Z` elimina la ambigüedad del desplazamiento durante el intercambio. Es una buena práctica, pero RFC 3339 no convierte el reloj del host en un testigo imposible de equivocar. Define una notación, no la verdad.

Asigna un `seq` creciente de forma monotónica a cada flujo de auditoría de solo anexado. Asígnalo en el componente que serializa las escrituras, no en cada proceso de agente. Si dos procesos de agente pueden asignar números localmente y subir después sus registros, has creado dos órdenes y los has llamado uno solo.

El número de secuencia hace una afirmación limitada pero útil:

- Dentro de un diario, un `seq` menor entró primero.
- Los saltos indican que el investigador debe justificar registros ausentes, retenidos o excluidos de forma intencionada.
- Un duplicado significa que falló el escritor, el importador o la capa de almacenamiento.
- Por sí solo, el valor no dice nada sobre un evento ocurrido en otra máquina.

Este último punto se ignora porque un entero con aspecto global parece autoritativo. No lo es. Un número de secuencia necesita un espacio de nombres. `journal_id=macbook-17, seq=841` es una afirmación con límites. `seq=841` en una hoja de cálculo invita a sacar conclusiones que no corresponden.

## El horario de verano repite las lecturas del reloj local

El horario de verano hace que el reloj local repita una hora en muchas jurisdicciones. Durante la transición de otoño, las 01:15 ocurren una vez con un desplazamiento UTC y de nuevo con otro. Un registro que solo guarda `2026-11-01 01:15:00` ha descartado la información necesaria para distinguirlas.

No intentes arreglarlo después adivinando a cuál de las dos ocurrencias se refería el operador. Captura suficiente contexto al escribir el evento:

```json
{
  "journal_id": "build-mac-07",
  "seq": 842,
  "event_id": "01JXYZ...",
  "recorded_at_utc": "2026-11-01T08:15:00.000Z",
  "local_offset": "-07:00",
  "time_zone": "America/Los_Angeles",
  "event_type": "agent.http.requested"
}
```

En la ocurrencia posterior, la misma hora local podría llevar `local_offset: "-08:00"` y un valor UTC una hora posterior. El desplazamiento numérico conserva el hecho que observaste. El nombre de la zona horaria ayuda a explicar por qué existía ese desplazamiento, pero no debe sustituirlo en el registro.

Las reglas de las zonas horarias cambian. Los gobiernos han cambiado las fechas de inicio y fin, así como la existencia del horario de verano, sin preocuparse demasiado por tu analizador de registros. Si guardas una fecha local y el nombre de una zona, y años después vuelves a calcular el desplazamiento usando una base de datos de zonas horarias más reciente, puedes representar las pruebas históricas de forma distinta a como las vio la máquina en aquel momento. Conserva el valor UTC original y el desplazamiento capturado. Usa las reglas actuales para mostrar la hora solo cuando el visor indique claramente que se trata de una conversión actual.

La transición de primavera provoca otro fallo. Algunas horas locales nunca ocurren. Un sistema que acepta un plazo local introducido por una persona a las 02:30 durante la hora omitida debe rechazarlo o pedir una resolución explícita. Moverlo en silencio a las 03:30 es una decisión de producto disfrazada de aritmética del calendario.

Para los registros de auditoría, UTC debe ser el valor canónico. Muestra la hora local cuando ayude al lector, pero incluye el desplazamiento en el mismo campo. `2026-11-01 01:15:00 -08:00` es menos cómodo que `01:15`, pero también es una prueba.

## Una corrección manual debe crear un registro nuevo

Las ediciones manuales son el punto en que un rastro de auditoría conserva su valor o se convierte en un pulido feed de actividad. Si un administrador corrige una marca de tiempo en el propio registro, la observación original desaparece. El investigador ya no puede saber si el reloj antiguo era incorrecto, si lo era el contenido del evento o si alguien quería que el historial pareciera distinto.

Mantén inmutable el registro original. Añade un registro de corrección que identifique el evento anterior, indique el campo en disputa, registre el valor corregido propuesto y explique en qué se basa. La corrección no borra el primer registro. Añade un hecho posterior: alguien formuló y justificó una afirmación sobre el primero.

Un registro de corrección puede ser pequeño y aun así cumplir su función:

```json
{
  "seq": 913,
  "event_type": "audit.timestamp_corrected",
  "corrects_event_id": "01JXYZ...",
  "original_recorded_at_utc": "2026-11-03T14:10:12.481Z",
  "asserted_occurred_at_utc": "2026-11-03T14:00:12.481Z",
  "basis": "host time service report and neighboring journal entries",
  "actor": "admin-identifier"
}
```

Usa `asserted_occurred_at_utc` solo cuando tengas pruebas para la hora revisada. No cambies el nombre de `recorded_at_utc`; describe la lectura del reloj del registrador y sigue siendo históricamente correcta aunque esa lectura fuera inexacta.

Separa estas tres ideas tanto en el esquema como en el lenguaje:

1. `occurred_at` es la hora en que tuvo lugar una acción, si el actor puede establecerla.
2. `observed_at` es la hora en que un recopilador concreto vio la acción.
3. `recorded_at` es la hora en que el escritor de auditoría confirmó su entrada.

Pueden coincidir. A menudo coinciden. No deberían compartir el mismo nombre de campo.

La recomendación habitual de «corregir sin más los datos incorrectos» procede de los sistemas de informes, donde un número erróneo debe desaparecer del panel. Los sistemas de auditoría tienen otra función. Conservan el camino desde la observación hasta la conclusión. Una corrección visible resulta menos cómoda para los lectores ocasionales, pero evita que el sistema convierta la incertidumbre en certeza.

## Las correcciones de hora de red pueden mover el reloj civil

La sincronización horaria de red mejora la precisión del reloj, pero la propia corrección puede hacer que las marcas de tiempo resulten sorprendentes. Un cliente puede ajustar gradualmente su velocidad o mover el reloj de golpe cuando el desplazamiento es suficientemente grande o la política lo permite. Un usuario que cambia la hora manualmente, una máquina virtual que reanuda su ejecución y un reloj de firmware con un valor incorrecto pueden producir el mismo resultado visible: la hora del calendario cambia entre dos entradas de auditoría.

La documentación de POSIX sobre `clock_gettime` distingue entre un reloj de tiempo real y un reloj monotónico. `CLOCK_REALTIME` sigue la hora del calendario y se puede ajustar. `CLOCK_MONOTONIC` no tiene un origen civil útil, pero no lo modifica `clock_settime`; es el tipo de fuente adecuado para medir un intervalo dentro de un sistema en ejecución.

Esta diferencia proporciona una regla práctica para los datos. Registra la hora civil para el investigador. Registra una muestra monotónica cuando necesites razonar sobre el tiempo transcurrido, el comportamiento de los tiempos de espera o el orden alrededor de una corrección. No serialices un valor monotónico como si fuera una fecha. Su valor absoluto solo tiene significado en relación con su arranque y su dominio de reloj.

RFC 8633, la guía del IETF para las operaciones NTP, trata explícitamente los grandes desplazamientos horarios e indica que los operadores no deben saltarse a ciegas el umbral de pánico de NTP durante los arranques en frío. La tentación habitual es tratar cada corrección como una tarea de mantenimiento inofensiva. No lo es cuando la hora controla la validez de tokens, la retención, la reconstrucción de incidentes o la detección de repeticiones.

Tu sistema de auditoría debe informar de las anomalías horarias como hechos de auditoría. No debe intentar ocultarlas con una regla de ordenación. Un evento útil podría contener el último valor de la hora civil, el nuevo valor, la diferencia estimada, el origen del cambio si se conoce y el proceso que lo detectó. Si el sistema operativo no expone la causa, dilo. Una explicación falsa es peor que una explicación ausente.

Mantén una tolerancia pequeña para la variación normal. No generes un evento dramático de cambio de reloj porque los valores adyacentes difieran unos milisegundos en una dirección inesperada entre escritores simultáneos. El `seq` serializado proporciona el orden. Marca una discontinuidad cuando el reloj civil retroceda más allá de la precisión que declaras o cuando un salto hacia delante contradiga la actividad esperada y requiera revisión.

## Los números de secuencia necesitan un ámbito definido

Un número de secuencia solo funciona dentro del registro que lo asignó. Tratarlo como un orden global después de que los registros abandonen ese registro produce conclusiones erróneas en sistemas distribuidos de agentes.

Supón que un agente de programación en un Mac solicita una acción de API y un host de compilación remoto escribe una acción SSH. Cada host tiene su propio diario:

```text
build-mac-07  seq 842  14:00:13Z  HTTP action requested
build-host-2  seq  91  14:00:14Z  SSH action accepted
build-mac-07  seq 843  14:00:15Z  HTTP result received
```

Puedes decir que 842 precede a 843 en `build-mac-07`. Puedes decir que `build-host-2` registró 91 a la hora indicada. No puedes demostrar solo con esos campos si el host remoto aceptó la acción SSH antes o después de la primera solicitud HTTP en tiempo real.

Para establecer relaciones entre sistemas, añade un vínculo explícito. Un identificador de solicitud propagado del emisor al receptor puede conectar el evento de solicitud con el evento de recepción. Un recibo firmado por el receptor puede aportar pruebas más sólidas. Un recopilador central puede asignar una secuencia de recopilación cuando recibe los registros, pero esa secuencia demuestra el orden de llegada al recopilador, no el orden en que ocurrieron los eventos en los orígenes.

No exageres ninguna de estas afirmaciones:

- Un ID de solicitud demuestra correlación cuando ambos lados lo conservan. No demuestra la hora de entrega.
- Una secuencia central de recepción demuestra el orden de recepción. La latencia de red puede cambiar el orden de llegada.
- Un reloj sincronizado reduce la incertidumbre. No la elimina.
- Un reloj lógico distribuido puede expresar causalidad si todos los participantes lo transportan correctamente. No proporciona la hora civil.

En muchos rastros de auditoría de agentes no necesitas un gran sistema de ordenación distribuida. Necesitas límites honestos. Conserva una secuencia local para cada diario de confianza, incluye IDs de correlación en los límites de las acciones y muestra el diario de origen en cada exportación. Así, el revisor puede ver dónde las pruebas son sólidas y dónde empiezan las inferencias.

## Guarda suficientes pruebas temporales para explicar una discrepancia

Un campo `timestamp` aislado no es un esquema de auditoría. Es una preferencia de visualización que se ha escapado al almacenamiento.

Usa una estructura de registro que mantenga separados el orden, la hora del calendario, la identidad del origen y los elementos de integridad. Los nombres exactos de los campos son tuyos, pero los conceptos deben sobrevivir a la exportación y la retención:

```json
{
  "journal_id": "build-mac-07",
  "seq": 842,
  "event_id": "01JXYZ...",
  "recorded_at_utc": "2026-11-03T14:00:13.107Z",
  "recorded_offset": "-08:00",
  "time_zone": "America/Los_Angeles",
  "monotonic_ns": 3982188001123,
  "boot_id": "boot-identifier",
  "event_type": "agent.http.requested",
  "correlation_id": "request-identifier",
  "actor_process": "process-identifier",
  "previous_hash": "hex-value",
  "record_hash": "hex-value"
}
```

`boot_id` evita un error habitual con los valores monotónicos. Un contador monotónico puede reiniciarse después de un arranque, por lo que `3982188001123` de un arranque no se puede comparar con el mismo valor de otro sin más contexto. Guárdalo solo si vas a utilizarlo y documenta su unidad. Un campo llamado `monotonic_time` que obliga a adivinar si son nanosegundos, milisegundos o ciclos del sistema desperdicia el campo.

`recorded_offset` es el desplazamiento vigente cuando el escritor registró el evento. No sustituye a UTC. Permite que una persona vea el contexto de hora local del origen y que un formateador conserve la interpretación civil original. Incluye una zona horaria con nombre solo si la plataforma puede proporcionarla de forma fiable. Un desplazamiento fijo basta para ordenar y reconstruir.

Los campos de hash necesitan reglas igual de claras. Calcula un hash del registro sobre una representación canónica de cada campo cuya alteración cambiaría el significado del registro, incluidos `seq`, las marcas de tiempo, el tipo de evento, la identidad del actor y el resumen del contenido. No calcules el hash sobre un bloque JSON con formato si distintos serializadores pueden cambiar los espacios, el orden de las propiedades o el formato de los números. Canoniza primero.

Una cadena de hashes detecta un registro modificado cuando la verificación parte de un ancla de confianza y cada registro confirma el anterior. No demuestra que el reloj del origen fuera exacto. No demuestra que un evento ocurriera fuera de la máquina. Demuestra una afirmación más limitada, pero importante: el historial verificado ha conservado las relaciones criptográficas que produjo el escritor.

Por eso una cadena y una secuencia deben ir juntas. La secuencia proporciona el orden local. La cadena hace visible una reescritura posterior. La marca de tiempo añade contexto de calendario. Ninguna puede hacer el trabajo de las demás.

## Investiga los retrocesos horarios sin inventar una historia

Cuando una secuencia posterior tiene una marca de tiempo anterior, empieza por las pruebas que ya tienes. No empieces llamándolo repetición, error del agente o manipulación.

Usa esta breve secuencia de investigación:

1. Verifica la continuidad de la secuencia del diario y el resultado de integridad antes de interpretar los valores horarios.
2. Compara los valores UTC anterior y posterior, los desplazamientos locales, los identificadores de arranque y cualquier muestra monotónica.
3. Comprueba si el host atravesó un cambio de horario de verano, se reinició, reanudó su ejecución o registró una corrección del servicio horario.
4. Sigue los IDs de correlación en los diarios adyacentes, pero considera estimada la sincronización entre hosts salvo que un recibo o un mecanismo de ordenación compartido la demuestre.
5. Añade un registro de anotación si las pruebas respaldan una corrección o un hallazgo de anomalía horaria.

Este es un fallo que aparece en revisiones reales. Un agente solicita una acción de API a las `09:02:04`, la máquina se activa, la hora de red retrocede cuatro minutos y el resultado de la acción se registra a las `08:58:07`. Un panel ordena cronológicamente y muestra el resultado antes que la solicitud. Un operador concluye que el resultado se repitió, bloquea al agente y empieza a rotar las credenciales.

La secuencia y los valores monotónicos cuentan una historia más sencilla. La solicitud es la secuencia 117. El resultado es la secuencia 118. Ambos tienen el mismo identificador de arranque. El contador monotónico avanza unos tres segundos. La hora civil cambió entre las dos entradas. En ese diario, el resultado siguió a la solicitud y la hora mostrada es incorrecta a causa de la corrección.

Esa conclusión aún deja preguntas útiles. ¿Por qué el reloj difería en cuatro minutos? ¿Dependía la acción de una credencial con caducidad limitada por tiempo? ¿Otro sistema recibió la solicitud antes de la corrección? La investigación debe responder esas preguntas por separado. No debe forzar un orden limpio de marcas de tiempo solo porque el visor lo prefiera.

Si falla una comprobación de integridad, deja de tratar el diario como una línea temporal establecida. Conserva la exportación, registra el fallo de verificación fuera del diario afectado si es posible y consigue una copia nueva mediante una ruta independiente. Una cadena fallida no identifica a la persona o al proceso que cambió los datos. Indica que las pruebas ya no respaldan la afirmación de que el historial no fue modificado.

## Un rastro a prueba de manipulaciones necesita verificación sin conexión

Un registro de auditoría tiene poco valor forense si su única prueba de integridad depende del servicio activo que lo produjo. El revisor debe poder exportar los registros, llevarlos a otra máquina y verificar la cadena sin usar el almacén ni pedir ayuda al agente que realizó las acciones.

Sallyport proyecta sus diarios Sessions y Activity desde un único registro de auditoría cifrado y encadenado mediante hashes, y `sp audit verify` puede verificar la cadena sin conexión sobre el texto cifrado y sin una clave del almacén.

Ese diseño aborda un problema distinto de la precisión del reloj. La verificación sin conexión indica si el historial cifrado conserva su coherencia interna conforme al diseño de auditoría. No certifica el reloj civil del host. Mantén separadas las dos afirmaciones en los informes: «la cadena de auditoría se verificó» y «la hora del evento fue corroborada por una fuente independiente» son útiles, y ninguna implica la otra.

Los procedimientos de exportación deben conservar la identidad del diario, el intervalo de secuencias, el resultado de la verificación y la versión del software que realizó la verificación. Un CSV con solo la marca de tiempo y el texto de la acción es un informe, no una prueba de auditoría. Pierde los campos que permiten cuestionar o confirmar el informe más adelante.

## Diseña el visor para mostrar discrepancias, no solo ordenaciones ideales

Un buen visor de auditoría no oculta las anomalías del reloj. Ordena por secuencia del diario de forma predeterminada dentro de un diario, muestra UTC junto a la hora local cuando el usuario amplía una fila y marca un retroceso de la marca de tiempo como una discontinuidad horaria, sin reorganizar el historial.

Ofrece varias vistas, pero ponles nombres precisos. «Orden del diario» significa orden de secuencia. «Hora civil indicada» significa orden por marca de tiempo y puede colocar primero registros causalmente posteriores. «Orden de llegada al recopilador» significa el orden en que otro servicio recibió los datos. Evita una ordenación genérica por «hora», porque oculta una decisión forense importante.

La interfaz también debe mostrar el alcance de su certeza. Cuando los eventos procedan de diarios distintos, agrúpalos por origen o muestra una etiqueta de origen clara junto a cada número de secuencia. Si un ID de solicitud conecta registros, muestra esa relación como un vínculo tanto en el modelo de datos como en la interfaz. No dibujes una línea temporal continua entre hosts si tu sistema no puede defenderla.

La primera acción es sencilla: revisa una de tus exportaciones actuales en busca de una hora local sin desplazamiento, un número de secuencia sin identidad de diario o una ruta de edición directa. Cualquiera de ellos basta para crear una línea temporal de incidente engañosa. Corregirlo ahora cuesta menos que explicarlo después de que una acción del agente se haya convertido en una prueba.
