"""RMI Backend - middleware registration. Per v3 unfuck rule #7: add_middleware MUST be called at module level, NOT inside lifespan. FastAPI rejects middleware added after startup. This module owns every middleware registration. Adding a new middleware: 1. Add a `try_register(app, "name")` block below 2. Each block is isolated - one failure doesn't break the rest """ from __future__ import annotations import logging log = logging.getLogger(__name__) def register_middleware(app) -> None: """Register all middleware. Each registration is isolated.""" _try_register_cors(app) _try_register_security_headers(app) _try_register_prometheus(app) _try_register_cost_tracking(app) _try_register_tracing(app) def _try_register_cors(app) -> None: """T19 - CORS hardening with strict allowlist.""" try: from fastapi.middleware.cors import CORSMiddleware ALLOWED_ORIGINS = [ "https://rugmunch.io", "https://www.rugmunch.io", "https://mcp.rugmunch.io", "https://x402.rugmunch.io", "https://rugcharts.io", "https://rugmaps.io", "http://localhost:5173", "http://localhost:3000", ] app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["Authorization", "Content-Type", "X-Request-ID", "X-Payment"], ) log.info("middleware_registered name=cors") except Exception as exc: log.warning("middleware_skipped name=cors err=%s", exc) def _try_register_security_headers(app) -> None: """T20 - Security headers on every response.""" try: from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request class SecurityHeadersMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): response = await call_next(request) response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["Strict-Transport-Security"] = ( "max-age=31536000; includeSubDomains; preload" ) response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" response.headers["Permissions-Policy"] = ( "geolocation=(), microphone=(), camera=()" ) if request.url.path not in ["/docs", "/redoc", "/openapi.json"]: response.headers["Content-Security-Policy"] = "default-src 'self'" return response app.add_middleware(SecurityHeadersMiddleware) log.info("middleware_registered name=security_headers") except Exception as exc: log.warning("middleware_skipped name=security_headers err=%s", exc) def _try_register_prometheus(app) -> None: """Prometheus metrics middleware. Records every request.""" try: from app.core.metrics import PrometheusMiddleware app.add_middleware(PrometheusMiddleware) log.info("middleware_registered name=prometheus") except Exception as exc: log.warning("middleware_skipped name=prometheus err=%s", exc) def _try_register_cost_tracking(app) -> None: """M7 - per-tenant/per-route cost tracking.""" try: from app.middleware.cost_tracking import CostBuffer, CostTrackingMiddleware app.add_middleware(CostTrackingMiddleware, buffer=CostBuffer()) log.info("middleware_registered name=cost_tracking") except Exception as exc: log.warning("middleware_skipped name=cost_tracking err=%s", exc) def _try_register_tracing(app) -> None: """Request-id + timing middleware (always on).""" try: from app.core.tracing import setup_tracing setup_tracing(app) log.info("middleware_registered name=tracing") except Exception as exc: log.warning("middleware_skipped name=tracing err=%s", exc)