The authoritative architecture document for the cross-agent platform service is in the agent-template repo:
agent-template/docs/architecture.md § "Cross-Agent Platform Service"
That section covers the design rationale, the four options that were considered, why Option 4 (remote-store adapter with a sibling platform service) was chosen, and the tradeoffs flagged for the initial extraction. The decision was recorded in agent-template#112.
This document captures the implementation-level details specific to this repo. Read the agent-template document first.
src/fipsagents_platform/
__init__.py
__main__.py # python -m fipsagents_platform entrypoint
app.py # FastAPI factory, lifespan, route registration
config.py # pydantic-settings, env-driven
auth.py # Bearer-token validation (none / keycloak modes)
store_factory.py # Builds fipsagents.server stores from config
routes/
feedback.py # /v1/feedback proof point (live)
sessions.py # /v1/sessions proof point (live)
traces.py # /v1/traces proof point (live)
The platform service does not reimplement persistence. It depends on fipsagents[feedback,server]>=0.12.0 and reuses the existing FeedbackStore / SessionStore / TraceStore ABCs:
fipsagents.server.feedback--FeedbackStore,SqliteFeedbackStore,PostgresFeedbackStore,create_feedback_store()fipsagents.server.sessions--SessionStore,SqliteSessionStore,PostgresSessionStorefipsagents.server.tracing--TraceStore,SqliteTraceStore,PostgresTraceStore
This is the deliberate design from agent-template#112: schema and migration logic stay in one place. The platform repo is a thin REST veneer.
Two modes, controlled by PLATFORM_AUTH_MODE:
none-- the platform validates nothing.user_iddefaults to"anonymous"on writes. Use this when a trusted gateway in front already enforces authn (the typical fips-agents topology) and the platform is reachable only via the cluster network.keycloak-- inboundAuthorization: Bearer <jwt>is validated against a Keycloak issuer's JWKS. The same realm as the gateway, so tokens issued bygateway-template's RFC 8693 exchange (gateway-template#27) validate here. The validatedsubclaim is recorded asuser_idon the feedback record.
JWKS is cached for PLATFORM_KEYCLOAK_JWKS_CACHE_SECONDS (default 300). On a kid miss, the cache is busted once and the JWKS re-fetched -- this handles key rotation without bouncing the pod.
All endpoints mirror fipsagents.server.app's per-agent endpoints exactly, with three extensions for write-side parity (PUT /v1/sessions/{id}, HEAD /v1/sessions/{id}, POST /v1/traces). The point is that an HttpFeedbackStore / HttpSessionStore / HttpTraceStore on the agent side (agent-template#114) can route to either the per-agent endpoint or the platform endpoint with no contract difference, and fully replace the in-process SQLite/Postgres backend.
| Endpoint | Status | Notes |
|---|---|---|
POST /v1/feedback |
live | Returns {"feedback_id": "fb_..."} |
GET /v1/feedback |
live | Filters: trace_id, session_id, user_id, since, until, limit, offset |
GET /v1/feedback/{id} |
live | New endpoint -- not on the per-agent server. Used by the agent-side HttpFeedbackStore.get() |
PATCH /v1/feedback/{id} |
live | Partial update; null means "leave unchanged" |
GET /v1/feedback/stats |
live | Aggregations grouped by agent_type and time window |
POST /v1/sessions |
live | Returns {"session_id": "sess_..."}; accepts optional session_id |
GET /v1/sessions/{id} |
live | Returns {"session_id", "messages"}; 404 if missing |
PUT /v1/sessions/{id} |
live | Save messages (upsert); body: {"messages": [...]}. Extension over per-agent shape — required for HttpSessionStore |
HEAD /v1/sessions/{id} |
live | 200 if exists, 404 if not. No body. Extension for HttpSessionStore.exists() |
DELETE /v1/sessions/{id} |
live | Returns {"deleted": true}; 404 if missing |
POST /v1/traces |
live | Save a trace (upsert); body mirrors the Trace dataclass. Extension over per-agent shape — required for HttpTraceStore |
GET /v1/traces |
live | List TraceSummary objects; limit (1-1000), offset |
GET /v1/traces/{id} |
live | Full Trace with all spans; 404 if missing |
ui-template ──► gateway-template ──► agent ──► fipsagents-platform
│ │
└─────────► fipsagents-platform (direct, when
gateway routing mode is enabled)
Two routing options for the /v1/feedback, /v1/sessions, /v1/traces paths, controlled by gateway-template#30's routing-mode config:
- Per-agent fan-out (default, backward-compatible) -- gateway forwards to the agent backend, which uses its local
SqliteFeedbackStoreorPostgresFeedbackStore. Each agent owns its data. - Direct routing -- gateway forwards to
fipsagents-platform, which owns one Postgres pool, one schema, and one auth boundary across all agents.
When the agent is configured with HttpFeedbackStore (agent-template#114), the per-agent endpoint round-trips to the platform anyway, so option 1 with Http*Store is functionally equivalent to option 2. Option 2 just removes the extra network hop.