Skip to main content

Overview

When a request exceeds the risk threshold, it enters the approval queue and follows a 5-state state machine:
Agents poll for status changes and execute approved requests within a TTL window.
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 in backend/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 poll GET /status/:actionId until the status changes.

Response Shapes by Status

Agent action: Continue polling every 5-10 seconds

Polling Implementation

Implementation in backend/src/routes/approval.ts:21:

Polling Best Practices

Recommended: 5-10 secondsWhy:
  • Too fast: wastes server resources, may trigger rate limits
  • Too slow: delays execution, poor user experience
Example (Python):
Set a maximum polling duration to avoid infinite loops:
Handle transient errors gracefully:

Human Approval

Humans approve or deny requests via the dashboard API.

Approve Action

Effect:
  • Transitions status: PENDING → APPROVED
  • Sets resolvedAt timestamp
  • Sets approvalExpiresAt to now + APPROVAL_EXECUTE_TTL_HOURS
Response:

Deny Action

Effect:
  • Transitions status: PENDING → DENIED
  • Sets resolvedAt timestamp
Response:

List Pending Approvals

Response:
The request_headers and request_body fields have auth headers stripped. Credentials are never stored in the approval queue.

Execution Flow

After approval, the agent calls POST /proxy/execute/:actionId to execute the request.

Execution Steps

Implemented in backend/src/routes/proxy.ts:141:
  1. Authenticate agent — verify Agent-Key header
  2. Fetch approval entry — look up by actionId
  3. Ownership check — verify agent owns the action (return 404 if not)
  4. Status check — must be APPROVED (409 Conflict otherwise)
  5. TTL check — if approvalExpiresAt passed, transition to EXPIRED and return 410 Gone
  6. Credential injection — decrypt vault secrets and inject into stored headers
  7. Forward request — send stored request to target API
  8. Cache response — transition to EXECUTED and store response in database
  9. Return response — send cached response to agent

Execution Request

No request body needed — the gateway uses the stored request from the approval queue.

Execution Response

The response mirrors the target API response:
The X-Proxy-Status header distinguishes executed approvals from low-risk passthrough requests.

TTL Expiration

If the agent doesn’t execute within the TTL window:
The agent must resubmit the original request, which starts the approval flow over.

Background Cleanup Job

A background job expires stale approvals every 5 minutes (server.ts:195):

Cleanup Implementation

Race safety: Only updates rows still in APPROVED status. If an agent executes just before cleanup runs, the conditional WHERE prevents the race.

Database Schema

The approval_queue table schema (backend/src/db/schema.ts:151):

Sequence Diagram

Complete flow from block to execution:

Error Scenarios

Scenario: Agent polls too slowly and TTL expiresResponse:
Recovery: Resubmit original request via POST /proxy
Scenario: Human reviews request and denies itResponse:
Recovery: Agent should handle gracefully (log, notify user, adjust behavior)
Scenario: Service is deleted while request is pendingResponse:
Recovery: Cannot execute, request must be abandoned
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:
The TTL is set when the human approves the request (backend/src/routes/dashboard.ts):

Cleanup Frequency

The cleanup job runs every 5 minutes (hardcoded in server.ts:195). To change:

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