This guide documents the HTTP surface exposed by the CORE Topology Generator web backend (webapp/app_backend.py) and the CLI entry point (core_topo_gen.cli). Use it to script scenario management, trigger runs, download artifacts, and integrate with external systems.
- Default base URL:
http://localhost:9090 - Entry modules:
- Web server:
python webapp/app_backend.py - CLI:
core-python -m core_topo_gen.cli(fall back topython -m core_topo_gen.cliifcore-pythonis unavailable)
- Web server:
- Artifacts:
- Scenario XML snapshots:
outputs/scenarios-<timestamp>/ - Run history index:
outputs/run_history.json - Reports:
./reports/scenario_report_<timestamp>.md
- Scenario XML snapshots:
The web UI uses cookie sessions. Script clients must authenticate once and reuse the cookie for subsequent requests.
POST /login- Form fields:
username,password - Success: HTTP 302 redirect to
/with asessioncookie. - Failure: HTTP 200 with an error message rendered in HTML.
- Form fields:
POST /logout- Clears the session and redirects to
/.
- Clears the session and redirects to
First run: The app may create a default admin user. Refer to the README for the bootstrap credentials and rotate them immediately.
- JSON payloads and responses are UTF-8 encoded.
- Unless noted, endpoints return
{ "ok": boolean, ... }or redirect to HTML views. - File parameters must be provided using
multipart/form-data. - Absolute paths are recommended (
os.path.abspath). When a relative path is supplied, the server resolves it against the repo root where possible. - Safe-delete operations only touch files under
uploads/oroutputs/; reports in./reports/are preserved. - Planning preview results are cached in
outputs/plan_cache.json, keyed by(xml_hash, scenario, seed). Override the location withTOPO_PLAN_CACHE_PATH.
- Health
- Scenario Lifecycle
- Planning Preview
- Run Execution & Reports
- Script Inspection
- Docker Helpers
- CORE Session Management
- Data Sources & Vulnerability Catalog
- Diagnostics & Maintenance
- User Administration
GET /healthz
- Returns plain-text
OKwhen the server is running.
POST /load_xml
: Multipart upload (scenarios_xml .xml file). Loads the file into the editor state and renders the main page.
POST /save_xml
: Form field scenarios_json (stringified JSON). Persists the editor payload to outputs/scenarios-<timestamp>/scenarios.xml and re-renders the editor. The saved XML includes additive planning attributes (base_nodes, combined_nodes, explicit_count, etc.) for lossless round-tripping.
POST /save_xml_api
: JSON body { "scenarios": [...], "active_index"?: int }. Returns { "ok": true, "result_path": ".../scenarios.xml" } on success or { "ok": false, "error": "..." } with HTTP 400/500 on failure.
GET /api/host_interfaces
: Returns { "interfaces": [...] } describing host NICs (name, mac, ipv4, ipv6, mtu, speed, flags, is_up). Requires psutil; if unavailable, returns an empty list with a warning in logs.
POST /upload_base
: Multipart upload (base_xml). Attaches a CORE base topology XML to the active scenario. Redirects to /.
POST /remove_base
: Optional scenarios_json to retain other edits while clearing the base topology. Renders the updated editor view.
GET /base_details
: Query path=<abs_xml_path>. Renders an HTML summary validating the CORE XML.
POST /api/plan/preview_full
Generates a deterministic planning preview without starting a CORE session.
Request JSON
{
"xml_path": "/abs/path/to/scenarios.xml",
"scenario": "Scenario Name", // optional
"seed": 12345 // optional; random when omitted (returned in response)
}Response JSON
{
"ok": true,
"full_preview": {
"routers": [...],
"hosts": [...],
"switches": [...],
"services_preview": {...},
"vulnerabilities_preview": {...},
"segmentation_preview": {...},
"traffic_preview": {...},
"seed": 12345,
"seed_generated": false
},
"plan": {
"role_counts": {"Workstation": 12, ...},
"routers_planned": 3,
"service_plan": {...},
"vulnerability_plan": {...},
"segmentation_plan": {...},
"traffic_plan": {...}
},
"breakdowns": {
"node": {...},
"router": {...},
"services": {...},
"vulnerabilities": {...},
"segmentation": {...},
"traffic": {...}
}
}Notes
seedis echoed or generated automatically. Store it to reproduce the same topology.r2s_policy_preview.per_router_boundsincludes min/max bounds when NonUniform host grouping is requested via XML attributes (r2s_hosts_min/r2s_hosts_max).- Exact aggregation (
r2s_mode=Exactandr2s_edges=1) collapses hosts behind a single switch and ignores bounds. - Preview responses are cached; purge
outputs/plan_cache.jsonto invalidate.
POST /run_cli
: Form field xml_path (absolute path). Runs the CLI synchronously with forwarded args --xml, --host, --port, --verbose (values derived from the saved XML when available). Returns the main page with logs. Side effects:
- Markdown report written to
./reports/ - JSON summary (
scenario_report_<timestamp>.json) next to the report with counts and metadata - Router aggregation metrics appended when routers are generated
- Pre/post CORE session XML captured under
outputs/core-sessions/when available - Run history appended to
outputs/run_history.json
POST /run_cli_async
: Same args as synchronous run. Returns { "run_id": "<uuid>" } immediately and writes logs to outputs/scenarios-<timestamp>/cli-<run_id>.log.
GET /run_status/<run_id>
: Polling endpoint returning:
{
"done": false,
"returncode": null,
"report_path": null,
"xml_path": null,
"log_path": "outputs/.../cli-<run_id>.log",
"scenario_xml_path": "outputs/.../scenarios.xml",
"pre_xml_path": null,
"full_scenario_path": null
}GET /stream/<run_id>
: Server-Sent Events (SSE) endpoint streaming live CLI log lines for async runs.
POST /cancel_run/<run_id>
: Attempts to terminate a running async job.
GET /reports
: Renders the Reports UI.
GET /reports_data
: Returns { "history": [...], "scenarios": [...] }, combining run metadata and known scenario files. Each history entry includes timestamp, mode, returncode, scenario_xml_path, report_path, pre_xml_path, post_xml_path, full_scenario_path, run_id, and parsed scenario_names.
GET /download_report?path=<path>
: Streams a report or artifact file. Accepts absolute or repo-relative paths.
POST /reports/delete
: JSON body { "run_ids": ["..."] }. Removes matching run history entries and deletes their artifacts under outputs/. Reports in ./reports/ remain untouched. Responds with { "deleted": <count> }.
POST /purge_history_for_scenario
: JSON body { "name": "Scenario" }. Removes all history entries tied to the scenario name and deletes associated artifacts under outputs/. Returns { "removed": <count>, "error"?: string }.
Report path detection: The backend parses the CLI log line Scenario report written to .... If missing, it falls back to the most recent ./reports/scenario_report_*.md.
GET /api/open_scripts
: Query params kind=traffic|segmentation (default traffic), scope=runtime|preview (default runtime). Returns { "ok": true, "kind": "traffic", "scope": "runtime", "path": "/tmp/traffic", "files": [...] }.
GET /api/open_script_file
: Same parameters, plus file=<filename>. Returns { "content": "...", "truncated": false } with up to 8KB per request.
GET /api/download_scripts
: Same parameters, responds with a ZIP archive containing the filtered scripts.
GET /docker/status
: Enumerates tracked Docker assignments with compose status:
{
"items": [{
"name": "node1",
"compose": "docker-compose.yml",
"exists": true,
"pulled": false,
"container_exists": false,
"running": false
}],
"timestamp": 1733422330
}POST /docker/cleanup
: Optional JSON body { "names": ["node1"] }. Stops and removes containers via docker stop / docker rm, returning { "ok": true, "results": [{ "name": "node1", "stopped": true, "removed": true }] }.
GET /core
: Renders the CORE session dashboard.
GET /core/data
: Returns { "sessions": [...], "xmls": [...] }. Sessions include gRPC metadata (id, state, node count, backing XML). XML entries list discovered CORE files and their validation status.
POST /core/upload
: Multipart field xml_file. Saves validated CORE XML under uploads/core/.
POST /core/start
: Form field path=<abs_xml_path>. Starts a new CORE session using the provided XML.
POST /core/stop
: Form field session_id=<int>.
POST /core/delete
: Form fields:
- session_id (optional) to delete a running CORE session
- path (optional) to remove a CORE XML under uploads/ or outputs/
GET /core/details
: Query parameters path=<abs_xml_path> and/or session_id=<int>. Renders validation results. When only session_id is provided, the server exports the current session XML for inspection.
POST /core/save_xml
: Form field session_id=<int>. Saves the running session’s XML into outputs/core-sessions/ and streams it back as a download.
POST /core/start_session
: Form field session_id=<int> to start an existing session.
GET /core/session/<sid>
: Convenience view for a single session.
POST /test_core
: Form or JSON body with host (string) and port (int). Returns { "ok": true } when gRPC connectivity succeeds.
GET /data_sources
: Renders the data sources administration page.
POST /data_sources/upload
: Multipart field csv_file. Adds a new data source.
POST /data_sources/toggle/<sid>
: Enables or disables a data source.
POST /data_sources/delete/<sid>
: Removes a data source.
POST /data_sources/refresh/<sid>
: Refreshes a source (implementation-specific).
GET /data_sources/download/<sid>
: Downloads a single source as CSV.
GET /data_sources/export_all
: Downloads all sources in a ZIP or bundled CSV.
GET /data_sources/edit/<sid>
: Renders the inline CSV editor.
POST /data_sources/save/<sid>
: JSON body { "rows": [["Header", "Value"], ...] }. Normalizes and saves the CSV, then redirects back to the editor. Malformed payloads return HTTP 400.
GET /vuln_catalog
: Renders the vulnerability catalog view.
POST /vuln_compose/status
: JSON { "items": [{ "Name": "Node1", "Path": "...", "compose"?: "docker-compose.yml" }] }. Returns { "items": [...], "log": [...] } with compose availability and Docker pull state.
POST /vuln_compose/download
: Same payload. Supports GitHub URLs (cloned via git) and direct download paths. Responds with { "items": [...], "log": [...] } summarizing results.
POST /vuln_compose/pull
: Performs docker compose pull for each item. Requires Docker CLI access.
POST /vuln_compose/remove
: Runs docker compose down --volumes --remove-orphans, removes images, and deletes downloaded directories under outputs/.
GET /diag/modules
: Returns imported module metadata to help troubleshoot environment issues.
POST /admin/cleanup_pycore
: Removes stale /tmp/pycore.* directories. Response { "ok": true, "removed": [...], "kept": [...], "active_session_ids": [...] }.
GET /users
: Admin-only view listing users.
POST /users
: Form fields username, password, role (user|admin, default user). Fails with a flash error if the username already exists.
POST /users/delete/<username>
: Removes the specified user (admin only).
POST /users/password/<username>
: Admin resets another user’s password. Form field password (new value).
GET /me/password
: Renders self-service password form.
POST /me/password
: Form fields current_password, password. Allows users to update their own credential.
Invoke from the repo root to ensure generated reports land in ./reports/:
core-python -m core_topo_gen.cli --xml /abs/path/scenarios.xml --verbose--xml(required): Scenario XML path.--scenario: Scenario name (defaults to the first in the file).--host,--port: CORE gRPC endpoint (defaults127.0.0.1:50051).--prefix: IPv4 prefix for auto-assigned addresses (default10.0.0.0/24).--ip-mode:private | mixed | public(defaultprivate).--ip-region:all | na | eu | apac | latam | africa | middle-east(defaultall).--max-nodes: Hard cap on node creation.--verbose: Enables debug logging.--seed: RNG seed for deterministic randomness.--layout-density:compact | normal | spacious(defaultnormal).--router-mesh-style:full | ring | tree(fallback when routing items omitr2r_mode).
--traffic-pattern:continuous | burst | periodic | poisson | ramp--traffic-rate: Float KB/s--traffic-period: Float seconds--traffic-jitter: Float percentage (0–100)--traffic-content:text | photo | audio | video
--allow-src-subnet-prob: Float 0–1 (default 0.3)--allow-dst-subnet-prob: Float 0–1 (default 0.3)--nat-mode:SNAT | MASQUERADE(defaultSNAT)--dnat-prob: Float 0–1 (default 0.0)--seg-include-hosts: Include hosts when deriving segmentation rules.--seg-allow-docker-ports: Ensure host INPUT chains allow docker-compose ports when default deny is applied.
- CLI automatically parses additive planning metadata via
parse_planning_metadata. Detected values are merged into scenario metadata with aplan_prefix and appear in reports under Planning Metadata (from XML). - CORE host/port defaults are overridden by
core.hostandcore.portsaved in the editor payload when present. - Extend the web backend if additional CLI flags must be surfaced to the UI.
<section name="Routing" density="0.5">
<!-- Balanced degree distribution among density-derived routers -->
<item selected="OSPF" factor="1" r2r_mode="Uniform" />
<!-- Two absolute routers with NonUniform aggregation targeting five hosts per switch -->
<item selected="BGP" v_metric="Count" v_count="2" r2r_mode="NonUniform"
r2s_mode="aggregate" r2s_edges="5" />
</section>- Density
0.5over 12 base hosts yields 6 density routers. - Two
Countrouters bring the total tomin(total_hosts, 6 + 2). - NonUniform aggregation introduces additional layer-2 switches sized to approximately five hosts each.
The web UI writes additive planning attributes onto section tags to support round-tripping and external tooling.
base_nodes: Density-derived hosts.additive_nodes: Hosts from Count rows.combined_nodes: Total planned hosts (base_nodes + additive_nodes).weight_rows/count_rows: Row counts by type.weight_sum: Sum of weight factors.
explicit_count: Count-based entries with absolute values.derived_count: Density-derived totals.total_planned:explicit_count + derived_count.weight_rows,count_rows,weight_sum: Analogous to Node Information.
from core_topo_gen.parsers.planning_metadata import parse_planning_metadata
meta = parse_planning_metadata("outputs/scenarios-123/scenarios.xml", "Scenario 1")
print(meta["node_info"]["combined_nodes"])- The legacy
core_topo_gen.parsers.xml_parsermodule was removed in 2025-10; import section-specific parsers instead. - When attributes are absent (legacy XML), parsing gracefully recomputes approximate values.
- Currently expose structural placeholders (
explicit_count,weight_rows,count_rows,weight_sum). - Derived totals may be added in future releases as semantics mature.