The backend provides a read-only FastAPI service, PostgreSQL-compatible data models, versioned migrations, seeded demonstration records, optional bearer authentication, and source-aware MITRE ATT&CK and Malpedia ingestion.
cd backend
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'
cp .env.example .env
alembic upgrade head
uvicorn app.main:app --reload --port 8001Open http://127.0.0.1:8001/docs for interactive API documentation. The
dashboard served on port 8000 automatically loads /api/dashboard/bootstrap
and falls back to bundled demonstration records when the API is unavailable.
From the repository root:
docker compose up --buildThe API is available at http://127.0.0.1:8001.
Run both source adapters once:
cd backend
python -m app.worker --once --source allRun a single adapter:
python -m app.worker --once --source mitre
python -m app.worker --once --source malpediaConfiguration:
MITRE_STIX_URL=https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/enterprise-attack/enterprise-attack.json
MALPEDIA_BASE_URL=https://malpedia.caad.fkie.fraunhofer.de
MALPEDIA_API_TOKEN=
INGESTION_INTERVAL_SECONDS=3600Malpedia actor endpoints do not require registration. If a token is provided,
the backend sends Authorization: apitoken <token> as documented by Malpedia.
Never put this token in index.html or another browser-delivered file.
The identity merge order is:
- Existing source external ID
- MITRE ATT&CK group ID
- Case-insensitive canonical name or alias
- New actor profile
Each actor may have multiple actor_sources records. These retain the source
name, external ID, public profile URL, and last successful sync time. MITRE
contributes descriptions and ATT&CK technique relationships; Malpedia enriches
aliases, country/sponsor metadata when existing fields are unknown, and its
actor profile link. Empty source fields never erase populated actor fields.
To run scheduled syncs in Docker:
docker compose --profile ingestion up --buildThe legacy custom STIX workflow remains available:
python -m app.worker --once --source stix --file /path/to/bundle.json
python -m app.worker --once --source stix --url https://approved.example/bundle.jsonEvery attempt is recorded in ingestion_runs with status and item counts.
Development is unauthenticated by default. To require a bearer token:
AUTH_REQUIRED=true
API_AUTH_TOKEN=replace-with-a-long-random-tokenThe health endpoint remains public. Data endpoints require
Authorization: Bearer <token>. The dashboard asks for the token after a 401
response and keeps it in browser session storage only.
GET /api/healthGET /api/dashboard/bootstrapGET /api/actorsGET /api/actors/{actor_id}GET /api/campaignsGET /api/iocsGET /api/attack/techniquesGET /api/reportsGET /api/ingestion/runs
Actor responses include source_profiles, for example:
{
"source_name": "Malpedia",
"external_id": "apt28",
"profile_url": "https://malpedia.caad.fkie.fraunhofer.de/actor/apt28",
"last_synced_at": "2026-08-04T18:00:00Z"
}cd backend
alembic upgrade head
pytestSee DEPLOYMENT.md for the production Compose stack and operational requirements.