# Chronologie des actions d'un agent : comparer les horodatages sans mentir

Une chronologie des actions d'un agent peut raconter un mensonge très convaincant alors que chaque système a enregistré un horodatage exact. Le mensonge apparaît quand l'enquêteur traite l'affichage de l'horloge locale, la réception par un serveur API et un enregistrement d'audit comme des preuves interchangeables d'un même instant.

Conservez les horodatages tels qu'ils ont été observés, avec leur décalage numérique et leur source. Comparez-les ensuite comme des horloges distinctes, chacune avec sa propre signification. Cela demande un peu plus de données qu'un simple champ `created_at`, mais évite le rapport d'incident classique où un agent semble avoir agi avant de recevoir l'autorisation, ou bien où une requête API semble s'être terminée avant d'avoir commencé.

## Un seul horodatage ne peut pas décrire une action

Une action comporte plusieurs moments importants. Un agent peut décider d'appeler une API à un instant, envoyer les octets plus tard, atteindre le serveur plus tard encore, puis recevoir un résultat après la fin du traitement côté serveur. Chacun de ces événements peut compter lorsqu'il faut déterminer si l'agent a dépassé son autorité.

Un enregistrement courant contient au moins les affirmations suivantes :

- Le processus agent indique quand il a commencé l'action.
- Le serveur destinataire indique quand il a accepté la requête.
- Le serveur destinataire peut indiquer quand il a validé ou terminé le traitement.
- La passerelle d'action indique quand elle a reçu et transmis la requête.
- Une personne peut voir une heure locale dans une interface utilisateur.

Il ne s'agit pas de versions concurrentes d'un même champ. Ces valeurs décrivent des points différents dans une séquence causale. Si vous les réduisez à un seul horodatage normalisé lors de la collecte, vous perdez la distinction qui explique les files d'attente, les délais réseau, les nouvelles tentatives, les attentes d'approbation et les appels longs.

J'ai vu des équipes présenter une entrée d'audit API comme l'heure à laquelle un agent avait « fait l'action », alors que cette entrée indiquait seulement l'acceptation de la requête. L'erreur devient coûteuse lorsque le point de terminaison place le travail en file d'attente et effectue la modification beaucoup plus tard. L'agent peut avoir été arrêté avant la modification, mais sa requête en est tout de même la cause.

Utilisez des noms qui décrivent l'événement : `agent_action_started_at`, `gateway_received_at`, `server_received_at` et `server_completed_at`. Ils obligent le lecteur à se demander ce qui s'est produit à chaque étape. Un champ vague nommé `timestamp` encourage à inventer une réponse plus tard.

## Un décalage préserve un instant, un fuseau explique l'affichage

Un décalage UTC numérique transforme une heure affichée par une horloge en un instant précis. Un fuseau horaire nommé explique les règles civiles qui ont produit cette heure. Vous avez souvent besoin des deux, mais ils répondent à des questions différentes.

Considérez ces deux valeurs :

```text
2025-11-02T01:30:00-04:00
2025-11-02T01:30:00-05:00
```

Le cadran indique 1 h 30 dans les deux cas. Les deux instants sont pourtant séparés d'une heure. En Amérique du Nord, lors du retour à l'heure standard, l'heure locale se répète. Une valeur dépourvue de contexte comme `2025-11-02 01:30:00` ne permet donc pas à l'enquêteur de déterminer quel instant s'est produit.

La RFC 3339 traite directement ce problème. Son format d'horodatage utilise une date et une heure complètes, suivies de `Z` pour l'UTC ou d'un décalage numérique. La RFC autorise aussi `-00:00` pour indiquer que la source connaît l'heure, mais pas le décalage local. Cette distinction constitue une preuve utile. Ne réécrivez pas silencieusement `-00:00` en `Z` : l'UTC affirme un fait que la source n'a pas établi.

