Status: Accepted (shipped in v2.0) Date: 2026-05-28 Author: Kunjar Bhaduri Repo: finserv-agent-audit v2.0
Reference pattern, not legal advice. Regulatory characterizations are summaries; readers must consult qualified counsel for jurisdictional applicability. See repo-root
DISCLAIMER.md.
Through v1.3 the framework's governance surface — audit chain verification, sovereign-veto triggers, model-inventory queries, vendor-score-gate drift events — was reachable only from inside the same Python process the agent was running in. The v2.0 ship moves the framework into a posture where adopters increasingly need to expose the governance surface over HTTP:
- Procurement-team scanners. FSI procurement teams running automated vendor-management scans key off OpenAPI 3.1 specifications. The procurement scan does not read Python type annotations; it asks "does this vendor publish an OpenAPI spec naming the endpoints we care about" before moving the engagement forward.
- Dashboard / SOC operators. A 24x7 security-operations-center team monitoring the agent posture needs to render veto status, chain-verification progress, and recent vendor drift in a browser. The browser cannot import the Python module; it asks an HTTP endpoint.
- Long-running chain verification. A multi-million-entry audit chain takes minutes to verify end-to-end. A blocking HTTP call that returns only the final verdict is operationally hostile; the dashboard needs an incremental progress signal so the operator can render a progress bar without polling.
- Cross-process veto triggers. An external risk monitor (a separate microservice, a SIEM hook, a peer agent on a different host) needs to trigger a sovereign veto on the in-process agent without a deploy-side IPC mechanism.
The procurement-team OpenAPI preference is decisive. A FastAPI-based control plane auto-generates an OpenAPI 3.1 specification at /openapi.json + a Swagger UI at /docs from the route definitions; the procurement scan keys off the auto-generated spec on day one. Hand-rolling an OpenAPI spec from a Flask / aiohttp / Tornado server is a maintenance burden the framework does not need to take on.
Ship create_app(audit_chain, model_inventory, sovereign_veto, vendor_score_gate) in finserv_agent_audit.integrations.governance_api as a FastAPI factory that returns a fully-wired FastAPI app. The factory accepts every governance Protocol seam the framework ships; passing None for an optional seam causes the matching endpoints to return 503 with a clear "not configured" payload.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /healthz |
none | Liveness probe |
| GET | /info |
none | Package + module versions |
| GET | /audit-chain/info |
none | Length, head hash, event-type histogram |
| GET | /audit-chain/verify |
none | {intact, last_tampered} JSON |
| GET | /audit-chain/verify-stream |
none | SSE stream of per-chunk verification progress |
| POST | /audit-chain/append |
required | Append an event |
| GET | /model-inventory |
none | List inventoried models |
| GET | /sovereign-veto/status |
none | Active vetos + history count |
| POST | /sovereign-veto/trigger |
required | Trigger a veto (emits chain entry) |
| POST | /sovereign-veto/clear |
required | Clear a veto with documented reason |
| GET | /vendor-score-gate/recent-drift |
none | Recent drift events |
| GET | /openapi.json |
none | Auto-generated OpenAPI 3.1 spec |
| GET | /docs |
none | Swagger UI |
- OpenAPI 3.1 by construction. FastAPI auto-generates the spec from the route definitions. The factory pins
openapi_version="3.1.0"so the procurement scan keys off the version-stable surface. - SSE streaming for chain verification. The
/audit-chain/verify-streamendpoint emits oneprogressevent persse_chunk_sizeaudited entries (default 1000) plus a terminalresultevent carrying the final verdict. The SSE wire format (event:+data:+ blank line) traverses corporate proxies cleanly over standard HTTP/1.1; WebSockets are rejected for this surface because the proxy traversal story is worse. - Auth is operator-supplied. The factory accepts an
auth_checker: Callable[[Request], None]that raisesHTTPExceptionon auth failure. The shipped default rejects every mutating call — there is no permissive default because the absence of an auth layer is a deploy-time error, not a runtime convenience. The operator MUST bind the checker to their identity infrastructure (OIDC bearer-token, mTLS subject lookup, internal service-account check). - Mutating calls emit audit-chain entries. Every
POST /sovereign-veto/triggerappends aVETO_APPLIEDentry to the chain; everyPOST /sovereign-veto/clearappends aHUMAN_OVERRIDEentry. The API-layer veto is hash-chained alongside in-process vetos so a regulator-side replay shows the full veto history regardless of trigger surface. - Import-guarded optional dependency. The module import-guards FastAPI behind a
HAS_FASTAPIboolean and the[api]extra. Without FastAPI installed,create_appraisesRuntimeErrorwith the install hint — the integration surface is opt-in, not silently no-op. - Stable API contract across deploys. Endpoints for unwired seams (
/model-inventorywhen no inventory is wired;/sovereign-veto/statuswhen no veto is wired) return 503 with a clear{detail: "<seam> not wired"}payload rather than 404. The procurement scan sees the endpoint enumerated in the OpenAPI spec regardless of which seams the deployer chose to wire.
fastapi>=0.136, uvicorn[standard]>=0.30, httpx>=0.27 ship under the [api] extra. SSE streaming uses Starlette's built-in StreamingResponse — no extra dependency needed. The base wheel keeps the zero-runtime-dependency contract (ADR D2.2).
- Hand-roll an OpenAPI 3.1 spec on top of Flask. Rejected: maintenance burden the framework does not need. FastAPI's auto-generation is the entire reason it dominates the FSI control-plane surface in 2026.
- Use aiohttp / Starlette directly. Rejected: Starlette IS what FastAPI is built on; choosing Starlette directly is choosing FastAPI without the OpenAPI auto-generation. The procurement scan wants the spec; choosing the layer below FastAPI gives up the procurement-team win for no gain.
- WebSockets for chain-verification progress. Rejected: SSE traverses corporate proxies cleanly over standard HTTP/1.1; WebSockets require the proxy to support HTTP UPGRADE which many FSI environments block by policy. SSE is the right wire format for a one-way progress channel.
- Ship a permissive default auth checker. Rejected: the absence of auth is a deploy-time error. A permissive default would let an operator wire the governance API to a production audit chain with no auth boundary; the cost of that mistake is too high. The reject-all default is loud.
- Inline the API into a separate
finserv-agent-audit-apipackage. Rejected: the factory composes onto the existing in-process governance seams; splitting the package would force every adopter to coordinate two release lines for one ship. The import-guard pattern keeps the base wheel dependency-free. - Defer the API to v2.1. Rejected: the procurement-team OpenAPI ask is the current 2026 buying signal. Without an OpenAPI spec, the framework does not clear the FSI vendor-management intake; deferring the API costs adoption today.
Positive. A v2.0 deployment exposes the framework's governance surface as a first-party HTTP API with an OpenAPI 3.1 contract that FSI procurement teams already know how to scan. The SSE endpoint lets the SOC dashboard render incremental progress on million-entry chains without polling. The auth-checker seam composes onto the deployer's identity infrastructure without locking the framework to a single auth library. The API-layer veto triggers are hash-chained alongside in-process triggers, preserving the chain-of-custody contract across the trust boundary.
Negative. The factory ships with a reject-all default auth checker. An operator who fails to read the docstring will see 401 on every mutating call and conclude the API is broken; the fix is documented (supply an auth_checker) but the friction is real. The trade-off is intentional — silent permissive defaults on a governance API are a worse failure mode than a loud rejection.
The SSE endpoint walks the chain in-memory. For multi-million-entry chains backed by a remote LedgerStore (Postgres+WAL, S3 + Object Lock), the in-memory walk is wrong — the chain should be paged from the backend. The v2.0 ship documents this; a v2.1 ADR may add a paged-walk seam to the chain-verification path.
Architectural. The integration introduces one new module (integrations/governance_api.py) and one new optional-dependency extra ([api]). It composes onto every governance Protocol seam the framework ships without modifying the chain, the inventory, the veto, or the gate. The audit-chain emission contract for mutating endpoints (VETO_APPLIED on trigger, HUMAN_OVERRIDE on clear) is the load-bearing piece.
- EU AI Act, Regulation (EU) 2024/1689 — Article 14 (human oversight of high-risk AI systems). The API surface is the operator-side path for the human-on-the-loop trigger / clear actions that Article 14 demands; the chain emission on every mutating call is the evidentiary record.
- EU AI Act — Article 12 (logging capabilities for high-risk AI systems). Every API-layer veto / clear / append emits a chained audit event; the chain is the Article-12-grade log.
- SR 11-7 (model risk management) — the API gives the second-line model-risk team a non-engineering surface for inventory queries and veto triggers; the chain emission preserves the third-line evidence.
- FFIEC IT Examination Handbook — third-party API surfaces require documented authentication, authorization, and audit-logging controls. The factory pins all three: pluggable auth checker, mutating-endpoint chain emission, and OpenAPI-documented endpoint enumeration.
- OWASP API Security Top 10 (2023) — the reject-all default auth checker, the OpenAPI 3.1 spec generation, and the input-validation-by-Pydantic posture address the API1 (Broken Object-Level Authorization) and API2 (Broken Authentication) categories at design time rather than at incident time.
- FastAPI 0.136+ + OpenAPI 3.1 + SSE production patterns — Starlette's
StreamingResponsewithmedia_type="text/event-stream"is the canonical SSE wire-format implementation; FastAPI'sopenapi_version="3.1.0"pin is the canonical procurement-grade contract emitter. - finserv-agent-audit ADR-0002 (Sovereign Veto) — the in-process veto contract this API exposes over HTTP.
- finserv-agent-audit ADR-0003 (Hash-Chained Audit Ledger) — the substrate the API-layer chain entries land on.
- finserv-agent-audit ADR-0007 (SR 11-7 Model Inventory) — the in-process inventory contract this API exposes over HTTP.
- finserv-agent-audit ADR-0016 (Vendor Score Gate) — the in-process drift contract this API exposes as a read-only convenience view.
- finserv-agent-audit ADR-0031 (AIBOM Generator) — the v2.0 procurement-grade artifact emitter; a future ADR may add an AIBOM-emit endpoint on this API surface.
The failure mode this ADR prevents: an FSI buyer scans the framework, sees a Python package with no HTTP surface, concludes there is no procurement-grade contract, and exits the engagement. With the API and the auto-generated OpenAPI 3.1 spec, the buyer's procurement scan passes on day one; the engagement proceeds.
The failure mode this ADR creates if mishandled: an operator wires the factory into production without supplying an auth_checker, hits 401 on every mutating call, swaps the default for a permissive lambda to "make it work", and now the audit chain accepts unauthenticated appends. Mitigation: the module docstring is explicit that the reject-all default is intentional; the RuntimeError on missing FastAPI carries the install hint; the auth_checker parameter is named, typed, and documented; the test suite exercises the 401 path so the operator sees the rejection in CI before deploy.
Reversible. The endpoint surface is the contract; the FastAPI implementation is the substrate. Replacing FastAPI with a successor framework that auto-generates OpenAPI 3.1 (Starlette + a hand-rolled spec, Litestar, Robyn) is a non-breaking change as long as create_app continues to return an ASGI-compatible app with the documented endpoints. The OpenAPI 3.1 contract + the audit-chain emission contract for mutating endpoints are the load-bearing pieces.
- ADR-0002 (Sovereign Veto) — the in-process veto contract this API exposes.
- ADR-0003 (Hash-Chained Audit Ledger) — the chain the API-layer mutating endpoints emit to.
- ADR-0007 (SR 11-7 Model Inventory) — the in-process inventory contract this API exposes.
- ADR-0014 (Persistence-Witness-Timestamp Pattern) — when the chain head is anchored, the API-layer chain entries inherit the witness-side evidence.
- ADR-0016 (Vendor Score Gate) — the in-process drift contract this API exposes.
- ADR-0017 (Audit-Chain Retention, Privilege & Discovery) — the retention schedule the API-layer chain entries land inside.
- ADR-0031 (AIBOM Generator) — the v2.0 procurement-grade artifact emitter exposed alongside this API.
Patterns are software, not legal advice. Regulatory citations are reference mappings; consult counsel for applicability to your control environment.