Overview
When a request exceeds the risk threshold, it enters the approval queue and follows a 5-state state machine:The default TTL is 1 hour after approval (configurable via
APPROVAL_EXECUTE_TTL_HOURS). Approved requests that aren’t executed within the TTL expire automatically.State Machine
The approval state machine is implemented inbackend/src/services/approval.service.ts.
States
Transitions
State transitions are race-safe using conditional WHERE clauses:Conditional updates prevent race conditions. If two processes try to update the same action simultaneously, only one succeeds.
Transition Diagram
Agent Polling Protocol
When an agent receives a 428 response, it should pollGET /status/:actionId until the status changes.
Response Shapes by Status
- PENDING
- APPROVED
- DENIED
- EXPIRED
- EXECUTED
Polling Implementation
Implementation inbackend/src/routes/approval.ts:21:
Polling Best Practices
Polling Interval
Polling Interval
Recommended: 5-10 secondsWhy:
- Too fast: wastes server resources, may trigger rate limits
- Too slow: delays execution, poor user experience
Timeout Handling
Timeout Handling
Set a maximum polling duration to avoid infinite loops:
Error Handling
Error Handling
Handle transient errors gracefully:
Human Approval
Humans approve or deny requests via the dashboard API.Approve Action
- Transitions status:
PENDING → APPROVED - Sets
resolvedAttimestamp - Sets
approvalExpiresAttonow + APPROVAL_EXECUTE_TTL_HOURS
Deny Action
- Transitions status:
PENDING → DENIED - Sets
resolvedAttimestamp
List Pending Approvals
Execution Flow
After approval, the agent callsPOST /proxy/execute/:actionId to execute the request.
Execution Steps
Implemented inbackend/src/routes/proxy.ts:141:
- Authenticate agent — verify
Agent-Keyheader - Fetch approval entry — look up by
actionId - Ownership check — verify agent owns the action (return 404 if not)
- Status check — must be
APPROVED(409 Conflict otherwise) - TTL check — if
approvalExpiresAtpassed, transition toEXPIREDand return 410 Gone - Credential injection — decrypt vault secrets and inject into stored headers
- Forward request — send stored request to target API
- Cache response — transition to
EXECUTEDand store response in database - Return response — send cached response to agent
Execution Request
Execution Response
The response mirrors the target API response:X-Proxy-Status header distinguishes executed approvals from low-risk passthrough requests.
TTL Expiration
If the agent doesn’t execute within the TTL window:Background Cleanup Job
A background job expires stale approvals every 5 minutes (server.ts:195):
Cleanup Implementation
APPROVED status. If an agent executes just before cleanup runs, the conditional WHERE prevents the race.
Database Schema
Theapproval_queue table schema (backend/src/db/schema.ts:151):
Sequence Diagram
Complete flow from block to execution:Error Scenarios
Agent Executes Expired Approval
Agent Executes Expired Approval
Scenario: Agent polls too slowly and TTL expiresResponse:Recovery: Resubmit original request via
POST /proxyHuman Denies Request
Human Denies Request
Scenario: Human reviews request and denies itResponse:Recovery: Agent should handle gracefully (log, notify user, adjust behavior)
Service Deleted During Approval
Service Deleted During Approval
Scenario: Service is deleted while request is pendingResponse:Recovery: Cannot execute, request must be abandoned
Agent Tries to Execute Another Agent's Action
Agent Tries to Execute Another Agent's Action
Scenario: Agent tries to execute an action belonging to a different agentResponse:Security: Ownership check prevents info leakage (returns 404, not 403)
Configuration
TTL Duration
Configure the execution window in.env:
backend/src/routes/dashboard.ts):
Cleanup Frequency
The cleanup job runs every 5 minutes (hardcoded inserver.ts:195). To change:
Related Concepts
Risk Assessment
Learn how requests enter the approval queue
Architecture
See where the approval queue fits in the system
Security Model
Understand why credentials are re-injected at execution