Un identifiant de fuseau comme `America/Los_Angeles` reste utile lorsqu'une approbation humaine, un ticket d'assistance ou un enregistrement d'écran fait référence à l'heure locale d'un bureau. Il permet de reproduire les règles calendaires en vigueur à cet endroit. Il ne remplace pas le décalage. Les règles des fuseaux peuvent changer, et un même fuseau applique des décalages différents selon la période de l'année.

Conservez l'horodatage reçu sous forme de chaîne, gardez son décalage original et dérivez-en un instant UTC pour le tri. Ne conservez pas uniquement une chaîne locale mise en forme. La mise en forme doit se faire à la périphérie, lorsqu'une personne choisit un fuseau d'affichage.

## Les horloges locale, serveur et d'audit répondent à des questions différentes

L'heure locale indique ce que l'opérateur ou l'hôte de l'agent croyait être l'heure. L'heure du serveur indique quand un service distant a observé ou effectué le travail. L'heure d'audit indique quand le système de référence a accepté un événement. Les enquêteurs doivent comparer les trois, sans en désigner une comme vérité universelle.

Commencez par la limite de l'événement. Si un agent demande `POST /deployments`, l'heure locale de l'action peut étayer son intention. L'heure de réception par le serveur peut établir le moment où le service distant est devenu responsable de la requête. L'heure de fin côté serveur peut établir le moment où l'état d'un déploiement a changé. Une entrée d'audit peut établir quand votre point de contrôle a observé la tentative et s'il l'a autorisée.

La latence réseau crée des écarts normaux entre ces valeurs. Les files d'attente en créent de plus importants. Les nouvelles tentatives compliquent encore la situation : un client peut utiliser un même identifiant d'action pour plusieurs tentatives, tandis que le serveur enregistre chaque tentative séparément. Une chronologie qui n'affiche que la première heure locale masque tout cela.

N'utilisez pas l'affichage d'un navigateur, l'invite d'un terminal ou l'horloge d'une capture d'écran comme arbitre, sauf si vous savez comment l'appareil synchronisait son heure. Ces affichages aident souvent à comprendre ce qu'une personne croyait, mais ils tranchent rarement un désaccord d'ordre très serré.

Une comparaison pratique utilise trois colonnes dans la feuille d'enquête :

| Source de preuve | À conserver | Question à laquelle répondre |
|---|---|---|
| Hôte de l'agent | Horodatage local brut, décalage, fuseau, identité du processus | Quand ce processus affirme-t-il avoir commencé ou reçu un résultat ? |
| Service distant | ID de requête, heure de réception, heure de fin, statut de réponse | Quand le service a-t-il accepté et effectué le travail ? |
| Système d'audit | ID d'événement, heure d'enregistrement, preuve d'intégrité, résultat de l'autorisation | Quand le point de contrôle a-t-il observé et autorisé ou refusé l'appel ? |

Les lignes doivent conserver leurs propres heures, même après l'ajout d'une colonne UTC calculée pour le tri. Un tableur propre avec une seule colonne temporelle paraît pratique, mais il dissimule la chaîne de preuve.

## Les changements d'heure d'été créent des heures en double et des heures manquantes

Les changements d'heure d'été révèlent les raccourcis liés aux horodatages, car ils brisent une hypothèse humaine : chaque minute locale se produit une seule fois et chaque journée a la même durée. Aucune de ces hypothèses n'est toujours vraie.

Lors du passage à l'heure standard, une heure locale se répète. Au printemps, une heure n'existe pas. Un analyseur qui accepte un horodatage local sans décalage doit choisir une règle, refuser l'entrée ou deviner. Deviner est inacceptable dans un parcours d'audit.

L'enregistrement suivant est exploitable parce qu'il contient à la fois un instant précis et le contexte civil qui l'a produit :

```json
{
  "event_id": "act_8f3c",
  "event": "authorization_granted",
  "observed_at": "2025-11-02T01:14:22-04:00",
  "zone": "America/New_York",
  "instant_utc": "2025-11-02T05:14:22Z",
  "clock_source": "agent_host"
}
```

