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.
This commit is contained in:
2026-05-22 21:04:16 -04:00
parent ee10b55cfe
commit f2b3393669
1563 changed files with 1810 additions and 77 deletions

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from fastapi import APIRouter
from .auth.api_login import router as login_router

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Shared helpers for the per-attacker routes.
Currently houses the 404 guard used by the SSE events stream

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""SSE stream of per-attacker behavioural events — one connection per
AttackerDetail page.

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/{uuid}/export/misp — MISP event for one attacker.
Converts the attacker's STIX 2.1 bundle to a MISP event JSON file via

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/{uuid}/export/stix — STIX 2.1 bundle for one attacker.
Returns a self-contained STIX 2.1 Bundle with the attacker's IP

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/export — bulk JSON export of all attacker + intel data."""
import json
from datetime import datetime, timezone

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/export/misp — fleet-wide MISP collection.
Returns a MISP collection JSON ({"response": [event, ...]}) with one event

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/export/stix — fleet-wide STIX 2.1 bundle.
Returns a self-contained STIX 2.1 Bundle covering every attacker the

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/{uuid}/attribution — per-primitive
attribution state for one attacker.

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any, Optional
from fastapi import APIRouter, Depends, HTTPException, Query

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/attackers/{uuid}/intel — latest threat-intel row for an attacker."""
from typing import Any

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any, Optional
from fastapi import APIRouter, Depends, HTTPException, status

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from datetime import timedelta
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""SSE stream of campaign events — one connection per viewer.
Subscribes to ``campaign.>`` on the bus for the duration of the

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/campaigns/{uuid} — single campaign row.
Soft-merge handling: if the requested UUID has merged_into_uuid set,

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/campaigns/{uuid}/identities — identities for a campaign.
Returns the ``AttackerIdentity`` rows whose ``campaign_id`` FK points

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/campaigns — paginated list of campaigns.
Mirror of :mod:`decnet.web.router.identities.api_list_identities` for

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Canary tokens — operator-facing CRUD.
Mounted under ``/api/v1/canary``. Covers:

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Operator-uploaded canary blob CRUD.
Three endpoints:

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Operator-facing canary token CRUD.
Every body-bearing route documents the 400 error per

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import uuid as _uuid
from fastapi import APIRouter, Depends, HTTPException

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from fastapi import APIRouter, Depends, HTTPException
from decnet.env import DECNET_DEVELOPER

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from fastapi import APIRouter, Depends
from decnet.telemetry import traced as _traced

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from typing import Any, Optional
from fastapi import APIRouter, Depends, HTTPException, Query

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Cross-cutting decky operation endpoints.
These routes apply to both fleet and MazeNET (topology) deckies; the

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST/DELETE /api/v1/deckies/files — generic file drops on deckies.
Wraps :func:`decnet.decky_io.write_file_to_container` /

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST/DELETE …/{decky}/services — live service add/remove.
Two scopes mounted here:

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST/GET/DELETE /api/v1/deckies/{decky_name}/tarpit — per-decky tc netem tarpit.
Applies port-selective traffic delay on the host veth paired to the target

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import os

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /deckies/lifecycle?ids=… — poll lifecycle rows by id.
The wizard polls this endpoint every ~2 s after POSTing /deckies/deploy

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /deckies/{name}/mutate — operator-triggered single-decky mutate.
Returns 202 Accepted with one ``lifecycle_id`` per mutated decky. The

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from fastapi import APIRouter, Depends, HTTPException
from decnet.telemetry import traced as _traced

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""SSE stream of identity-resolution events — one connection per viewer.
Subscribes to ``identity.>`` on the :class:`~decnet.bus.base.BaseBus` for

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/identities/{uuid} — single identity row.
Soft-merge handling: if the requested UUID has merged_into_uuid set,

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/identities — paginated list of resolved identities.
Returns an empty list while the clusterer hasn't run yet (the

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/identities/{uuid}/observations — observations for an identity.
Returns the per-IP ``Attacker`` rows whose ``identity_id`` FK points at

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/orchestrator/events/stats — authoritative failure count.
The dashboard's failure-count badge previously derived its number from

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""SSE stream of orchestrator events.
Subscribes to ``orchestrator.>`` for the duration of the request and

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /api/v1/orchestrator/events — paginated orchestrator activity.
Two underlying tables back this endpoint:

View File

@@ -0,0 +1 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET/PUT ``/api/v1/realism/config`` — operator-tunable realism knobs.
Today only the planner's content-class weights + canary probability

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET/PUT ``/api/v1/realism/llm`` — LLM provider configuration.
Reads accept viewer; writes are admin (same trust level as the existing

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET/PUT ``/api/v1/realism/personas`` — global persona pool CRUD.
The "global pool" is a JSON file consumed by the realism content

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET ``/api/v1/realism/synthetic-files`` — browse planted realism files.
The orchestrator's realism worker grows synthetic files on each decky

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import time
from typing import Any, Optional

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio
import orjson

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Swarm controller routers.
One file per endpoint, aggregated under the ``/swarm`` prefix. Mounted

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/check — active mTLS probe of every enrolled worker.
Updates ``SwarmHost.status`` and ``last_heartbeat`` for each host based

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""DELETE /swarm/hosts/{uuid} — decommission a worker.
Removes the DeckyShard rows bound to the host (portable cascade — MySQL

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/deploy — shard a DecnetConfig across enrolled workers.
Per worker we build a filtered copy containing only the deckies assigned

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/enroll — issue a worker cert bundle and register the host.
Enrollment is master-driven: the controller holds the CA private key,

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/hosts/{uuid} — fetch a single worker by UUID."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/health — controller liveness (no I/O)."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/heartbeat — agent→master liveness + decky snapshot refresh.
Workers call this every ~30 s with the output of ``executor.status()``.

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/deckies — list decky shards with their worker host's identity.
The DeckyShard table maps decky_name → host_uuid; users want to see which

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/hosts — list enrolled workers, optionally filtered by status."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/teardown — tear down one or all enrolled workers."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Swarm management endpoints for the React dashboard.
These are *not* the unauthenticated /swarm routes mounted on the separate

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""DELETE /swarm/hosts/{uuid} — decommission a worker from the dashboard.
Also instructs the worker agent to stop all DECNET services and delete

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Agent-enrollment bundles — the Wazuh-style one-liner flow.
Three endpoints:

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/deckies — admin-gated list of decky shards across the fleet."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm/hosts — admin-gated list of enrolled workers for the dashboard.
Fans out an ``AgentClient.health()`` probe to each host on every call and

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm/hosts/{uuid}/teardown — remote teardown on a swarm worker.
Body: ``{"decky_id": "..."}`` (optional). When ``decky_id`` is null/omitted

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Remote Updates — master dashboard's surface for pushing code to workers.
These are *not* the swarm-controller's /swarm routes (those run on a

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /swarm-updates/hosts — per-host updater health + release slots.
Fans out an ``UpdaterClient.health()`` probe to every enrolled host that

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm-updates/push — fan a tarball of the master's tree to workers.
Mirrors the ``decnet swarm update`` CLI flow: build the tarball once,

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm-updates/push-self — push only to workers' /update-self.
Use case: the agent is fine but the updater itself needs an upgrade (e.g.

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /swarm-updates/rollback — manual rollback on a single host.
Calls the worker updater's ``/rollback`` which swaps the ``current``

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from fastapi import APIRouter
from .api_deployment_mode import router as deployment_mode_router

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""GET /system/deployment-mode — tells the UI whether a deploy will shard
across SWARM workers or land on the master itself.

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""MazeNET topology REST endpoints (phase 3).
Thin FastAPI layer over the phase-2 topology machinery:

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Shared helpers for the Phase-3 child-CRUD routes."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Shared validation for the ``mode`` / ``target_host_uuid`` pair.
Called by the two topology-create endpoints

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Read-only catalog endpoints — services, next-subnet, next-ip.
These wrap fleet/allocator helpers so the phase-4 canvas UI can lean

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /topologies/blank — create an empty editable topology.
Produces a minimal ``pending`` topology seeded with exactly one DMZ LAN

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /topologies — generate and persist a new MazeNET topology."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Decky CRUD endpoints — pending-only child mutations.
POST /topologies/{id}/deckies

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""DELETE /topologies/{id} — cascade-delete a pending or torn-down topology."""
from __future__ import annotations

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""POST /topologies/{id}/deploy — transition pending → deploying and fire
the background deploy.

Some files were not shown because too many files have changed in this diff Show More