Passer au contenu principal
Certains appels d’outils sont trop lourds de conséquences pour être autorisés aveuglément et trop utiles pour être bannis purement et simplement — une écriture en base de données de production, un virement bancaire, un *.delete sur des données réelles. Pour ceux-là, vous voulez une personne dans la boucle : mettre l’appel en attente, laisser un humain regarder, puis ne procéder que sur un oui. C’est exactement ce que fait le verdict pending_approval. Cette page couvre le flux d’approbation d’agent human in the loop de bout en bout : comment un appel mis en attente apparaît, comment un relecteur le résout depuis la console ou un webhook, et comment l’agent re-soumet l’appel approuvé. Pour savoir où le verdict se situe dans la grammaire des règles, voir Règles du Firewall ; pour le modèle de politique qui l’entoure, voir la Vue d’ensemble du Firewall.

1. À quoi ressemble un appel mis en attente

Quand une règle se résout en pending_approval, le moteur met en file d’attente un enregistrement d’approbation et l’appel n’atteint pas l’outil. Le relais renvoie HTTP 400 avec error.code firewall_approval_pending ; l’id d’approbation que l’agent interrogera est porté dans le error.message lisible par un humain :
{
  "error": {
    "code": "firewall_approval_pending",
    "message": "tool \"db.write\" held for approval (…) — resolve approval 507f1f77bcf86cd799439011 and retry with header X-OrcaRouter-Firewall-Approval"
  }
}
Les error.metadata structurées (lorsqu’elles sont présentes) portent le détail de la raison du verdict — reason_code, factors, risk_score — pas l’id d’approbation. Extrayez l’id du message, ou obtenez-le depuis le helper SDK ci-dessous. La mise en attente est immédiate — il n’y a pas de long-poll inline bloquant votre requête. L’agent récupère l’id, l’appel est garé côté serveur dans l’état pending, et la résolution se produit hors-bande.
Un appel mis en attente est enregistré comme un événement firewall avec le verdict pending_approval, donc il est filtrable dans le journal d’événements juste à côté des événements deny — vous pouvez toujours voir ce qui a été mis en attente et, via l’enregistrement d’approbation, ce qui a été résolu.

2. Un exemple concret

Rédigez une règle qui met en attente d’un humain toute écriture vers une connexion de production :
{
  "label": "hold prod db writes",
  "tool_name_glob": "db.write",
  "verdict": "pending_approval",
  "args_match_json": "{\"clauses\":[{\"path\":\"$.connection\",\"op\":\"eq\",\"value\":\"prod\"}]}"
}
Maintenant le cycle de vie :
1

L'agent appelle l'outil

L’agent émet db.write contre prod. La règle correspond, le moteur met l’appel en attente, et le relais renvoie 400 firewall_approval_pending avec un approval_id.
2

Un humain (ou votre système) relit

Un relecteur résout l’approbation — dans la console ou via un callback webhook signé (voir §3).
3

L'agent interroge jusqu'à résolution

L’agent interroge l’id d’approbation jusqu’à ce que son état ne soit plus pending (voir §4).
4

L'agent re-soumet avec l'en-tête d'approbation

Sur approved, l’agent ré-émet exactement le même appel une fois, portant un en-tête X-OrcaRouter-Firewall-Approval à usage unique. Le moteur réclame l’approbation et laisse passer cet unique appel.

3. Résoudre une approbation

Il y a deux façons de transformer une approbation pending en approved ou rejected. Les deux partagent une garantie first-decision-wins — la première résolution à atterrir est appliquée atomiquement, et toute résolution ultérieure (ou un doublon) est un no-op idempotent renvoyant 200.
L’onglet Approvals liste les mises en attente pending de la plus ancienne à la plus récente, chacune avec le nom de l’outil et une ligne « Held because… » nommant la politique et la clause de règle qui s’est déclenchée. (Les arguments bruts de l’appel ne sont pas stockés sur l’enregistrement d’approbation — seulement le nom de l’outil, la provenance et un hash des args — donc le relecteur décide à partir de l’outil plus la clause correspondante.) Un relecteur en résout une avec :
PATCH /api/workspace/firewall/approvals/:id
{ "decision": "approved", "reason": "verified change ticket #4821" }
decision doit être approved ou rejected. Cette route est UserAuth (la session console du relecteur) et filtrée à Developer+ — l’identité de votre relecteur est l’autorisation, donc aucun secret partagé n’est impliqué. Les résolutions sont écrites dans le journal d’audit de l’espace de travail.
Pour câbler les approbations dans un système externe (une approbation Slack, un workflow de ticketing), configurez un secret de webhook d’approbation pour l’espace de travail, puis POSTez la décision en retour :
POST /api/v1/firewall/approvals/:id/callback
{ "decision": "approved", "reason": "auto-approved by change-control bot" }
Le callback est authentifié par HMAC-SHA256 : définissez l’en-tête X-Orca-Signature: sha256=<hex> sur le HMAC de <approval_id>\n<raw_body> keyé avec le secret de webhook d’approbation de votre espace de travail. L’id fait partie du matériel signé, de sorte qu’une signature capturée ne peut pas être rejouée contre une approbation différente. Sans secret configuré, la résolution pilotée par callback est rejetée — résolvez plutôt via le PATCH de la console.
Configurer un chemin de rejet par webhook d’approbation est le défaut sûr pour les exécutions sans surveillance : si aucun humain ne résout une mise en attente, l’appel reste simplement garé et l’agent continue d’interroger. Un appel mis en attente ne devient jamais silencieusement un allow.