Le champ `instant_utc` est dérivé. Conservez donc aussi la chaîne originale `observed_at`. Si un analyseur est modifié plus tard ou si un bug affecte la conversion, vous pourrez recalculer la valeur et expliquer la différence. Traitez les valeurs dérivées comme des résultats d'analyse, pas comme des remplacements des preuves sources.

Une abréviation de fuseau comme `EST` complique encore les choses. Les abréviations sont ambiguës selon les régions et n'indiquent pas toujours correctement le passage à l'heure d'été. Utilisez un identifiant de fuseau IANA pour le contexte civil et un décalage numérique pour l'instant. Si une source ne produit qu'une abréviation, enregistrez-la telle quelle et indiquez que sa signification reste à établir.

Les calendriers récurrents obéissent à leur propre règle. Stockez le calendrier sous forme d'heure locale associée à un fuseau IANA, puis calculez chaque occurrence selon les règles de ce fuseau. Stockez une action réellement exécutée comme un horodatage avec décalage. Un calendrier indique quand une tâche devait s'exécuter ; un enregistrement d'événement indique quand elle s'est exécutée.

## La dérive d'horloge transforme un ordre apparemment précis en fausse certitude

Une précision à la milliseconde ne signifie pas une exactitude à la milliseconde. Un ordinateur portable mal synchronisé peut produire des horodatages comportant six décimales tout en avançant ou retardant de plusieurs minutes sur un serveur. La veille, les pertes réseau, les machines virtuelles et une mauvaise synchronisation peuvent toutes créer ce problème.

Séparez la précision de l'incertitude dans votre modèle de données. La précision correspond au nombre de chiffres enregistrés. L'incertitude est l'intervalle dans lequel vous pensez que se situe l'instant réel. Un horodatage d'hôte `10:00:00.123Z` associé à une incertitude de deux secondes ne doit pas trancher un désaccord d'une seconde avec une API distante.

Mesurez le décalage de l'horloge lorsque c'est possible. Capturez l'heure d'une référence fiable avant une exécution d'agent, puis de nouveau après, et stockez la différence observée. Si la référence est distante, tenez compte du temps de transport de la requête. Une estimation par le point médian convient aux opérations approximatives, à condition de conserver la mesure au lieu de la présenter comme une correction exacte.

Par exemple, un collecteur envoie une requête à l'heure locale `10:00:00.000`, reçoit une réponse fiable à l'heure locale `10:00:00.200`, et la réponse indique `10:00:00.150Z`. L'heure du serveur se situe quelque part dans l'aller-retour. Le point médian local est `10:00:00.100`, ce qui laisse penser que l'hôte a environ 50 millisecondes de retard selon l'hypothèse habituelle d'un trajet symétrique. Cette hypothèse peut être fausse. Le résultat utile est donc une plage, pas une déclaration de vérité.

Les horloges monotones résolvent un problème plus précis. Une horloge monotone mesure le temps écoulé dans un processus en cours et ne saute pas lorsque l'heure civile change. Enregistrez une valeur de départ monotone et une durée en plus de l'heure civile lorsque vous devez prouver que l'action B a suivi l'action A dans un même processus. Ne convertissez pas les valeurs monotones en UTC et ne les comparez pas entre hôtes.

La documentation NTP fait une distinction pratique similaire : la synchronisation estime le décalage et la dispersion, sans garantir une heure parfaite. Considérez ces estimations comme des éléments de preuve lorsque l'ordre est suffisamment serré pour avoir de l'importance.

## Conservez les preuves brutes et l'heure normalisée dans le même enregistrement

Un schéma d'événement défendable conserve ce que chaque participant a réellement signalé et rend l'analyse reproductible. La structure JSON suivante convient à une action d'agent sans prétendre que tous les champs proviennent d'une seule horloge.

