# Como investigar logs de auditoria de API quando os registros do agente entram em conflito

Um provedor de API afirma que uma requisição alterou dados de produção. O registro do agente diz que ela nunca chegou a esse ponto. As duas afirmações podem ser verdadeiras, e tratar qualquer um dos logs como veredito é como uma equipe transforma uma divergência contida em uma resposta ruim ao incidente.

Investigue a ação como uma cadeia de observações. Descubra quem iniciou a execução, o que o agente tentou fazer, o que atravessou a fronteira das credenciais, o que o provedor aceitou e o que mudou depois. Os timestamps ajudam a ordenar essa cadeia. Os identificadores de requisição conectam os registros. Os resultados e o estado mostram se aquilo teve efeito. Eventos ausentes também são evidências, mas só depois que você descartar as formas comuns de desaparecimento dos registros.

Já vi pessoas começarem com uma planilha e imediatamente ordenarem tudo por horário. Isso é o caminho inverso. Um timestamp costuma ser o pior campo de associação disponível. Comece com identificadores estáveis e exportações imutáveis, depois use o horário para testar se a sequência proposta faz sentido.

## Preserve os registros antes que alguém atualize um painel

Capture as evidências originais antes de filtrar, tentar novamente, revogar acessos ou pedir que a equipe de suporte do provedor investigue. Painéis interativos mudam, tarefas de retenção são executadas e um retry pode criar o segundo evento que confunde o primeiro.

Abra uma pasta do caso com um ID e colete exportações brutas, não apenas capturas de tela. Inclua o registro da sessão do agente, os registros individuais das ações, a exportação de auditoria do provedor, os logs da aplicação do sistema afetado e qualquer registro de saída disponível para a equipe. Registre o horário da coleta em UTC, a pessoa que a realizou, a conta ou função usada e o filtro aplicado para gerar cada exportação.

Gere um hash de cada arquivo após a coleta. Um comando de shell é suficiente se o sistema operacional oferecer um utilitário SHA-256 padrão:

```
$ shasum -a 256 provider-events.json agent-activity.json
81b5777b8416320fe26cb8a8dddb6a9e736fab4f5e7aa5812bf6afeffc5f4e82  provider-events.json
a1e98c01992b51104fbc8c5fcbaa78e65db31f1edb3e546f4c14d0e6d3673ba  agent-activity.json
```

Coloque os hashes em uma nota simples do caso. O hash não prova que uma exportação do provedor estava completa. Ele prova que sua cópia de trabalho não mudou silenciosamente desde a coleta. São afirmações diferentes, mas os relatórios de incidentes costumam misturá-las.

Não transforme o JSON em uma planilha como primeira cópia. A normalização pode descartar campos duplicados, a ordem de arrays, frações de segundo, valores vazios e o corpo exato da requisição que mais tarde explicará a divergência. Mantenha uma exportação intocada e crie um arquivo analisado separado para trabalhar.

Se a divergência puder envolver vazamento de credenciais ou uso não autorizado, contenha o acesso de modo a preservar a sequência. Revogue uma sessão ativa do agente ou bloqueie o caminho da ação, se possível. Evite trocar a credencial do provedor antes de coletar os registros recentes de auditoria, a menos que um abuso ativo exija rotação imediata. A rotação às vezes é necessária, mas pode apagar o único caminho restante para atribuição.

## Um identificador de requisição vale mais que um timestamp

Associe os registros usando identificadores que sobrevivam às fronteiras: um ID de requisição do provedor, um ID de correlação fornecido pelo cliente, uma chave de idempotência, um ID de objeto retornado pela gravação e um ID de trace, se o provedor documentar um. Guarde todos os identificadores, pois o provedor pode expor IDs diferentes em cabeçalhos, eventos de auditoria, exportações de suporte e corpos de erro.

O melhor cenário é simples. O registro da ação diz que o agente chamou `POST /v1/invoices`; os cabeçalhos da resposta contêm `x-request-id: req_72M...`; a exportação do provedor inclui `req_72M...`; e a fatura criada tem `inv_4P...`. Agora você tem uma associação entre intenção, entrega, processamento pelo provedor e estado persistente.

Os casos difíceis são mais comuns. Um provedor pode atribuir um ID de requisição apenas depois de analisar a requisição. Uma falha de TLS, portanto, não tem ID do provedor porque a requisição nunca chegou à aplicação. Um gateway pode gerar um ID e o serviço seguinte gerar outro. Uma API assíncrona pode devolver um ID de tarefa e gravar o objeto solicitado minutos depois. Registre qual fronteira emitiu cada ID, em vez de reduzi-los a uma única coluna `request_id`.

