The HITL gate ensures no high-risk agent action proceeds without human judgment. This cookbook walks the full approval lifecycle — from the moment a governed action fires a request through risk-tier resolution, human decisions, and the break-glass escape valve — so you can trace every moving part before you encounter it in production.
If you have not read the authorization model yet, start with Governance. The guide Govern & approve covers the same machinery from an operator perspective; this page is the step-by-step reference.
The request lifecycle
An approval request opens deny-closed and time-boxed. It starts pending, carries an
expiry, and authorizes nothing until enough humans have decided it. The request cannot
lower its own bar — a matching approval policy is authoritative for the threshold and
timeouts.
The request is bound to a plan hash of the exact tool-call payload. This is the anti-TOCTOU defense: if the underlying action changes between request and decision, the hash will not match and the approval cannot bind. A stale approval never authorizes a different action.
Risk-tier resolution
Four tiers drive how much human control a governed action requires:
| Tier | What it means |
|---|---|
low / medium | Assigned only by explicit approval policy — nothing defaults here |
high | The default for anything that reaches the approval queue. One human approval required |
critical | Mandatory two-person floor (NIST SP 800-53 AC-3(2) dual authorization) |
The tier is re-derived from the live policy set at every decision, never read from a stored snapshot. A policy change takes effect immediately. A stale row can never hold the bar lower than the current classification — this is deny-closed by construction.
The built-in critical set covers the genuinely irreversible: production deploy and
retire, data deletion and erasure, security-enforcement and kill-switch changes, key
custody and rotation, and NHI offboarding.
An approval policy with an explicit risk_tier can raise or lower the default per action.
But it can never lower a critical action below the dual-control floor.
Step by step
1. A governed action triggers a request
A governed action — deploy apply, agent fire, budget override, or any action matching an
approval policy — reaches the approval engine. The engine opens a new request in pending
state.
2. The request binds to a plan hash
The request records a hash of the exact tool-call payload. This plan-hash binding is the TOCTOU defense: the decision will only authorize the action the request was created for.
3. Risk tier resolves from current policy
The engine evaluates the live policy set (RBAC, native ABAC, and any external PDP) to derive the risk tier. The tier is never cached from an earlier evaluation.
4. A human reviewer decides
A human with sufficient role issues an approve or deny. The server enforces three
invariants at this point, keyed on stable user identity:
- Separation of duty. The requester cannot decide their own request. This keys on the stable user identity, not the credential string a single person could vary.
- Duplicate-decider guard. A unique index backstops a duplicate-decider race — one human counts once toward the threshold.
- Expiry binds. An expired request can never receive a binding decision, even before a sweep materializes the expiry. The effective status is re-derived at every decision.
5. Critical actions: dual-control with AAL3
For critical tier: the threshold is floored at two distinct human approvers. The
floor is enforced both at create (the stored threshold can never start below two) and
re-applied at decide (a request created before the action became critical still cannot
cross with one human).
Each deciding human must have a fresh hardware-verified session — WebAuthn or PIV step-up (AAL3). A system token carries no human assurance and is refused.
6. Decision records to the ledger
Every decision appends an immutable row to the request’s decision trail and a ledger event recording the decision, the resulting status, and the risk tier under which it was made. The ledger is append-only, hash-chained, and Ed25519-signed — rewriting history is cryptographically detectable.
Expiry
Expiry is derived at read, not written once. An expired request can never receive a binding decision even if no background sweep has materialized the expiry yet. The effective status is always re-derived, so a slow sweep is a cosmetic delay, not a security gap.
Policy configuration
Approval policies let you override the default risk tier per action class. A policy with an
explicit risk_tier can move an action from high to low, or raise a medium to
critical. The one constraint: you can never lower critical below the dual-control
floor. The two-person requirement for irreversible actions is structural, not
configurable.
Break-glass
When a quorum is unreachable — the 03:00 incident where the second approver is asleep — break-glass provides an audited escape valve. It is loud by construction.
Activation requires all of:
- The activator is an admin, always a real human — a system token is refused.
- A fresh AAL3 step-up (WebAuthn or PIV).
- A written justification recorded in the same transaction.
- An actively recorded session as a precondition.
Time-boxing: the grant defaults to one hour, hard-capped at 24. An emergency that needs longer is an operating mode, not an emergency, and must go through normal dual-control.
Every use is recorded. Each action taken under break-glass appends an immutable row and a ledger event naming the grant, the action, and the subject. An action that proceeded under break-glass is permanently distinguishable from a properly approved one.
Forced post-review. A new grant cannot be activated while any prior grant is unreviewed. The review must come from a different human than the activator. You cannot stack emergencies to dodge scrutiny.
Break-glass relaxes a missing quorum; it never overrides an explicit rejection. A request someone deliberately denied stays denied.
What is recorded
Every decision — approve, deny, expire, or break-glass use — appends to two places:
- The request’s decision trail: an immutable sequence of decision rows on the request itself, each carrying the decider, the verdict, and the timestamp.
- The audit ledger: a hash-chained, Ed25519-signed event recording the decision, the resulting request status, and the risk tier under which it was made.
Both writes happen in the same transaction as the state change. You cannot make a governed decision the ledger silently forgets.
Related
- Govern & approve — the operator guide covering the same machinery.
- Governance — the authorization model, self-audit posture, and kill-switch mechanics.
- Cookbook: kill-switch drill — the estate-wide deny gate exercise.
- Kill switch — the product page for the emergency stop.