Files
bootnode/api/bootnode/main.py
T
zeekay 0aa9c4e346 feat: add Explorer + MPC product pages, fleet management, operator, Helm charts, and CI/CD
- Add Explorer product page with branded block explorer deployment, contract
  verification, Etherscan-compatible API, and multi-chain support
- Add MPC product page with deep Lux MPC integration: CGGMP21 (ECDSA),
  FROST (EdDSA), LSS resharing, 15+ chain support, threshold signing API
- Update landing page with 6-product grid (Node, Data, Wallets, Explorer,
  MPC, Rollups) and 8 API cards including Explorer API and MPC Signing
- Update navbar and command menu with Explorer and MPC products
- Add Lux fleet management dashboard, observability, and services pages
- Add AI chat component and unified navigation with org switcher
- Add Kubernetes operator (Rust) for multi-chain node lifecycle management
- Add Lux Helm charts with genesis configs for devnet/testnet/mainnet
- Add docker-release.yml CI/CD pipeline for automated DOKS deployment
- Add monitoring, network policies, web deployments, and ingress updates
- Refactor API: fleet management, billing tracker, observability endpoints
- Update brand config, API client, and infrastructure manifests
2026-03-12 14:48:40 -07:00

154 lines
4.6 KiB
Python

"""Bootnode API - Main application entry point."""
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
import structlog
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import ORJSONResponse
from bootnode.api import router as api_router
from bootnode.config import get_settings
from bootnode.core.cache import redis_client
from bootnode.core.datastore import datastore_client
from bootnode.core.kms import inject_secrets
from bootnode.db.session import engine, init_db
from bootnode.ws import router as ws_router
from bootnode.zap import start_zap_server, stop_zap_server
# Load secrets from Hanzo KMS before settings
inject_secrets()
# ---------------------------------------------------------------------------
# Hanzo O11y — Traces + Metrics + Logs
# All telemetry → OTEL Collector → Datastore + Loki
# Prometheus /metrics served natively for ServiceMonitor scraping
# ---------------------------------------------------------------------------
from bootnode.core.o11y import setup_otel, setup_otel_logging
# Wire OTEL log export early (before any logging)
setup_otel_logging()
logger = structlog.get_logger()
settings = get_settings()
@asynccontextmanager
async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
"""Application lifespan handler."""
# Startup
logger.info("Starting Bootnode API", env=settings.app_env)
await init_db()
await redis_client.initialize()
# Initialize DataStore - optional, won't fail startup
try:
await datastore_client.initialize()
logger.info("DataStore connected")
except Exception as e:
logger.warning("DataStore not available", error=str(e))
# Start native ZAP server (Cap'n Proto RPC over TCP)
zap_server = None
if settings.zap_enabled:
try:
zap_server = await start_zap_server(
host=settings.zap_host,
port=settings.zap_port,
)
logger.info(
"ZAP server started",
url=f"zap://{settings.zap_host}:{settings.zap_port}",
)
except Exception as e:
logger.warning("ZAP server failed to start", error=str(e))
yield
# Shutdown
logger.info("Shutting down Bootnode API")
# Stop ZAP server
if zap_server:
try:
await stop_zap_server()
logger.info("ZAP server stopped")
except Exception as e:
logger.warning("ZAP server stop error", error=str(e))
await engine.dispose()
await redis_client.close()
await datastore_client.close()
app = FastAPI(
title="Bootnode API",
description="Blockchain Development Platform - Multi-chain RPC, Token API, NFT API, Smart Wallets, and more",
version="2.0.0",
docs_url="/docs" if not settings.is_production else None,
redoc_url="/redoc" if not settings.is_production else None,
openapi_url="/openapi.json" if not settings.is_production else None,
default_response_class=ORJSONResponse,
lifespan=lifespan,
)
# Hanzo O11y — /metrics + OTEL traces/metrics/logs
setup_otel(app)
# Validate production secrets (fail-fast)
settings.validate_production()
# CORS - all cloud domains for white-label multi-network support
production_origins = [
"https://bootno.de",
"https://lux.build",
"https://cloud.lux.network",
"https://cloud.pars.network",
"https://cloud.zoo.network",
"https://cloud.hanzo.ai",
"https://docs.bootno.de",
]
# Add frontend_url if set
if settings.frontend_url:
origin = settings.frontend_url.rstrip("/")
if origin not in production_origins:
production_origins.append(origin)
# Add extra origins from ALLOWED_ORIGINS env var
for extra in getattr(settings, "allowed_origins", []):
origin = extra.strip().rstrip("/")
if origin and origin not in production_origins:
production_origins.append(origin)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"] if not settings.is_production else production_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API routes
app.include_router(api_router, prefix=settings.api_prefix)
# Include WebSocket routes
app.include_router(ws_router, prefix=f"{settings.api_prefix}/ws", tags=["WebSocket"])
@app.get("/health")
async def health_check() -> dict:
"""Health check endpoint."""
return {"status": "healthy", "version": "2.0.0"}
@app.get("/")
async def root() -> dict:
"""Root endpoint."""
return {
"name": "Bootnode API",
"version": "2.0.0",
"docs": "/docs",
"status": "operational",
}