```json
{
  "action_id": "a91c2d7e",
  "attempt": 2,
  "agent": {
    "process_id": "p_4b71",
    "started_at": "2025-04-18T14:07:12.481-07:00",
    "zone": "America/Los_Angeles",
    "monotonic_start_ms": 9184421,
    "clock_uncertainty_ms": 750
  },
  "gateway": {
    "received_at": "2025-04-18T21:07:12.661Z",
    "authorized_at": "2025-04-18T21:07:14.034Z",
    "result_released_at": "2025-04-18T21:07:14.882Z",
    "audit_event_id": "aud_3e90"
  },
  "server": {
    "request_id": "req_7c19",
    "received_at": "2025-04-18T21:07:14.301Z",
    "completed_at": "2025-04-18T21:07:14.649Z",
    "status": 201
  },
  "normalization": {
    "sort_instant_utc": "2025-04-18T21:07:12.481Z",
    "method": "RFC3339 offset conversion"
  }
}
```

Ce schéma établit une limite que les équipes brouillent souvent : un identifiant d'action relie les enregistrements, tandis qu'un horodatage ordonne un événement dans cette action. Réutiliser un identifiant après une nouvelle tentative est logique. Réutiliser un même horodatage pour toutes les étapes ne l'est pas.

Enregistrez l'ID de requête du serveur, même si vous disposez déjà d'un ID d'action interne. Lors d'une investigation, l'identifiant propre au serveur est souvent le seul moyen fiable de distinguer une requête qui a expiré d'une requête qui n'a jamais quitté le client.

Évitez de ne stocker que des millisecondes depuis l'époque Unix, sauf si le producteur garantit l'UTC et documente sa source temporelle. Les valeurs d'époque se trient facilement, mais elles perdent le décalage original, le contexte d'affichage et parfois l'unité. Si vous les acceptez de tiers, enregistrez explicitement l'unité et conservez la représentation reçue.

## Une chronologie a besoin d'intervalles lorsque les preuves se chevauchent

Lorsque deux sources comportent une incertitude, calculez des intervalles au lieu d'imposer un ordre. Vous éviterez ainsi une erreur courante : l'enquêteur voit `10:03:01.010` et `10:03:01.400`, les trie, puis affirme que le premier événement a causé le second alors que les décalages d'horloge des deux machines sont inconnus.

Supposons que l'agent signale le début d'une action à `21:07:12.481Z` avec une incertitude de 750 millisecondes. Son intervalle possible va de `21:07:11.731Z` à `21:07:13.231Z`. La passerelle signale une réception à `21:07:12.661Z` avec une incertitude de 20 millisecondes. Les intervalles se chevauchent. Les horodatages seuls ne peuvent donc pas prouver que la passerelle a reçu l'appel après le début annoncé. La séquence du protocole peut encore étayer cette conclusion, mais les horloges civiles ne le peuvent pas.

Indiquez la raison de chaque affirmation concernant l'ordre. Voici des conclusions différentes :

- « La passerelle a enregistré la réception après que l'agent a émis la requête » découle d'une preuve de protocole ou d'une trace de requête corrélée.
- « L'horodatage de réception de la passerelle est ultérieur » découle uniquement des heures civiles affichées.
- « Les enregistrements établissent l'ordre compte tenu de leur incertitude » est valable lorsque les intervalles ne se chevauchent pas.
- « Les enregistrements ne permettent pas d'établir l'ordre » est la bonne conclusion lorsque les intervalles se chevauchent et qu'aucune preuve causale ne comble l'écart.

Cette quatrième conclusion déplaît, car les rapports d'incident recherchent une histoire nette. Inventer de la précision ne rend pas cette histoire meilleure. Cela donne au prochain lecteur une raison de douter de toutes les conclusions voisines.

Cela modifie aussi la conception des alertes. Ne signalez pas automatiquement un agent parce qu'un événement de passerelle semble précéder de quelques centaines de millisecondes le début local de l'agent. Signalez un temps écoulé négatif uniquement après application des limites connues du décalage, ou classez-le pour une investigation de l'état de l'horloge.

## Les approbations et l'exécution doivent conserver des horodatages distincts

Une approbation humaine prouve qu'une personne a autorisé une capacité à un moment donné. Elle ne prouve pas que l'agent a envoyé une requête au même instant, et encore moins qu'un système distant a terminé le travail demandé à ce moment-là.

