recalled.dev
Concepts de base

Audit d'agents

Recalled est pensé pour les actions humaines et celles des agents IA, côte à côte. En 2026, la moitié des actions d'un SaaS typique sont prises par des agents IA (Claude, GPT, agents custom branchés en tool calls), et le même audit log les enregistre avec la même chaîne de hash, les mêmes signatures, le même dashboard. Deux petites conventions en font le système de référence pour ce que tes agents ont fait, quand, et au nom de qui.

Le pattern

L'agent lui-même n'appelle pas Recalled. Ton backend orchestre l'agent, exécute les tool calls, et logue les actions résultantes. Du point de vue de Recalled, un agent est juste un acteur avec un actor.type différent.

Deux conventions :

  1. actor.type est mis à "agent" (ou "ai_agent", choisis-en un et tiens-toi-y).
  2. metadata.triggered_by_user porte l'id du user humain qui a démarré la conversation menant à cette action.
ts
client.events.emit({
  action: "file.deleted",
  actor: {
    type: "agent",
    id: "claude-sonnet-4.6",
    name: "Support Triage Agent",
  },
  organization: "acme_corp",
  targets: [{ type: "file", id: "f_42", name: "old-report.pdf" }],
  metadata: {
    triggered_by_user: "user_123",
    conversation_id: "conv_xyz",
    tool_call_id: "call_abc",
    reasoning: "user asked to clean up old uploads",
    confidence: "high",
    model: "claude-sonnet-4.6",
    tokens_used: 1240,
    result: "success",
  },
});

Trois events par tool call

Pour la traçabilité complète, logue trois events autour de chaque tool call d'agent :

ts
// 1. L'agent a décidé d'appeler un tool
client.events.emit({
  action: "agent.tool_called",
  actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
  targets: [{ type: "tool", id: "delete_file" }],
  metadata: {
    triggered_by_user: "user_123",
    conversation_id: "conv_xyz",
    tool_args: { file_id: "f_42" },
  },
});

// 2. L'action elle-même (comme une action humaine)
client.events.emit({
  action: "file.deleted",
  actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
  targets: [{ type: "file", id: "f_42" }],
  metadata: { triggered_by_user: "user_123", on_behalf_of: "user_123" },
});

// 3. Le tool a renvoyé à l'agent
client.events.emit({
  action: "agent.tool_returned",
  actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
  targets: [{ type: "tool_call", id: "call_abc" }],
  metadata: {
    triggered_by_user: "user_123",
    result: "success",
    duration_ms: 142,
  },
});

Si la cardinalité devient un problème à grande échelle, drop tool_called et tool_returned et garde uniquement l'action elle-même avec une metadata riche. L'event d'action seul suffit pour la responsabilité.

Filtrer : humains vs agents

Une fois actor.type=agent posé partout, tu peux filtrer le dashboard ou l'API pour voir uniquement l'activité agents, uniquement l'activité humaine, ou tout pour un user donné :

  • Toute l'activité agents ce mois : ?actor_type=agent
  • Toutes les actions qu'un user humain a déclenchées, y compris celles exécutées par des agents en son nom : filtre par metadata.triggered_by_user
  • Audit trail pour un agent spécifique : ?actor_id=claude-sonnet-4.6

Receipts : preuve rejouable

Quand un agent dit "j'ai supprimé ce fichier pour toi", tu veux qu'il appuie sa réponse avec une preuve. Recalled émet un reçu pour n'importe quel event :

bash
curl https://api.recalled.dev/v1/events/$EVENT_ID/receipt \
  -H "Authorization: Bearer rec_live_..."

Tu obtiens un objet JSON que l'agent peut coller dans sa réponse, avec un view_url (page publique) et un verification_url (endpoint JSON sans auth). Le destinataire peut vérifier cryptographiquement que l'event a eu lieu, dans quel ordre, sans altération, sans clé API.

Dans Claude Desktop, Cursor ou n'importe quel client MCP connecté à Recalled, le tool get_event_receipt retourne le même objet pour que l'agent le cite sans passer par HTTP lui-même.

Ce que les auditeurs veulent vraiment voir

Si un client ou un auditeur conteste une action d'agent trois mois plus tard, voici ce qui rend le dossier solide :

  1. L'action est dans l'audit log avec actor.type=agent.
  2. Le receipt vérifie vert : chaîne intacte, signature HMAC valide.
  3. metadata.triggered_by_user lie l'action à l'humain qui l'a initiée.
  4. metadata.reasoning, metadata.confidence, metadata.model donnent le contexte.
  5. metadata.conversation_id permet de rejouer toute la conversation si besoin.

Page de vérification publique

Chaque receipt a une URL publique du genre https://recalled.dev/receipts/<event-id>. La page masque la PII de l'acteur et la metadata (le viewer public ne voit jamais qui ni quoi), et affiche uniquement :

  • Le nom de l'action
  • Le type d'acteur (agent, user, service, etc.)
  • Le timestamp
  • Les hashes et la signature
  • Un bandeau vert ou rouge selon que la preuve cryptographique tient

Donne cette URL plutôt qu'un accès au dashboard. N'importe qui peut confirmer qu'un event a existé, sans accéder à ton projet.

Privacy

La page receipt publique n'expose jamais :

  • Nom, email, id, IP, user agent de l'acteur
  • Metadata de l'event
  • Les autres events de la chaîne

Elle prouve uniquement que l'event id spécifique, avec ce verbe d'action et ce timestamp précis, a été enregistré par Recalled et n'a pas été altéré.