From 2f132e9d1cc67bf9982fecca038d13470100c520 Mon Sep 17 00:00:00 2001 From: Bobcatsfan33 Date: Thu, 11 Jun 2026 20:14:41 -0400 Subject: [PATCH] =?UTF-8?q?refactor(T-1=20final):=20api.py=20is=20the=20ap?= =?UTF-8?q?p=20factory=20only=20=E2=80=94=206934=20->=20205=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes ~106 dead imports (ruff F401), two unused helpers, orphaned section comments; rewrites the docstring. All 305 product routes live in api_routers/* (19 domain routers); only /, /healthz, /readyz, /metrics remain. A handful of location-coupled tests retargeted to the router/_shared modules. Decomposition complete (97% reduction); route-surface guard empty diff; full suite green. --- api.py | 290 +++-------------------------- scripts/ci/api_line_budget.txt | 2 +- tests/test_agent_discovery.py | 4 +- tests/test_behavioral_dna.py | 4 +- tests/test_compliance_engine.py | 4 +- tests/test_enforcement_plane.py | 4 +- tests/test_mcp_gateway.py | 4 +- tests/test_production_readiness.py | 20 +- 8 files changed, 42 insertions(+), 290 deletions(-) diff --git a/api.py b/api.py index 8004df6..4e4b95a 100644 --- a/api.py +++ b/api.py @@ -1,129 +1,39 @@ """ -TokenDNA Identity Backbone -- FastAPI v2.5.0 - -Endpoints -───────── -GET / health check (unauthenticated) -GET /dashboard admin dashboard SPA (unauthenticated) - -GET /api/stats KPI counters for current tenant -GET /api/events recent session events for current tenant -GET /api/events/hourly hourly volume for past 24h (chart data) -GET /api/threats threat signal breakdown for past 24h -GET /api/health detailed system health - -GET /secure main token integrity check -GET /profile/{uid} inspect user adaptive profile -DELETE /profile/{uid} reset user profile -POST /revoke manually revoke token by jti - -POST /admin/tenants create tenant (returns raw API key, show once) -GET /admin/tenants list all tenants -GET /admin/tenants/{id}/keys list API keys for a tenant -POST /admin/tenants/{id}/keys rotate / add API key -DELETE /admin/tenants/{id}/keys/{kid} revoke a key - -POST /onboarding/aws/external-id generate ExternalId for CloudFormation -POST /onboarding/aws/test test IAM role + quick posture scan - -POST /api/uis/normalize normalize a protocol event into UIS v1.0 -POST /api/agent/attest generate 4D agent attestation record -POST /api/mcp/verify verify MCP server integrity/capabilities +TokenDNA Identity Backbone -- FastAPI app factory (v2.5.0). + +This module is the APP FACTORY only (T-1 decomposition complete). It builds the +FastAPI app, wires middleware + lifespan + startup checks, and mounts every +product domain router via ``api_routers.mount_all``. The 305 product routes live +in ``api_routers/.py`` — one router per domain, each declaring its own +auth + tier-gate dependencies. The CI ratchet +(``scripts/ci/api_monolith_ratchet.py``) keeps this file from regrowing; new +endpoints are born in ``api_routers/``, never here. + +Only infrastructure endpoints remain in this file: + GET / service info / health + GET /healthz Kubernetes liveness + GET /readyz Kubernetes readiness (503 when a dependency is down) + GET /metrics Prometheus exposition """ from __future__ import annotations import asyncio -import datetime -import json import logging import os -import uuid from contextlib import asynccontextmanager -from pathlib import Path -from typing import Optional -from fastapi import Body, Depends, FastAPI, HTTPException, Request, Response +from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse, HTMLResponse, JSONResponse - -from auth import verify_token -from config import DEV_MODE, OIDC_ISSUER, RATE_LIMIT_PER_MINUTE, RATE_LIMIT_OPEN_PER_MINUTE -from modules.identity import async_pipeline, geo_intel, ml_model, scoring, session_graph, threat_intel -from modules.identity.alerts import handle_block, handle_revoke, handle_step_up -from modules.identity.cache_redis import ( - TenantRedis, - get_baseline, - get_event_counters, - get_redis, - increment_event_counter, - increment_rate, - is_token_revoked, - push_baseline_history, - revoke_token, - set_baseline, -) -from modules.identity.scoring import RiskTier -from modules.identity.token_dna import generate_dna, migrate_dna -from modules.identity.uis import normalize_from_protocol, validate_uis_event -from modules.identity.uis_protocol import get_uis_spec, normalize_with_adapter -from modules.identity.attestation import create_attestation_record -from modules.identity.mcp_attestation import verify_mcp_server -from modules.identity.attestation_certificates import issue_certificate, revoke_certificate, verify_certificate -from modules.identity.certificate_status import build_crl, certificate_status_payload -from modules.identity.edge_enforcement import evaluate_runtime_enforcement -from modules.identity.trust_authority import list_key_configs -from modules.identity.attestation_drift import build_drift_event, DriftAssessment, assess_runtime_drift -from modules.identity import schema_registry -from modules.identity import trust_graph -from modules.identity import blast_radius -from modules.identity import policy_guard -from modules.identity import permission_drift -from modules.identity import intent_correlation -from modules.identity import policy_bundles -from modules.identity import agent_lifecycle -from modules.identity import mcp_inspector -from modules.identity import mcp_gateway -from modules.identity import agent_discovery -from modules.identity import enforcement_plane -from modules.identity import behavioral_dna -from modules.identity import compliance_engine -from modules.identity import cert_dashboard -from modules.identity import policy_advisor -from modules.identity import network_intel -from modules.identity import compliance -from modules.identity import attestation_store -from modules.identity import uis_store -from modules.identity import decision_audit -from modules.identity import trust_federation -from modules.identity import certificate_transparency as ct_log +from fastapi.responses import JSONResponse + +from config import DEV_MODE, OIDC_ISSUER +from modules.identity import threat_intel from modules.identity import clickhouse_client -from modules.integrations.siem_taxii import build_taxii_bundle -from modules.integrations.idp_events import adapt_idp_event -from modules.integrations.sdk_wrappers import ( - build_adapter_normalize_request, - build_attestation_request, - sdk_create_attestation, - sdk_normalize_event, -) # alias used in /api/oss/sdk/attest route -from modules.product import metering as feature_metering -from modules.product.commercial_tiers import ( - list_features as list_commercial_features, - require_feature, - tier_for_plan, -) -from modules.product.feature_gates import PlanTier, evaluate_feature_access, list_feature_matrix -from modules.storage import db_backend -from modules.identity.uis_narrative import enrich_event as uis_enrich_event -from modules.tenants import store as tenant_store -from modules.tenants.middleware import get_tenant -from modules.tenants.models import Plan, TenantContext from modules.security.audit_log import AuditEventType, AuditOutcome, log_event from modules.security.headers import RequestValidationMiddleware, SecurityHeadersMiddleware -from modules.security.rbac import Role, require_role -from modules.security.fips import fips, FIPSError -from modules.identity.hvip import HVIPEnforcer, HVIPRole, HVIPAction, HVIPError +from modules.security.fips import fips logging.basicConfig( level=logging.INFO, @@ -132,36 +42,6 @@ logger = logging.getLogger(__name__) from api_routers._shared import APP_VERSION # noqa: E402 -def _plan_tier_from_tenant(tenant: TenantContext) -> PlanTier: - plan_value = str( - getattr(tenant, "plan", Plan.FREE).value if hasattr(tenant.plan, "value") else tenant.plan - ).lower() - if plan_value in {p.value for p in PlanTier}: - return PlanTier(plan_value) - return PlanTier.FREE - - -def _record_decision_audit( - *, - tenant: TenantContext, - request_id: str, - source_endpoint: str, - actor_subject: str, - evaluation_input: dict, - enforcement: dict, - policy_bundle: dict | None = None, -) -> dict: - return decision_audit.record_decision( - tenant_id=tenant.tenant_id, - request_id=request_id, - source_endpoint=source_endpoint, - actor_subject=actor_subject, - evaluation_input=evaluation_input, - enforcement_result=enforcement, - policy_bundle=policy_bundle, - ) - - @asynccontextmanager async def lifespan(_app: FastAPI): await _startup_checks() @@ -278,7 +158,6 @@ async def _startup_checks() -> None: # ── Rate limiting dependency ────────────────────────────────────────────────── -from api_routers._shared import check_rate_limit, check_rate_limit_open # noqa: E402 # ── Health / dashboard (unauthenticated) ───────────────────────────────────── @app.get("/") @@ -324,130 +203,3 @@ async def metrics_endpoint(): from modules.observability.metrics import render_metrics body, content_type = render_metrics() return Response(content=body, media_type=content_type) - - -# ── Enterprise SAML SSO (alpha) ─────────────────────────────────────────────── - - -# ── SCIM 2.0 (alpha) ────────────────────────────────────────────────────────── - - -# ── Dashboard data API (tenant-scoped, real data) ──────────────────────────── - -# ── Consolidation endpoints: UIS + Agent Attestation + MCP Verification ────── - -# ── Main integrity check ────────────────────────────────────────────────────── - -# ── Profile endpoints ───────────────────────────────────────────────────────── - -# ── Manual revocation ───────────────────────────────────────────────────────── - -# ── Tenant management (admin) ───────────────────────────────────────────────── - -# ── AWS onboarding ──────────────────────────────────────────────────────────── - -# ── Session Intelligence (/api/sessions) ────────────────────────────────────── - -# ── Cloud Posture Findings (/api/cloud-findings) ─────────────────────────────── - -# ── Audit Log endpoint (OWNER only) ──────────────────────────────────────────── - -# ── Trust Graph endpoints ────────────────────────────────────────────────────── - -# ── Blast Radius Simulator endpoints ────────────────────────────────────────── - -# ── Intent Correlation endpoints ─────────────────────────────────────────────── - -# ── ZTIX Exchange endpoints ─────────────────────────────────────────────────── - -# ── Permission Drift Tracker (Sprint 5-2) ───────────────────────────────── - -# ── Sprint 5-3: Ghost Agent Offboarding Enforcement ─────────────────────────── - -# ── Sprint 5-4: MCP Intent-Aware Inspection ─────────────────────────────────── - -# ── Sprint 6-1: Agent Attestation Certificate Lifecycle Dashboard ───────────── - -# ========================================================================== -# Policy Advisor — Adaptive Policy Suggestion Engine (Sprint 6-2) -# ========================================================================== - - -# ========================================================================== -# Agent Identity Passport (Sprint 3-1) — restored 2026-04-21 -# ========================================================================== - -from modules.identity import passport as passport_module - - -# ========================================================================== -# Verifier Reputation Network (Sprint 3-2) — restored 2026-04-21 -# ========================================================================== - -from modules.identity import verifier_reputation as reputation_module - - -# ========================================================================== -# ZTIX Continuous Proof-of-Control (Expansion #2) — Sprint 7-B -# ========================================================================== - -from modules.identity import proof_of_control as poc_module - - -# ── Phase 5-1: MCP Security Gateway ────────────────────────────────────────── - - -# ── Phase 5-2: Agent Discovery & Inventory ──────────────────────────────────── - - -# ── Phase 5-3: Enforcement Plane ───────────────────────────────────────────── - - -# ── Phase 5-3: Behavioral DNA ───────────────────────────────────────────────── - - -# ── Phase 5-4: Compliance Engine ───────────────────────────────────────────── - - -# ── Commercial Tier Entitlements ────────────────────────────────────────────── -# Ungated — community tenants need to see what they're locked out of. - -# ── Threat Sharing Network ──────────────────────────────────────────────────── -# Cross-tenant threat-intel sharing built on top of intent_correlation. -# Gated behind ent.intent_correlation since the network is an extension of -# that feature. - -# ── Delegation Receipts ─────────────────────────────────────────────────────── -# Cryptographic paper trail for agent delegation chains. Gated behind -# ent.enforcement_plane — receipts are an authorization-enforcement primitive. - -# ── Threat Sharing Flywheel ─────────────────────────────────────────────────── -# Network-effect loops on top of /api/threat-sharing: hit recording, catalog -# scoring, industry digest, auto-subscribe. - -# ── Workflow Attestation ────────────────────────────────────────────────────── -# Multi-hop signed DAG with replay + drift detection. Gated behind -# ent.enforcement_plane — workflow attestation is the chain-of-custody -# layer above per-receipt delegation. - -# ── Compliance Posture & Incident Reconstruction ────────────────────────────── -# Auditor-facing surfaces. compliance.py owns the framework definitions; -# this is the operator's "prove our posture as of now" deliverable. - -# ── Honeypot Mesh / Active Deception ────────────────────────────────────────── -# Active counterpart to deception_mesh: emit attestation-valid decoys, -# harvest attacker TTPs. Gated behind ent.enforcement_plane. - -# ── Staged Rollout / Allowlist Admin ────────────────────────────────────────── -# Per-tenant feature allowlists for design-partner / beta / staged-rollout -# scenarios. Admin-only. require_feature() consults this transparently; -# tier-based entitlement remains the default. - -# ── Edge enforcement parity (Cloudflare Worker snapshot endpoints) ──────────── -# -# Pulled by edge/index.js scheduled() handler every 60s and cached in KV so -# the request-path edge checks (cert revocation + drift score gating) are -# O(1) and never block on the backend. Authenticates via a shared -# X-Edge-Sync-Token header (set as a Cloudflare Worker secret). - - diff --git a/scripts/ci/api_line_budget.txt b/scripts/ci/api_line_budget.txt index 534b992..485369e 100644 --- a/scripts/ci/api_line_budget.txt +++ b/scripts/ci/api_line_budget.txt @@ -1 +1 @@ -453 +205 diff --git a/tests/test_agent_discovery.py b/tests/test_agent_discovery.py index 9d11786..2a40e65 100644 --- a/tests/test_agent_discovery.py +++ b/tests/test_agent_discovery.py @@ -504,8 +504,8 @@ def test_acknowledge_nonexistent_raises(self): class TestAPIRouteRegistration(unittest.TestCase): def test_api_module_imports_agent_discovery(self): - import api as api_mod - assert hasattr(api_mod, "agent_discovery") + import api_routers.agents as _r + assert hasattr(_r, "agent_discovery") def test_discovery_routes_registered(self): try: diff --git a/tests/test_behavioral_dna.py b/tests/test_behavioral_dna.py index 1784335..9e92ee1 100644 --- a/tests/test_behavioral_dna.py +++ b/tests/test_behavioral_dna.py @@ -331,8 +331,8 @@ def test_empty_agent_snapshot_ok(self): class TestAPIRouteRegistration(unittest.TestCase): def test_api_imports_behavioral_dna(self): - import api as api_mod - assert hasattr(api_mod, "behavioral_dna") + import api_routers.identity_surface as _r + assert hasattr(_r, "behavioral_dna") def test_behavioral_routes_registered(self): try: diff --git a/tests/test_compliance_engine.py b/tests/test_compliance_engine.py index 1e98e08..5109d5c 100644 --- a/tests/test_compliance_engine.py +++ b/tests/test_compliance_engine.py @@ -415,8 +415,8 @@ def test_audit_export_empty_agent_ok(self): class TestAPIRouteRegistration(unittest.TestCase): def test_api_imports_compliance_engine(self): - import api as api_mod - assert hasattr(api_mod, "compliance_engine") + import api_routers.compliance as _r + assert hasattr(_r, "compliance_engine") def test_compliance_routes_registered(self): try: diff --git a/tests/test_enforcement_plane.py b/tests/test_enforcement_plane.py index fd4a048..7a3a9b0 100644 --- a/tests/test_enforcement_plane.py +++ b/tests/test_enforcement_plane.py @@ -396,8 +396,8 @@ def test_decisions_logged_with_kill_switch_flag(self): class TestAPIRouteRegistration(unittest.TestCase): def test_api_imports_enforcement_plane(self): - import api as api_mod - assert hasattr(api_mod, "enforcement_plane") + import api_routers.enforcement as _r + assert hasattr(_r, "enforcement_plane") def test_enforcement_routes_registered(self): try: diff --git a/tests/test_mcp_gateway.py b/tests/test_mcp_gateway.py index 1b3dc9f..cd45f8c 100644 --- a/tests/test_mcp_gateway.py +++ b/tests/test_mcp_gateway.py @@ -567,8 +567,8 @@ class TestAPIRouteRegistration(unittest.TestCase): requiring a live FastAPI app instance.""" def test_api_module_imports_mcp_gateway(self): - import api as api_mod - assert hasattr(api_mod, "mcp_gateway"), "api.py should import mcp_gateway" + import api_routers.mcp as _r + assert hasattr(_r, "mcp_gateway"), "api.py should import mcp_gateway" def test_gateway_routes_exist_in_api(self): """Confirm the expected route paths are registered on the FastAPI app.""" diff --git a/tests/test_production_readiness.py b/tests/test_production_readiness.py index e6564f6..65ed61f 100644 --- a/tests/test_production_readiness.py +++ b/tests/test_production_readiness.py @@ -294,7 +294,7 @@ def test_under_limit_does_not_raise(self): with patch.object(_shared_mod, "increment_rate", return_value=1): req = self._make_request() # Should not raise - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) def test_over_limit_raises_429(self): from fastapi import HTTPException @@ -306,7 +306,7 @@ def test_over_limit_raises_429(self): with patch.object(_shared_mod, "increment_rate", return_value=9999): req = self._make_request() with pytest.raises(HTTPException) as exc_info: - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) assert exc_info.value.status_code == 429 assert "Retry-After" in exc_info.value.headers @@ -319,7 +319,7 @@ def test_exactly_at_limit_is_allowed(self): with patch.object(_shared_mod, "increment_rate", return_value=30): req = self._make_request() # Should not raise — 30 == limit, not 30 > limit - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) def test_one_over_limit_raises(self): from fastapi import HTTPException @@ -330,7 +330,7 @@ def test_one_over_limit_raises(self): with patch.object(_shared_mod, "increment_rate", return_value=31): req = self._make_request() with pytest.raises(HTTPException) as exc_info: - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) assert exc_info.value.status_code == 429 def test_uses_open_namespace(self): @@ -347,7 +347,7 @@ def mock_increment(key, window_seconds, tenant_id): with patch.object(_shared_mod, "increment_rate", side_effect=mock_increment): req = self._make_request() - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) assert captured["tenant_id"] == "_open_" @@ -366,7 +366,7 @@ def mock_increment(key, window_seconds, tenant_id): with patch.object(_shared_mod, "increment_rate", side_effect=mock_increment): req = MagicMock() req.client = None - asyncio.run(api_mod.check_rate_limit_open(req)) + asyncio.run(_shared_mod.check_rate_limit_open(req)) assert "open_rate:unknown" in captured.get("key", "") @@ -485,16 +485,16 @@ def test_api_imports_open_rate_limit_constant(self): import api as api_mod import api_routers._shared as _shared_mod - assert hasattr(api_mod, "RATE_LIMIT_OPEN_PER_MINUTE") or hasattr( - api_mod, "check_rate_limit_open" - ), "api.py must import or define RATE_LIMIT_OPEN_PER_MINUTE or check_rate_limit_open" + assert hasattr(_shared_mod, "RATE_LIMIT_OPEN_PER_MINUTE") or hasattr( + _shared_mod, "check_rate_limit_open" + ), "api_routers._shared must define check_rate_limit_open (T-1 decomposition)" def test_check_rate_limit_open_is_async(self): import asyncio import api as api_mod import api_routers._shared as _shared_mod - assert asyncio.iscoroutinefunction(api_mod.check_rate_limit_open) + assert asyncio.iscoroutinefunction(_shared_mod.check_rate_limit_open) # =============================================================================