Conservez les événements d'approbation séparément des appels. Un enregistrement d'approbation doit inclure l'acteur, le périmètre accordé, le processus ou la session concernés, l'heure observée et l'ID d'événement du système d'autorisation. Un enregistrement d'appel doit référencer cette autorisation lorsque c'est pertinent et conserver ses propres heures de réception et de transmission.

Cette distinction est particulièrement importante pour l'autorisation d'une session. Une approbation peut couvrir plusieurs actions pendant la durée de vie d'un processus. Si une action ultérieure cause un dommage, l'enquêteur doit répondre à deux questions distinctes : quand l'autorisation a-t-elle eu lieu, et le processus respectait-il encore la session approuvée lorsqu'il a effectué cet appel ? Un seul champ `approved_at` ne peut pas répondre aux deux.

Une approbation par appel crée une séquence plus étroite, mais laisse tout de même un intervalle. L'utilisateur peut approuver à 14:07:14, la passerelle peut envoyer la requête à 14:07:14.1 et le service distant peut valider la modification à 14:08:02. Un délai d'attente distant après l'envoi n'exclut pas que le service ait terminé l'action.

Dans un flux contrôlé par une passerelle, journalisez aussi les refus. Un appel refusé établit qu'un agent a tenté une action, même si aucune requête distante n'aurait dû être envoyée. Si l'agent réessaie après un changement d'autorisation, la chronologie doit contenir des tentatives séparées avec des preuves distinctes. N'écrasez pas un refus avec une réussite ultérieure.

## Examiner un déploiement contesté sans aplatir les preuves

Supposons qu'un agent ait demandé un déploiement après qu'un développeur a approuvé une session. Le développeur affirme ensuite que l'approbation a eu lieu après les heures de bureau, tandis que l'enregistrement du service distant indique que le déploiement a commencé avant l'approbation. Cette contradiction apparente vient souvent de la comparaison entre une heure locale affichée et une heure serveur UTC.

Les preuves contiennent les enregistrements suivants :

| Événement | Heure indiquée | Source |
|---|---|---|
| Approbation de la session | `2025-04-18T17:58:40-07:00` | Enregistrement local de l'autorisation |
| Début de l'appel de déploiement par l'agent | `2025-04-18T17:59:02-07:00` | Hôte de l'agent |
| Réception de l'appel par la passerelle | `2025-04-19T00:59:02.410Z` | Journal d'audit de la passerelle |
| Acceptation de la requête par le service distant | `2025-04-19T00:59:04Z` | Enregistrement d'audit du service |
| Fin du déploiement par le service distant | `2025-04-19T01:01:18Z` | Enregistrement d'audit du service |

Convertissez les deux premiers enregistrements, mais conservez leurs formes reçues. L'approbation a eu lieu à `00:58:40Z`, et l'agent a commencé à `00:59:02Z`. Les enregistrements de la passerelle et du service suivent une séquence causale plausible. Rien ne s'est produit avant l'approbation : quelqu'un a simplement lu `17:58` à côté de `00:59` comme si les deux valeurs provenaient de la même horloge.

Ajoutons maintenant la partie moins confortable. Supposons que l'incertitude estimée de l'hôte de l'agent soit de 90 secondes parce qu'il était resté en veille. Vous pouvez toujours dire que la passerelle a reçu un appel après que le système d'autorisation a enregistré l'approbation, car ces enregistrements reposent sur la passerelle et la trace d'autorisation. En revanche, vous ne pouvez pas utiliser l'heure locale de l'agent pour établir un ordre plus précis à l'intérieur de cet intervalle.

L'investigation doit conserver ces deux conclusions. L'une est solide, l'autre limitée. C'est préférable à une chronologie qui donne la même apparence de précision à tous les événements.

## Vérifier l'intégrité de l'audit avant de lui faire confiance pour situer les événements

Un journal d'audit ne peut indiquer si quelqu'un a modifié, réordonné ou supprimé des preuves que si vous vérifiez son mécanisme d'intégrité. Effectuez cette vérification avant d'utiliser les entrées d'audit pour établir une séquence. Posez ensuite une question distincte : quelle horloge a horodaté chaque entrée ?

