Approval workflows overview
Some tool calls are too consequential to gate behind allow/deny alone — delete_database, send_payment, rotate_secrets. AI.Sentinel's approval workflow lets you require a human approval out-of-band for matched tool calls, then resume the conversation when the approval lands.
When to use this vs. plain authorization
| You want... | Use |
|---|---|
| Allow/deny based on caller identity / role | IAuthorizationPolicy |
| Allow/deny based on caller identity, AND a human approves first | This (RequireApproval) |
RequireApproval is additive — your existing RequireToolPolicy(...) bindings are unchanged. The same tool can have both: e.g. authorize "is this caller in the DBA group?" and then require a separate approver to sign off.
The lifecycle
- Tool call hits the guard — middleware (
AuthorizationChatClient) or CLI hook routes throughIToolCallGuard. - Guard returns
RequireApprovalDecision— carries aRequestId,ApprovalUrl, andWaitTimeout. - Approval pends — the configured
IApprovalStore(InMemory / SQLite / Entra PIM) holds the request until an approver settles it. - Caller observes the outcome:
- In-process middleware: blocks via
WaitForDecisionAsync, then re-evaluates the guard. - Hook CLIs (
sentinel-hook,sentinel-copilot-hook): emits a deny-with-receipt, the user approves out-of-band and retries the prompt. - MCP proxy: wait-and-block when
SENTINEL_MCP_APPROVAL_WAIT_SECis set; fail-fast otherwise.
- In-process middleware: blocks via
The three backends
| Backend | Persistence | Approver experience | Best for |
|---|---|---|---|
| In-memory | Process lifetime | Dashboard | Single-process apps; dev/demo |
| SQLite | File on disk | Dashboard | CLI deployments; multi-process on one host |
| Entra PIM | Azure AD | Native PIM portal | Enterprise tenants with PIM already in place |
Backends are exclusive — one IApprovalStore per process. Switching is a config change; no code change.
Wiring in code
Add a RequireApproval binding alongside your existing severity/policy config:
services.AddAISentinel(opts =>
{
opts.OnHigh = SentinelAction.Quarantine;
opts.RequireApproval("delete_database", spec =>
{
spec.GrantDuration = TimeSpan.FromMinutes(15);
spec.RequireJustification = true;
spec.BackendBinding = "DBA"; // role name passed to the backend
});
});
For CLIs (where source-edits aren't an option), use a config file instead.
Next steps
- Pick a backend: in-memory, SQLite, or Entra PIM.
- For CLI hosts: config file reference.
- Approver UX: pending-approvals dashboard panel.