Skip to content
VerifAIer
Home / Docs / Engineering / Runtime & request
Engineering · Lifecycles

Runtime & request lifecycle

What happens when the process starts, what state lives where, and the precise path of one HTTP request from socket to response model.

Runtime lifecycle

The runtime is a single FastAPI application defined in api/routes.py. Importing the module is the boot sequence, there is no separate bootstrap. The order is fixed and side-effectful:

src/vailidator/api/routes.py
flowchart TD
  A["import api.routes"] --> B["FastAPI(app) constructed"]
  B --> C["add CORSMiddleware
(CORS_ORIGINS env, default *)"] C --> D["add _SecurityHeadersMiddleware
(nosniff · DENY · XSS · Referrer-Policy)"] D --> E["init_db()"] E --> F{"SESSION_SECRET ephemeral
AND AUTH_COOKIE_SECURE?"} F -->|yes| G["RuntimeError, refuse to start"] F -->|no| H["validate_on_startup()
print [SECURITY] warnings to stderr"] H --> I["Instantiate module-singleton engines
_evidence_engine · _compliance_engine · _risk_engine
_quality_engine · _trust_engine · _identity_engine
_passport_engine · _reputation_engine · _signal_engine
_control_center_engine"] I --> J["_registry = PersistentAgentRegistry(MemoryPersistenceStore())"] J --> K["include_router(cc · artifact · auth · org · billing · observability · security)"] K --> L["App ready, serving"]
Process startup / runtime lifecycle. Source: api/routes.py lines 43–75, 84–100, 671–1224.

Two security gates run at import time before any request is served:

  • Production start gate (V-F3). If the session secret is ephemeral and AUTH_COOKIE_SECURE is true (production posture), import raises RuntimeError and the process refuses to start. This makes an unsafe production config a hard failure, not a warning.
  • Secret advisories (V-F2). validate_on_startup() prints [SECURITY] warnings to stderr for missing/soft secrets without blocking development.

After the gates, every capability engine is constructed once as a module-level singleton. The intelligence, passport, reputation, signal and control-center engines are stateless: they hold no per-request state, so a single shared instance is safe. The one stateful object is the registry.

Runtime state & lifetimes

StateWhereLifetime
Capability enginesmodule singletons (_trust_engine, …)process; stateless, no accumulation
Agent registry_registry = PersistentAgentRegistry(MemoryPersistenceStore())process; survives requests, lost on restart
Per-request trust profileAgentTrustScoreEngine() created inside the handlerone request → exactly one observation
Provider configProviderConfig.from_env() per audit callread fresh each request; holds no secret values
Validation recordsSQLite via repository / init_db()persistent (legacy validation endpoints)
Why a fresh AgentTrustScoreEngine per request? The score engine accumulates observations in memory. Creating it per request guarantees an evidence-derived passport reflects exactly one observation, with no cross-request bleed. The long-lived registry is the deliberate exception where cross-request accumulation is wanted.

Request lifecycle

Every request traverses the same outer shell before reaching a handler, and the same serialization on the way out. Middleware is applied in reverse registration order, so the security-headers middleware wraps the response last:

sequenceDiagram
  autonumber
  participant C as Client
  participant SH as SecurityHeaders MW
  participant CORS as CORS MW
  participant R as Route handler
  participant P as Pydantic model
  participant E as Engine(s)
  C->>SH: HTTP request
  SH->>CORS: forward
  CORS->>R: dispatch (method + path match)
  R->>P: validate request body
  alt invalid body / unknown enum
    P-->>C: 422 Unprocessable Entity
  else valid
    P->>E: typed request → engine call(s)
    E-->>R: domain object(s)
    R->>R: to_export_dict() → response model
    R-->>CORS: response
    CORS-->>SH: add CORS headers
    SH-->>C: + X-Content-Type-Options / X-Frame-Options / X-XSS-Protection / Referrer-Policy
  end
Request lifecycle, outer middleware shell, validation, engine composition, response. Source: api/routes.py lines 54–73, 315+.

The handler body is deliberately thin: it validates, composes one or more engines, and serializes the domain object via that capability's to_export_dict(). No business logic lives in the route. The audit endpoint is the one that reaches all the way down to a provider; the assessment endpoints operate purely on a supplied evidence envelope.

CORS & headers. Origins come from the CORS_ORIGINS env var (JSON array), defaulting to ["*"] for extension + dev-dashboard use; credentials are disabled; methods are limited to GET/POST/DELETE/OPTIONS. Four hardening headers are set with setdefault so a handler can override them but never lose them. See security boundaries.