Sallyport projette les journaux de sessions et d'appels à partir d'un journal d'audit chiffré et chaîné par hachage, et `sp audit verify` peut vérifier cette chaîne hors ligne sur le texte chiffré. Cette vérification permet à l'enquêteur de contrôler la continuité du journal sans exposer les secrets stockés.

L'intégrité ne transforme pas un horodatage d'audit en horloge mondiale parfaite. Une entrée valide prouve que le journal contient l'événement enregistré dans sa chaîne. Elle nécessite toujours une source d'horloge documentée, un format de décalage et une politique d'incertitude si vous souhaitez la comparer précisément à un serveur externe.

Effectuez la vérification sur la copie des preuves et enregistrez le résultat de la commande avec les éléments du dossier. Un enregistrement utile contient la commande, l'identifiant de l'artefact d'entrée, le résultat de la vérification et la personne ou le processus automatisé qui l'a effectuée. Ne collez pas seulement une ligne verte dans un ticket. Le prochain enquêteur doit pouvoir refaire la même vérification.

Le chaînage par hachage modifie aussi la manière de traiter les lacunes. Si la chaîne indique que des entrées manquent ou ont été modifiées, ne poursuivez pas discrètement avec une chronologie normalisée. Marquez l'intervalle concerné comme incomplet et recherchez des enregistrements indépendants côté serveur. Un segment d'audit manquant peut en dire davantage sur l'incident que les entrées qui l'entourent.

## Construire la vue d'investigation à partir d'hypothèses explicites

Une bonne vue d'investigation affiche les horodatages bruts, les conversions UTC, l'identité des sources et l'incertitude. Elle ne dissimule pas la conversion derrière un libellé de tableau de bord comme « heure de l'événement ». Le lecteur doit pouvoir comprendre pourquoi l'affichage place deux événements dans cet ordre.

Utilisez cette séquence pour assembler une chronologie d'action :

1. Collectez les enregistrements sources immuables et conservez leurs chaînes d'horodatage, identifiants et décalages d'origine.
2. Identifiez l'événement marqué par chaque horodatage : décision, approbation, réception par la passerelle, acceptation par le serveur, fin du traitement ou transmission du résultat.
3. Convertissez les horodatages comportant un décalage en UTC dans un champ distinct, en indiquant l'analyseur ou la méthode utilisée.
4. Estimez l'incertitude de l'horloge pour chaque source susceptible d'influencer l'ordre contesté.
5. Triez sur les instants normalisés, puis examinez les intervalles d'incertitude qui se chevauchent et les ID de requête avant de formuler des affirmations causales.

Ne convertissez pas un horodatage local en lui ajoutant le décalage actuel de l'enquêteur. Cette erreur décale les événements historiques lorsque l'enquêteur travaille dans un autre fuseau ou lorsque les règles d'heure d'été diffèrent. Analysez le décalage fourni avec l'enregistrement. Si l'enregistrement n'en contient pas, marquez-le comme non résolu jusqu'à ce que vous puissiez établir la zone source et les règles applicables.

Les équipes doivent tester ce fonctionnement avant un incident. Créez une action près d'un changement d'heure d'été dans un environnement hors production. Modifiez l'horloge d'un client dans une limite de test sûre. Forcez une nouvelle tentative et une réponse serveur retardée. Demandez ensuite à une personne qui n'a pas conçu la journalisation de reconstituer l'ordre. Si elle a besoin d'explications orales pour interpréter les preuves, le format des enregistrements est incomplet.

L'heure est rarement la seule preuve dans un incident impliquant un agent. Les identifiants de requête, le périmètre d'autorisation, l'identité du processus, les corps de réponse et les contrôles anti-altération établissent souvent des faits que les horloges ne peuvent pas établir. Gardez ces faits associés à leurs propres événements. La prochaine action contestée sera plus facile à expliquer, car vous aurez enregistré ce que chaque système savait au lieu de forcer tous les systèmes à s'accorder sur un horodatage fictif.
