How the platform works
FastAPI app factory, middleware order, lifespan startup, and API router registration.
Intended audience: Stakeholders, Business analysts, Solution architects, Developers, Testers
Redis, Postgres, and optional RabbitMQ behavior is defined at startup and in middleware; see also Configuration and Monitoring guides.
Learning outcomes by role
Stakeholders
- Explain how shared infrastructure (databases, cache, optional broker) underpins uptime and cost trade-offs for a multi-tenant deployment.
- Relate middleware ordering to customer-visible failures (auth, rate limits, sessions) when prioritizing incidents.
Business analysts
- Document prerequisites and dependencies for operational runbooks (which subsystems must be healthy before API calls succeed).
- Align acceptance language with observable HTTP outcomes (401, 403, rate limiting) tied to documented middleware behavior.
Solution architects
- Map inbound middleware order, lifespan initialization, and router registration to integration boundaries and NFRs (security headers, CORS, scaling).
- Justify where Redis, Postgres, and RabbitMQ sit in deployment diagrams relative to the FastAPI process.
Developers
- Trace configure_* registration in cadence.main against the inbound execution order when debugging request.state and errors.
- Locate register_api_routers and lifespan steps when extending startup or adding routers.
Testers
- Predict failure modes from middleware order (session before rate limit, public paths, missing Redis) and design negative tests accordingly.
- Correlate 401 versus 403 scenarios with authentication versus tenant and permission layers after this page.
Cadence is a FastAPI service: startup wires databases and optional brokers; each HTTP request passes through middleware (errors, auth, tenant session, rate limits) before a route handler runs.
Summary for stakeholders
Section titled “Summary for stakeholders”- Single deploy, many tenants — One API process serves all organizations; isolation is enforced after authentication in tenant and permission layers (see Multi-tenancy).
- Operational leverage — Health and behavior depend on PostgreSQL, Redis (sessions and rate limiting), and optionally RabbitMQ for orchestrator-related messaging; gaps there surface as structured errors or degraded features, not silent data mixing.
- Risk posture — Middleware order is fixed in code: changes to infrastructure or config alter when failures appear (e.g. before or after rate limiting), which matters for incident triage.
Business analysis
Section titled “Business analysis”- Single source for “what runs first” — Product and operations can reference this page instead of ad-hoc diagrams when writing prerequisites (“Redis required for interactive JWT sessions and rate limits”).
- Observable outcomes — Link user stories to HTTP status patterns: missing or invalid credentials (401), known user but disallowed org or role (403), and rate limiting (too many requests) as separate acceptance themes.
- Scope boundaries — OpenAPI describes routes; effective rate limits and session behavior follow middleware and tier rules documented here and in feature pages.
Architecture and integration
Section titled “Architecture and integration”You need one place to see middleware order, startup steps, and router mounting without opening half a dozen modules. That mental model matters when debugging 401/403, missing sessions, or rate-limit surprises.
Request flow (conceptual)
Section titled “Request flow (conceptual)”flowchart LR Client[Client] --> MW[Middleware stack] MW --> R[Route handler] R --> MW MW --> Client
Inbound requests hit the outermost middleware first (see below), then deeper layers, then your route.
Inbound stack (memory diagram)
Section titled “Inbound stack (memory diagram)”
Inbound order (top first). Registration order in cadence.main is the reverse; see Middleware chain.
Middleware chain
Section titled “Middleware chain”main.py registers middleware in this registration order (first line runs first):
configure_cors_middlewareconfigure_security_headers_middlewareconfigure_rate_limiting_middlewareconfigure_tenant_context_middlewareconfigure_authentication_middlewareconfigure_error_handlers_middleware
In Starlette/FastAPI, the last add_middleware runs first on each incoming HTTP request. So the outermost layer (first to see the request) is ErrorHandlerMiddleware, then AuthenticationMiddleware, then TenantContextMiddleware, then RateLimitMiddleware, then security headers, then CORS.
The inline comment in main.py summarizes the middle of the stack: inbound flow among auth, tenant, and rate limiting is authentication → tenant context → rate limiting — JWT/API key validation and request.state preparation happen before tenant session hydration and before Redis-backed rate limits.
| Layer (incoming order, after network) | Purpose |
|---|---|
| Error handling | Error handler middleware assigns X-Request-ID, catches uncaught exceptions, returns JSON error payloads; avoids double-send if response already started. |
| Authentication | Authentication middleware applies a public path allowlist; Authorization: Bearer for JWTs; X-API-KEY for API keys; sets request.state.api_key_row when a key is used. |
| Tenant context | Tenant context middleware loads TokenSession from Redis (JWT jti) or synthesizes from API key row. |
| Rate limiting | Rate limiting middleware uses a Redis sliding window; per org/user/IP; tier from cache; no-op if Redis client is missing. |
| Security headers | Security headers middleware sets nosniff, DENY frame, Referrer-Policy, Permissions-Policy; HSTS when environment is production. |
| CORS | CORSMiddleware — origins from settings; logs if unset. |
Implementation notes
Section titled “Implementation notes”Application entry
Section titled “Application entry”cadence.main builds a single FastAPI app with lifespan, OpenAPI customization, middleware, and router registration. The call order for middleware registration is explicit in source:
app.openapi = build_openapi_schema_generator(app)# ...cors_origins = app_settings.cors_origins or []configure_cors_middleware(app, cors_origins)configure_security_headers_middleware(app, app_settings)# Middleware runs outer-first (last registered). Order here is inner→outer:# rate → tenant → auth → error, so inbound is auth → tenant → rate (session# exists before rate limiting; API keys validated before tenant builds session).configure_rate_limiting_middleware(app)configure_tenant_context_middleware(app, app_settings)configure_authentication_middleware(app, app_settings)configure_error_handlers_middleware(app, app_settings)
register_api_routers(app)lifespan=create_lifespan_handler(app_settings)— Async startup/shutdown (Lifespan).app.openapi = build_openapi_schema_generator(app)— Custom OpenAPI generation.register_api_routers(app)— Mounts HTTP routers.
uvicorn cadence.main:app is the normal process entry; the if __name__ == "__main__" block is for local development.
Lifespan: startup and shutdown
Section titled “Lifespan: startup and shutdown”create_lifespan_handler runs before any request is served:
- Config validation —
settings.validate_production_config(); in production, insecure config raises and aborts startup. - PostgreSQL + Redis —
initialize_database_clientsconnects pools; clients attached toapp.state. - Repositories and stores —
_create_repositoriesbuilds Postgres repos,SessionStoreRepository(Redis),MessageRepository,PluginStoreRepository(optional S3), bootstrapsglobal_settingskeys (token TTLs, OAuth defaults) if missing. - RBAC —
BuiltInRBACProviderwired with role repositories and Redis async client. - Telemetry — OpenTelemetry from DB settings; FastAPI/SQLAlchemy/Redis instrumentation; LLM instrumentors after hot-tier load.
- Services —
TenantService,AuthService,OAuthService,ConversationService,PluginService,CentralPointService, etc. onapp.state. - Orchestrator pool —
OrchestratorPool+ factory;OrchestratorServiceattached. - RabbitMQ (optional) — If
RabbitMQClient.connect()succeeds: publisher + consumer for orchestrator events; on failure, logs a warning and leavesapp.state.rabbitmq_clientasNone. - Plugins —
ensure_all_catalog_plugins_localpulls catalog plugins to local cache when S3 is configured. - Hot tier —
load_hot_tier_instancesloads activehot-tier orchestrators into the pool.
Shutdown (after yield): stop event consumer, disconnect RabbitMQ, shutdown_orchestrator_pool, disconnect Postgres/Redis, shutdown_telemetry.
API router registration
Section titled “API router registration”register_api_routers includes routers in this order (path prefixes live on each router):
def register_api_routers(application: FastAPI) -> None: """Register all API route controllers with the application.""" application.include_router(health.router) application.include_router(oauth2.router) application.include_router(auth.router) application.include_router(api_key.router) application.include_router(chat.router) application.include_router(orchestrator.router) application.include_router(engine.router) application.include_router(plugins.router) application.include_router(tenant.router) application.include_router(admin.router) application.include_router(telemetry.router) application.include_router(stats.router)Summarized: health → oauth2 → auth → api_key → chat → orchestrator → engine → plugins → tenant → admin → telemetry → stats. Order only matters when two routes could match the same path pattern; in practice each module owns distinct prefixes.
Verification and quality
Section titled “Verification and quality”- Rate limiting needs Redis in normal operation; if the client is missing, the middleware skips limiting (see
RateLimitMiddleware.dispatch). - RabbitMQ is optional: without the broker, orchestrator event messaging is off; the rest of the API can still run.