Use uma tabela de reconciliação que deixe a incerteza visível:

| Campo | Registro da ação local | Registro do provedor | Sistema afetado |
| --- | --- | --- | --- |
| ID de correlação do cliente | `run-18-call-42` | `run-18-call-42` | ausente |
| ID da requisição do provedor | `req_72M...` na resposta | `req_72M...` | ausente |
| Método e caminho | `POST /v1/invoices` | `POST /v1/invoices` | fatura criada |
| Resultado | `504 timeout` | `202 accepted` | tarefa `job_91...` concluída |
| Horário do evento | `10:04:03.219Z` | `10:04:03Z` | `10:04:11.802Z` |

Essa tabela revela uma falha conhecida: o cliente atingiu o tempo limite, mas o provedor aceitou a gravação e a processou depois que o cliente desistiu. Seria errado chamar a ação de «falha» por causa do resultado do cliente. Também seria errado chamar o log do provedor de «prova de que o agente pretendia fazer isso». As evidências mostram que o agente enviou uma requisição aceita pelo provedor e que o cliente não recebeu uma resposta a tempo.

Se o provedor permitir uma chave de idempotência para gravações, use-a. O Internet-Draft do IETF sobre Idempotency-Key descreve bem o objetivo prático: o cliente repete uma operação HTTP insegura sem criar acidentalmente o mesmo efeito duas vezes. O comportamento varia de um provedor para outro, portanto leia a documentação sobre retenção e regras de correspondência. Não presuma que comparar apenas o endpoint seja suficiente.

Para APIs que aceitam cabeçalhos personalizados, gere um ID de correlação antes da chamada e envie-o em um cabeçalho documentado, como `X-Client-Request-ID`. Armazene-o com o evento local. Nunca coloque segredos, prompts, dados de usuários ou tokens brutos nesse ID. Um valor seguro não tem significado fora do caso, por exemplo `case-2025-041-run7-call18`.

## O horário pode refutar uma história, mas raramente prova uma

Use timestamps para delimitar eventos e detectar ordens impossíveis. Não os use como campo principal de identidade quando todas as fontes não tiverem um identificador melhor.

A RFC 3339 define um formato comum de timestamp para a Internet e recomenda a forma UTC em maiúsculas terminada em `Z`, como `2025-03-08T10:04:03.219Z`. Preserve a string original mesmo depois de analisá-la. A diferença entre `10:04:03Z` e `10:04:03.219Z` importa quando uma fonte arredonda para segundos e outra informa milissegundos.

Crie quatro campos de tempo para cada evento relevante:

- o timestamp da fonte exatamente como foi exportado
- o timestamp UTC normalizado
- o tipo do evento, como enviado, aceito, concluído ou registrado
- o responsável pelo relógio, como o Mac local, a borda do provedor, um worker do provedor ou o banco de dados

Um timestamp da borda do provedor pode anteceder um timestamp local de «resposta recebida» sem contradição. O timestamp de conclusão de um worker do provedor pode vir depois do encerramento do processo do agente. Um relógio local adiantado ou atrasado pode fazer uma ação parecer anterior ao início da sessão. Isso é funcionamento normal, não prova de adulteração.

Crie uma janela em torno de uma âncora conhecida, geralmente um ID de requisição ou o início da sessão. Comece com uma janela estreita para evitar associações acidentais. Amplie-a somente quando puder explicar o motivo: o provedor registra apenas segundos, a operação é assíncrona ou você mediu a diferença do relógio em relação a uma referência confiável. Registre a janela escolhida na nota do caso. «Pesquisamos aproximadamente em torno do horário» não é um método.

Tenha cuidado com o horário de ingestão dos logs. Muitos sistemas exibem `event_time` e `created_at`. O primeiro descreve quando o evento aconteceu segundo o sistema que o emitiu. O segundo pode descrever quando um agregador o recebeu ou indexou. Uma chegada tardia não significa execução tardia. Se um evento aparecer depois do início de um incidente, examine os dois campos antes de criar uma narrativa.

Um teste útil de ordenação pergunta apenas se a história proposta é possível. Um evento do provedor às 10:04:03, associado a um envio local às 10:04:03.219, pode ser possível se os relógios diferirem ou se o provedor arredondar para baixo. Uma conclusão alegada às 10:02, quando o provedor diz que aceitou a tarefa às 10:04, não é possível, a menos que você tenha misturado dois eventos ou entendido mal o campo.

## Separe tentado, entregue, aceito e concluído

As equipes costumam comprimir quatro estados diferentes na palavra «chamou». Esse atalho causa a maioria das disputas entre logs.

