Files
DECNET/decnet/web/swarm_api.py
anti f2b3393669 chore: relicense to AGPL-3.0-or-later and add SPDX headers
Replaces LICENSE (GPLv3 -> AGPLv3) and prepends
`SPDX-License-Identifier: AGPL-3.0-or-later` to every source file
across decnet/, decnet_web/, tests/, scripts/, and tools/.

Rationale: closes the GPLv3 ASP loophole so any party operating a
modified DECNET as a network service must offer their modified
source. Personal copyright (Samuel Paschuan) + inbound=outbound
contributions make a future unilateral relicense infeasible.

- LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt)
- COPYRIGHT: project copyright notice
- tools/add_spdx_headers.py: idempotent header injector
  (shebang- and PEP 263-aware)

Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh).
No behavior change; comments only.
2026-05-22 21:04:16 -04:00

69 lines
2.1 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""DECNET SWARM Controller — master-side control plane.
Runs as an independent FastAPI/uvicorn process. Isolated from
``decnet.web.api`` so controller failure cannot cascade to the main API,
ingester, or dashboard (mirrors the existing pattern used by
``decnet api`` with ``start_new_session=True``).
Responsibilities:
* host enrollment (issues CA-signed worker bundles);
* dispatching DecnetConfig shards to worker agents over mTLS;
* active health probes of enrolled workers.
The controller *reuses* the same ``get_repo`` dependency as the main API,
so SwarmHost / DeckyShard state is visible to both processes via the
shared DB.
"""
from __future__ import annotations
from decnet.web import _uvicorn_tls_scope # noqa: F401 # patches uvicorn on import
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
from decnet.logging import get_logger
from decnet.swarm import pki
from decnet.swarm.client import ensure_master_identity
from decnet.web.dependencies import repo
from decnet.web.router.swarm import swarm_router
log = get_logger("swarm_api")
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
log.info("swarm-controller starting up")
# Make sure the CA and master client cert exist before we accept any
# request — enrollment needs them and AgentClient needs them.
pki.ensure_ca()
ensure_master_identity()
await repo.initialize()
log.info("swarm-controller ready")
yield
log.info("swarm-controller shutdown")
app: FastAPI = FastAPI(
title="DECNET SWARM Controller",
version="0.1.0",
lifespan=lifespan,
default_response_class=ORJSONResponse,
# No interactive docs: the controller is an internal management plane,
# not a public surface. Enable explicitly in dev if needed.
docs_url=None,
redoc_url=None,
openapi_url=None,
)
app.include_router(swarm_router)
@app.get("/health")
async def root_health() -> dict[str, str]:
"""Top-level liveness probe (no DB I/O)."""
return {"status": "ok", "role": "swarm-controller"}