Files
DECNET/decnet/web/router/swarm/__init__.py
anti 148e51011c feat(swarm): agent→master heartbeat with per-host cert pinning
New POST /swarm/heartbeat on the swarm controller. Workers post every
~30s with the output of executor.status(); the master bumps
SwarmHost.last_heartbeat and re-upserts each DeckyShard with a fresh
DeckyConfig snapshot and runtime-derived state (running/degraded).

Security: CA-signed mTLS alone is not sufficient — a decommissioned
worker's still-valid cert could resurrect ghost shards. The endpoint
extracts the presented peer cert (primary: scope["extensions"]["tls"],
fallback: transport.get_extra_info("ssl_object")) and SHA-256-pins it
to the SwarmHost.client_cert_fingerprint stored for the claimed
host_uuid. Extraction is factored into _extract_peer_fingerprint so
tests can exercise both uvicorn scope shapes and the both-unavailable
fail-closed path without mocking uvicorn's TLS pipeline.

Adds get_swarm_host_by_fingerprint to the repo interface (SQLModel
impl reuses the indexed client_cert_fingerprint column).
2026-04-19 21:37:15 -04:00

38 lines
1.4 KiB
Python

"""Swarm controller routers.
One file per endpoint, aggregated under the ``/swarm`` prefix. Mounted
onto the swarm-api FastAPI app (``decnet/web/swarm_api.py``), a separate
process from the main DECNET API so swarm failures cannot cascade into
log ingestion / dashboard serving.
"""
from fastapi import APIRouter
from .api_enroll_host import router as enroll_host_router
from .api_list_hosts import router as list_hosts_router
from .api_get_host import router as get_host_router
from .api_decommission_host import router as decommission_host_router
from .api_deploy_swarm import router as deploy_swarm_router
from .api_teardown_swarm import router as teardown_swarm_router
from .api_get_swarm_health import router as get_swarm_health_router
from .api_check_hosts import router as check_hosts_router
from .api_heartbeat import router as heartbeat_router
from .api_list_deckies import router as list_deckies_router
swarm_router = APIRouter(prefix="/swarm")
# Hosts
swarm_router.include_router(enroll_host_router)
swarm_router.include_router(list_hosts_router)
swarm_router.include_router(get_host_router)
swarm_router.include_router(decommission_host_router)
# Deployments
swarm_router.include_router(deploy_swarm_router)
swarm_router.include_router(teardown_swarm_router)
swarm_router.include_router(list_deckies_router)
# Health
swarm_router.include_router(get_swarm_health_router)
swarm_router.include_router(check_hosts_router)
swarm_router.include_router(heartbeat_router)