4. Interroger, puis re-soumettre

Le côté agent est une boucle d’interrogation suivie d’une seule re-soumission. Interrogez l’état de l’approbation avec un token scopé à la passerelle firewall :
GET /api/v1/firewall/approvals/:id
Cette route requiert un token avec la portée firewall-gateway (la même clé de passerelle dédiée utilisée pour /evaluate et la passerelle MCP) ; une clé de relais ordinaire reçoit un 403. Elle renvoie le document d’approbation — attendez jusqu’à ce que state soit approved ou rejected plutôt que pending. Un id inter-espaces-de-travail ou inconnu renvoie 404, ne révélant jamais qu’il existe à un autre tenant. Re-soumettez une fois que l’état est approved : ré-émettez le même appel d’outil, portant l’id d’approbation dans un en-tête à usage unique :
X-OrcaRouter-Firewall-Approval: 507f1f77bcf86cd799439011
Le moteur réclame atomiquement l’approbation — usage unique. La première re-soumission qui la porte est laissée passer cette unique fois ; un rejeu du même en-tête trouve l’approbation déjà consommée et est mis en attente à nouveau, pas autorisé. Une approbation rejected n’est jamais réclamable, donc l’agent devrait traiter le rejet comme un deny terminal et choisir un autre chemin.
Le helper HITL du SDK MCP d’OrcaRouter exécute cette boucle interroger-puis-re-soumettre pour vous : quand evaluate renvoie pending_approval, il interroge GET /api/v1/firewall/approvals/:id et re-soumet avec l’en-tête d’approbation à l’approbation — vous n’avez qu’à rédiger la règle et à pourvoir le relecteur.

5. États et rôles en un coup d’œil

ÉtatSignificationAction de l’agent
pendingEn attente d’une décisionContinuer d’interroger
approvedLe relecteur a dit ouiRe-soumettre une fois avec l’en-tête
rejectedLe relecteur a dit nonTraiter comme un deny
ActionRouteAuth · rôle
Lister la fileGET /api/workspace/firewall/approvalsUserAuth · Developer+
RésoudrePATCH /api/workspace/firewall/approvals/:idUserAuth · Developer+
Callback webhookPOST /api/v1/firewall/approvals/:id/callbackSigné HMAC
Interroger l’étatGET /api/v1/firewall/approvals/:idToken de passerelle

6. Où s’insèrent les approbations

Un verdict pending_approval est l’un des verdicts firewall — il se compose avec tout le reste d’une politique. Deux interactions à connaître :
  • La quarantaine de skill escalade en une mise en attente. Si un appel d’outil mis en attente appartient à un skill en quarantaine, tout ce qui est en-deçà d’un deny est escaladé en pending_approval automatiquement — la quarantaine et les approbations sont la même porte de revue dans deux directions.
  • Le mode shadow l’aplatit. En mode shadow, un verdict pending_approval est rétrogradé en audit et journalisé comme [shadow] would …, de sorte que vous pouvez mesurer à quelle fréquence une mise en attente se déclencherait avant qu’elle ne commence à filtrer le trafic réel.
C’est le bon contrôle pour les appels d’outils dangereux et l’agence excessive — les cas où un verdict « demander à un humain » bat à la fois allow et deny.

Où aller ensuite

Verdicts

Les six verdicts firewall et le verdict par défaut.

Clés de passerelle

Frappez le token firewall-gateway utilisé pour interroger les approbations.

Mode shadow

Mesurez une mise en attente avant qu’elle ne filtre le trafic réel.

Référence des règles

Rédigez la règle qui produit un verdict pending_approval.