#40

Add network topology tab with auto-discovered extractor models

Openbronezppg-archwants to merge intofrom

Network Topology tab

Adds a Topology view to each project that renders a host / credential / access graph derived automatically from the project's free-text facts — turning the prose agents write into a map you can read at a glance.

What it does

  • Facts ↔ Topology toggle on the graph canvas; a Cytoscape (dagre) graph whose nodes are hosts colored by the highest confirmed access level (root/SYSTEM, admin, user, scanned-only), with the attacker box and ICS/OT targets called out, and edges typed as visibility / pivot / cred-works.
  • On-demand generation, cached in a new topology table (auto-migrates via CREATE TABLE IF NOT EXISTS); a staleness badge appears when new facts arrive.
  • Selectable extractor model via a gear dropdown — the recommended option is highlighted; unavailable ones are greyed with the reason.

How the extractor finds models

The feature does not hard-code one backend. list_models() merges:

  1. Workers of the running dispatcher — it locates the live cairn dispatch --config X process, reads that config, and exposes every worker carrying an API endpoint + key (pi/GLM/Qwen/DeepSeek via OpenAI-compatible or Anthropic HTTP, or container-mode claudecode). The dispatcher's primary worker becomes the recommended default, so "whatever model you plugged in" just shows up.
  2. Host CLIs on PATH — claude (sonnet/opus/haiku) and codex.
  3. A fully custom command via CAIRN_TOPO_CMD.

Notes:

  • API keys are read server-side only and are never included in the /topology/models payload sent to the browser.
  • For "thinking" models (e.g. GLM) the call sends thinking: {type: disabled} so they don't spend the whole output budget on reasoning and return empty content; the field is stripped and retried on a 400/422 for strict backends.
  • Claude Haiku is flagged as discouraged (its cyber safeguards reject offensive-security notes).

New endpoints

  • GET /topology/models
  • GET /projects/{id}/topology
  • POST /projects/{id}/topology/refresh?model=<id>

Files

server/topology_extract.py (new), server/routers/topology.py (new), server/db.py, server/app.py, server/static/index.html.

Verified end-to-end against Claude Sonnet (29 hosts / 51 edges) and a live GLM worker (~z-ai/glm-flash-latest via an OpenAI-compatible endpoint, 29 hosts / 44 edges) on a real engagement's 38 facts.


Вкладка «Топология сети» (RU)

Добавляет в каждый проект вкладку Topology — граф хостов / учёток / уровней доступа, который строится автоматически из свободного текста фактов проекта.

Что умеет

  • Переключатель Facts ↔ Topology; граф Cytoscape (dagre), где узлы-хосты раскрашены по наивысшему подтверждённому доступу (root/SYSTEM, admin, user, только скан), выделены атакующая машина и цели АСУ ТП, а рёбра размечены как видимость / пивот / рабочие креды.
  • Генерация по кнопке, результат кэшируется в новой таблице topology; при появлении новых фактов показывается плашка «устарело».
  • Выбор модели-экстрактора через шестерёнку: рекомендованная подсвечена, недоступные серые с причиной.

Как находятся модели

Бэкенд не привязан к одному провайдеру. list_models() объединяет:

  1. Воркеры запущенного диспетчера — находит живой процесс cairn dispatch --config X, читает конфиг и показывает каждый воркер с API-эндпоинтом и ключом (pi/GLM/Qwen/DeepSeek через OpenAI- или Anthropic-совместимый HTTP, либо claudecode в контейнерном режиме). Основной воркер диспетчера становится рекомендованным по умолчанию — «какую нейронку подключил, та и появляется».
  2. Хостовые CLI на PATH — claude (sonnet/opus/haiku) и codex.
  3. Произвольная команда через CAIRN_TOPO_CMD.

Важное:

  • Ключи API читаются только на сервере и никогда не попадают в ответ /topology/models.
  • Для «думающих» моделей (например GLM) запрос шлёт thinking: {type: disabled}, иначе они тратят весь бюджет вывода на рассуждения и возвращают пустой ответ; поле убирается и запрос повторяется при 400/422 на строгих бэкендах.
  • Claude Haiku помечен как нежелательный (его cyber-фильтры отклоняют офенсив-контент).

Проверено

End-to-end на Claude Sonnet (29 хостов / 51 ребро) и на живом GLM-воркере (~z-ai/glm-flash-latest, OpenAI-совместимый эндпоинт, 29 хостов / 44 ребра) на 38 фактах реального проекта.

🤖 Generated with Claude Code