Skip to Content
Developer DocsServicesApproval Service

Approval Service

The Approval Service implements the Human-in-the-Loop (HITL) approval system for AEGIS. It manages approval requests created by the orchestration engine at mandatory checkpoints, records reviewer decisions, handles escalation for timed-out requests, and writes HMAC-signed entries to the append-only audit trail.

Overview

All regulatory filings produced by AEGIS agents must be reviewed and approved by a human before submission. The approval service provides the backend for this workflow:

  1. The orchestration engine detects a HITL checkpoint (e.g., pre_filing, good_cause_review)
  2. It creates an approval request via this service, including a snapshot of the current execution state
  3. The approval sits in pending status until a reviewer decides (approve, reject, or modify)
  4. Once decided, the orchestration engine can resume execution

The service also implements time-based escalation: if a pending approval exceeds the configured timeout (default 24 hours), it can be reassigned to a fallback reviewer.

Every action (creation, decision, escalation) is recorded in the HMAC-signed, append-only audit trail.

Port & Language

PropertyValue
Port8004
LanguagePython 3.12
FrameworkFastAPI
Entry pointsrc/approval/main.py

Key Endpoints

MethodPathDescription
POST/approvalsCreate a new approval request for a HITL checkpoint.
GET/approvalsList approval requests with optional filters (status, reviewer_id, agent_id). Supports pagination.
GET/approvals/{approval_id}Get a single approval request with full state snapshot.
POST/approvals/{approval_id}/decideRecord a decision: approved, rejected, or modified.
POST/approvals/escalateCheck for timed-out approvals and optionally reassign to a fallback reviewer.
GET/healthHealth check.

Architecture

Module Breakdown

src/approval/ ├── main.py # FastAPI app, all endpoint definitions ├── config.py # Settings from environment variables ├── schemas.py # Pydantic request/response models ├── repository.py # PostgreSQL CRUD for approval_requests table ├── audit.py # HMAC-signed audit trail writer └── seed_filings.py # Demo filing data seeder

Approval Lifecycle

Created (pending) ├── Reviewer approves → status: "approved" ├── Reviewer rejects → status: "rejected" ├── Reviewer modifies → status: "modified" (modified_output stored in snapshot) └── Timeout exceeded → Escalated (reassigned to fallback reviewer, stays "pending")

Approval Request Fields

Each approval request stores:

FieldDescription
idUUID of the approval request
execution_idThe orchestration execution that triggered this checkpoint
agent_idWhich agent created this request
checkpoint_typeThe type of checkpoint (e.g., pre_filing, good_cause_review, technical_review)
state_snapshotFull JSON snapshot of the execution state at the time of the checkpoint
reviewer_strategyEither named_individual or role_based
reviewer_idThe specific reviewer assigned (for named_individual strategy)
statuspending, approved, rejected, or modified
decisionThe recorded decision
reviewer_commentsFree-text comments from the reviewer
decided_atTimestamp of the decision

Decision Types

DecisionBehavior
approvedThe filing package is approved as-is. The orchestration engine can proceed.
rejectedThe filing is rejected. The reviewer should provide comments explaining why.
modifiedThe reviewer has edited the output. The modified_output dict is stored in the state snapshot for the orchestration engine to use on resume.

Reviewer Assignment Strategies

StrategyDescription
named_individualA specific reviewer_id is assigned. This reviewer must decide.
role_basedAny user with the appropriate role can decide. The approval is visible to all eligible reviewers.

Escalation Logic

The /approvals/escalate endpoint finds all pending approvals whose created_at timestamp exceeds the configured timeout:

SELECT * FROM approval_requests WHERE status = 'pending' AND created_at < NOW() - INTERVAL '1 second' * $1

If a fallback_reviewer_id is provided, timed-out approvals are reassigned to that reviewer. Each escalation is recorded in the audit trail.

In production, the escalation endpoint would be called by a cron job or scheduler. In development, you can call it manually to test the escalation flow.

Audit Trail (audit.py)

Every approval action writes a signed entry to the audit_logs table:

  1. Payload construction: The event data dict is JSON-serialized with sorted keys
  2. HMAC signing: A SHA-256 HMAC signature is computed using the HMAC_SIGNING_KEY
  3. Insert: The entry is inserted into audit_logs with the signature
signature = hmac.new( settings.HMAC_SIGNING_KEY.encode(), json.dumps(event_data, sort_keys=True).encode(), hashlib.sha256, ).hexdigest()

The audit_logs table has database triggers that prevent UPDATE and DELETE operations, ensuring the trail is truly append-only.

Audit event types:

Event TypeTrigger
approval.createdNew approval request created
approval.approvedReviewer approved the request
approval.rejectedReviewer rejected the request
approval.modifiedReviewer modified and approved
approval.escalatedApproval timed out and was escalated

Dependencies

Python Packages

PackageVersionPurpose
fastapi^0.115Web framework
uvicorn^0.34ASGI server
asyncpg^0.29PostgreSQL async driver
aegis-sharedlocalShared DB helpers (PostgresPool)

Infrastructure Dependencies

DependencyPurpose
PostgreSQL 15Approval requests table, audit trail

Configuration

Environment VariableDefaultDescription
APPROVAL_HOST0.0.0.0Bind address
APPROVAL_PORT8004Bind port
DATABASE_URLpostgresql://aegis:aegis_local@localhost:5432/aegisPostgreSQL connection
HMAC_SIGNING_KEYaegis-local-hmac-key-change-in-productionKey for HMAC-SHA256 audit signatures
ESCALATION_TIMEOUT_SECONDS86400Timeout before escalation (24 hours)
DB_SCHEMApublicDatabase schema prefix for tables

The HMAC_SIGNING_KEY must be kept secret and consistent across deployments. Changing the key will make it impossible to verify the signatures of existing audit log entries. In production, this should be sourced from HashiCorp Vault.

Running Locally

cd services/approval-service poetry install poetry run uvicorn approval.main:app --reload --port 8004

Requires PostgreSQL to be running with the approval_requests and audit_logs tables created (handled by infrastructure/docker/postgres/init.sql).

Last updated on