Um agente pode tentar uma ação ao construir uma requisição. Um componente local pode entregar bytes a um endpoint remoto. O provedor pode aceitar a requisição. Um worker downstream pode concluir o efeito. Cada etapa tem um registro e um modo de falha diferentes.

A especificação de Semântica HTTP, RFC 9110, deixa claro que o código de status é uma afirmação sobre a resposta do servidor, não um histórico completo da experiência do cliente. Um `202 Accepted` diz explicitamente que o processamento foi aceito, mas ainda não foi concluído. Um `204 No Content` diz que o servidor concluiu a requisição com sucesso, mas não explica sozinho todos os efeitos posteriores. Um timeout de rede pode não produzir resposta HTTP alguma, enquanto o servidor ainda processa a requisição.

Classifique cada evento contestado com um status como estes:

- **Somente tentado**: existe um registro local da ação, mas nenhuma evidência mostra que os dados chegaram à rede.
- **Entregue, resultado desconhecido**: a requisição saiu da fronteira local, mas o cliente não recebeu uma resposta confiável e o provedor ainda não tem um registro pesquisável.
- **Aceito, efeito pendente**: o provedor devolveu uma aceitação ou referência de tarefa, mas ainda não há estado concluído.
- **Concluído**: um resultado do provedor e uma mudança de estado observada são compatíveis.
- **Contradito**: as fontes fazem afirmações que não podem ser verdadeiras ao mesmo tempo depois que os significados dos campos são considerados.

«Resultado desconhecido» é uma conclusão legítima. Não o reclassifique como falha apenas porque o agente recebeu uma exceção. Em uma operação de gravação, essa exceção deve interromper retries automáticos, a menos que um mecanismo de idempotência ou uma verificação posterior torne o retry seguro.

O erro inverso é igualmente grave: uma resposta `200` não significa que o resultado de negócio pretendido aconteceu. Um endpoint pode devolver sucesso para uma requisição sintaticamente válida, enquanto uma validação posterior, uma tarefa assíncrona ou uma dependência downstream rejeita a alteração pretendida. Inspecione o objeto retornado, o status da tarefa ou o evento do sistema-alvo que o contrato da API define como conclusão.

## Eventos ausentes precisam de uma explicação delimitada

Um registro ausente pode significar que a requisição nunca aconteceu, mas também pode significar que você consultou o serviço errado, usou o escopo de conta errado, pesquisou a camada de retenção errada ou esperava um registro que o provedor nunca promete emitir.

Investigue eventos ausentes em uma ordem fixa. Primeiro, confirme a conta, o projeto, a região, o ambiente e o produto de API exatos. Os provedores frequentemente separam as visões de auditoria por um ou mais desses campos. Depois, pesquise por todos os identificadores e, em seguida, por uma janela de tempo e um endpoint documentados. Verifique se o provedor registra requisições aceitas, rejeitadas, chamadas do plano de dados, chamadas do plano de controle ou apenas ações administrativas. Depois, confira a retenção e o atraso da exportação. Por fim, avalie se um proxy, SDK ou fila assíncrona cria um evento diferente daquele que você esperava.

Vale lembrar de uma falha concreta. Um agente envia `POST /exports` e recebe um timeout de conexão. A equipe pesquisa os logs de auditoria do provedor pelo ID local do cliente e não encontra nada. Faz um retry e depois recebe duas notificações de conclusão da exportação.

A primeira requisição foi para um endpoint regional de ingestão. A tela de auditoria consultada mostrava apenas eventos do plano de controle. O provedor registrou a tarefa com um ID de exportação gerado, não com o cabeçalho do cliente, e o serviço de tarefas a concluiu depois do timeout. Nada nessa sequência exigia atividade maliciosa. A duplicação veio do retry de uma gravação antes da verificação de uma chave de idempotência, de um endpoint de consulta de tarefas ou de um marcador de negócio.

Essa falha também mostra por que a ausência precisa ser descrita com cuidado. Diga: «A exportação que coletamos não contém nenhum evento correspondente do plano de dados nesta janela», em vez de: «O provedor não tem registro». A primeira afirmação identifica a evidência e seu limite. A segunda faz uma afirmação que muitas vezes não pode ser sustentada.

Se um log era esperado, mas não está presente, preserve os parâmetros da consulta e capture a documentação do provedor que descreve a cobertura esperada dos eventos. Uma solicitação de suporte sem o ID exato da requisição, o escopo da conta, a janela UTC, o endpoint e os hashes das evidências desperdiçará dias.

## Os resultados precisam ser inspecionados além dos códigos de status

Compare a intenção declarada na requisição com o payload da resposta e o efeito observável. Os códigos de status informam sobre uma troca de protocolo. Eles não dizem se a requisição tinha o escopo correto, se o provedor aplicou um valor padrão ou se o agente enviou um identificador antigo.

Para cada ação, capture, quando a API expuser esses dados: método HTTP, caminho normalizado, ID da requisição, chave de idempotência, identidade do ator ou da credencial, código de status, hash do corpo da resposta, ID do objeto retornado e qualquer ID de tarefa assíncrona. Redija credenciais e dados sensíveis do payload antes de compartilhar amplamente, mas preserve um original protegido se a política permitir.

Um hash do corpo da resposta ajuda a distinguir dois registros `200` superficialmente idênticos. Calcule-o sobre os bytes brutos da resposta antes de embelezar o JSON. Se a API devolver JSON e a ordem dos campos mudar entre as camadas, preserve tanto os bytes brutos quanto uma cópia analisada e canônica. Não afirme que códigos de status iguais significam respostas iguais.

Depois, consulte o recurso que deveria existir ou mudar. Em uma criação, recupere o ID do objeto retornado e compare seu criador, horário de criação e atributos. Em uma atualização, recupere uma versão, revisão ou entrada de auditoria, se o serviço oferecer uma. Em uma exclusão, verifique se o objeto está ausente e se um registro de auditoria do provedor atribui a exclusão à mesma credencial.

É nesse ponto que credenciais amplas prejudicam as investigações. Se muitas ferramentas compartilham um token de API, o provedor geralmente consegue dizer que o token agiu, mas não qual processo local ou pessoa iniciou a ação. Trate a identidade da credencial como um marcador de fronteira, não como a identidade do ator.

## Um registro do gateway só é útil se registrar a fronteira

Um gateway de ações oferece um ponto de observação claro entre um agente e uma operação autenticada. Ele deve registrar o processo ou a execução que fez a chamada, o estado de autorização aprovado, a operação solicitada, o resultado devolvido ao agente e identificadores suficientes para associar os registros do provedor. Não deve entregar a credencial ao agente e depois chamar a telemetria local resultante de trilha de auditoria.

O Sallyport mantém credenciais de API e SSH em seu cofre criptografado, executa a ação diretamente e devolve o resultado ao agente, não o segredo. Seus diários Sessions e Activity são projetados a partir de um log de auditoria criptografado, encadeado por hashes e cego para gravação, oferecendo ao investigador registros tanto no nível da execução quanto da chamada. `sp audit verify` pode verificar essa cadeia offline sobre o texto cifrado, sem precisar da chave do cofre.

Esse design resolve uma lacuna específica. Um log do provedor pode identificar uma credencial e uma requisição de API. Ele não informa qual processo do agente recebeu permissão para usar essa credencial nem prova que o agente nunca viu o segredo. Um registro de auditoria local só responde a parte dessa pergunta quando a fronteira da credencial realmente está dentro do componente que gera o registro.

Não exagere o que um registro do gateway pode fazer. Ele não consegue registrar uma requisição que o contornou nem transformar uma API ambígua do provedor em uma API precisa. Ele oferece um ponto mais forte para comparar evidências e um local onde você pode revogar uma execução conhecida do agente enquanto a investigação continua.

## Escreva a conclusão como afirmações com evidências e limites

Uma boa conclusão permite que outro engenheiro reproduza seu raciocínio sem herdar suas suposições. Escreva afirmações separadas sobre invocação, permissão, entrega da requisição, processamento pelo provedor e efeito observado. Anexe os identificadores, timestamps, arquivos de origem e significados dos campos que sustentam cada afirmação.

Use uma linguagem compatível com o nível de confiança. «O diário de ações registra que o processo X solicitou `POST /v1/invoices` neste horário.» «A exportação do provedor contém uma requisição com o mesmo ID de requisição do provedor.» «A fatura existe e seus atributos correspondem à resposta registrada.» Essas são afirmações testáveis. «O agente definitivamente causou a fatura» só pode ser justificado quando as associações e a fronteira da credencial sustentarem essa conclusão.

Quando os registros discordarem, mantenha a divergência visível no relatório final. Não faça uma média dos timestamps nem descarte a fonte inconveniente. Informe a explicação mais provável, as alternativas que você descartou e as evidências que ainda faltam. Se não puder estabelecer se uma gravação foi concluída, registre-a como desconhecida e corrija o caminho da API antes de permitir retries automáticos.

A mudança prática depois de um incidente costuma ser pequena e pouco glamorosa: exigir um ID de correlação, manter a classe correta de eventos do provedor, preservar timestamps UTC fracionários e usar idempotência em gravações. Esses controles transformam a próxima divergência de uma discussão forense em uma reconciliação rápida.
