Skip to content
BreachPilot

System Endpoints

tools/api/routes/system.py:1APIRouter(prefix="/api/v1", tags=["system"]). All routes below are mounted at /api/v1/* via app.py:148. Every route except GET /health depends on _require_auth (tools/api/routes/system.py:49) → BearerAuth.__call__ 401 when missing/invalid. Module wiring: configure(auth, config, config_path) + configure_run_manager(run_manager) (tools/api/routes/system.py:27).

GET /api/v1/healthhealth

tools/api/routes/system.py:56no auth. Response 200 {version:"v1", ready:true}.

GET /api/v1/capabilitiescapabilities

tools/api/routes/system.py:62 — requires bearer. Reads live api.max_concurrent_runs from config. Returns api_version, features (advisory MCP families + surfaces like graph_route, poc_verification, etc.), constraints (max_concurrent_runs, loopback_only:true, manual_tool_calls:true), run_options (modes recon|attack|fast, kinds agent, flags). The WebUI gates panels on features.

GET /api/v1/configget_config

tools/api/routes/system.py:136 — bearer. Returns redacted config via sanitize(_CONFIG) (tools/api/errors.py:62).

PATCH /api/v1/configpatch_config

tools/api/routes/system.py:190 — bearer. Body must be dict. Deep-merge via _merge_config (tools/api/routes/system.py:39), validate with _write_config (tools/api/routes/system.py:142): rejects api.allowed_origins containing non-loopback (is_loopback_origin), runs ConfigValidator.validate(), atomic tmp.write + os.replace. On success {status:"ok", config: sanitize(merged)} (tools/api/routes/system.py:202). Errors 400 invalid_body, 400 config_invalid.

Internal helpers: _write_config(merged) and _apply_config_patch(patch) (tools/api/routes/system.py:142, :185).

GET /api/v1/secretsget_secrets

tools/api/routes/system.py:205 — bearer. Returns {keys: {ENV:"configured"|"missing"}} derived from configured_api_key_env_names(_CONFIG) + load_api_key_file + os.environ (tools/api_key_store.py). Never returns values.

PUT /api/v1/secretsput_secrets

tools/api/routes/system.py:227 — bearer. Body {secrets:{name:value}}. Validates names in configured_api_key_env_names, values non-empty strings else 400 invalid_secrets. Writes via save_api_keys + injects into os.environ. Response 200 {status:"ok", written:[...]}.

GET /api/v1/modelslist_models

tools/api/routes/system.py:261 — bearer. Returns {provider, default_alias, registry, info, chatgpt?} via get_ai_provider / get_chatgpt_config (tools/config_manager.py).

POST /api/v1/modelsadd_model

tools/api/routes/system.py:284 — bearer. Body {alias, model} stripped, both non-empty, len guards 64/256. _apply_config_patch({"models":{"registry":{alias:model}}}) + ConfigValidator. Returns {status:"ok", alias, model, registry}.

DELETE /api/v1/models/{alias}remove_model

tools/api/routes/system.py:302 — bearer. copy.deepcopy(_CONFIG), refuse unknown 404, refuse deleting default_alias 400 invalid_model, remove from registry + info, _write_config. Response {status:"ok", alias, deleted:true}.

POST /api/v1/models/providerset_model_provider

tools/api/routes/system.py:323 — bearer. Body {provider} must be ollama|chatgpt else 400 invalid_provider. Patches models.provider, returns {status:"ok", provider}.

POST /api/v1/models/refreshrefresh_models

tools/api/routes/system.py — bearer, ollama provider only (400 invalid_provider). Off-thread tools/ollama_models.refresh_model_registry(_CONFIG, persist=False): GET {ollama.host}/api/tags (bearer), bumps every alias to the strictly newest same-family version (glm-5.2:cloudglm-5.3:cloud; tag preference preserved; versionless specs untouched; no pulls). Updates persist via _apply_config_patch (validated write). 200 {ok, host, available_count, updates:{alias:{old,new}}, registry, persisted}; 503 {ok:false, error} when Ollama is unreachable. Same sync runs at daemon boot when models.auto_update is true.

GET /api/v1/system/infoget_system_info

tools/api/routes/system.py:338 — bearer. hostname, platform, os, python, local_ips (socket.getaddrinfo), public_ip via urllib.request https://api.ipify.org 3 s timeout degraded to null offline (tools/api/routes/system.py:368 off-thread).

GET /api/v1/system/telemetryget_telemetry

tools/api/routes/system.py:380 — bearer. Off-thread workspace_root_from_sources(_CONFIG_PATH)usage_summary + read_usage_records(..., limit=50) (tools/model_telemetry.py). Numeric/categorical only, no prompts.

GET /api/v1/system/memoryget_memory

tools/api/routes/system.py:520 — bearer. Off-thread _load_memory_sync (tools/api/routes/system.py:445): Flow B lessons table (no embeddings) + confidence Beta(1,1) aggregation + attack_memory.db attack_memory_items under reports_dir (_read_attack_memory_db reports/**/*.attack_memory.db). Best-effort empty on error.

POST /api/v1/system/resetreset_system

tools/api/routes/system.py:526 — bearer. Refuses 409 conflict if run_manager.has_active. Else persistence.reset_all() (keep file), shutil.rmtree(reports_dir, exploit_workspace, swarm_workspace) and recreate reports_dir + _init_db(), wipe research_workspace tables in-place (file locked) and delete children. Response {status:"ok", runs_deleted, removed:[...], research_cleared}.

GET /api/v1/pluginslist_plugins

tools/api/routes/system.py:611 — bearer. tools.plugins.list_discovered_plugins() else [].

GET /api/v1/skillslist_skills

tools/api/routes/system.py:622 — bearer. get_registry(_CONFIG).list_skills(){name, description, tags} each; on error {skills:[], error}.

GET /api/v1/skills/searchsearch_skills

tools/api/routes/system.py:638 — bearer. Query q="". reg.search(q) if q else list_skills() capped 20 → {results:[{name,description}]}.

POST /api/v1/diagnostics/doctorrun_doctor

tools/api/routes/system.py:664 — bearer. Off-thread _run_doctor_sync(config_path) (tools/api/routes/system.py:651) which redirect_stdouttools.doctor.run_doctor. Response {exit_code:int, output:str}.

POST /api/v1/diagnostics/self-testrun_self_test

tools/api/routes/system.py:671 — bearer. redirect_stdoutawait tools.self_test.run_self_test(None) (self_test is async). Response {exit_code, output}.

GET /api/v1/attack/moduleslist_attack_modules

tools/api/routes/system.py:688 — bearer. tools.attack_modules.registry.list_modules() → per-module {name, description, family, target_services, target_ports, required_cves, destructive_ics}.

GET /api/v1/goalslist_goals

tools/api/routes/system.py:713 — bearer. GoalEngine().presets{name, description, risk, compatible=engine.is_compatible(name,"standard_authorized")} (safe|gated compatible baseline; high requires high_authorized_testing).

GET /api/v1/config/schemaget_config_schema

tools/api/routes/system.py:743 — bearer. Returns {schema: CONFIG_SCHEMA} (tools/config_manager.CONFIG_SCHEMA) for typed WebUI forms.

GET /api/v1/models/livelist_live_models

tools/api/routes/system.py:753 — bearer. Provider-aware live probe:

  • ChatGPT (get_ai_provider=="chatgpt"): ChatGptProxyManager.get().ensure_running(chatgpt_cfg) off-thread (idempotent, respects auto_start and is_authenticated), then httpx.AsyncClient GET {base_url}/models 5 s. On any failure (not authenticated, proxy won't start, unreachable) returns 503 JSON {models: fallback registry or [default_model], source:"registry", error: msg} (tools/api/routes/system.py:779).
  • Ollama else: httpx GET {ollama_host}/api/tags with Authorization: Bearer OLLAMA_API_KEY if present; on success {models, source:"ollama"}; on failure 503 {models: registry.values, source:"registry", error} (tools/api/routes/system.py:835).

GET /api/v1/providersget_providers

tools/api/routes/system.py:857 — bearer. get_ai_provider + get_chatgpt_config + off-thread _chatgpt_status_sync (is_authenticated file-existence only + _health_ok). Returns {provider, chatgpt:{enabled, authenticated, proxy_running, host, port, default_model, we_started}}.

POST /api/v1/providers/chatgpt/loginchatgpt_login

tools/api/routes/system.py:881 — bearer. ChatGptProxyManager.get().run_login(chatgpt_cfg) off-thread; returns {ok, url?, reason?}. Tokens stay in ~/.codex/auth.json, never in request/response/config.

POST /api/v1/providers/chatgpt/proxy/startchatgpt_proxy_start

tools/api/routes/system.py:895 — bearer. ensure_running(chatgpt_cfg) off-thread.

POST /api/v1/providers/chatgpt/proxy/stopchatgpt_proxy_stop

tools/api/routes/system.py:903 — bearer. manager.shutdown(chatgpt_cfg) off-thread; returns {ok:true, stopped: we_started} — never stops a proxy the daemon didn't start.

GET /api/v1/skills/{name}get_skill

tools/api/routes/system.py:922 — bearer. get_registry(_CONFIG).get(name)404 if missing else {name, description, body, sections, tags, references, nist_csf, mitre_attack, domain, subdomain, version} (skill_registry_cache).

POST /api/v1/skillsinstall_skill

tools/api/routes/system.py:1023 — bearer. Body {name, markdown}. Validates name ^[a-z0-9][a-z0-9-]{1,63}$ (_SKILL_NAME_RE tools/api/routes/system.py:960), markdown non-empty. _skill_writable_root() is skills.roots[0] (_CONFIG["skills"]["roots"] default ["skills"]) resolved via _CONFIG_PATH (tools/api/routes/system.py:963), must be directory. target_dir = resolve(skill_dir) under root containment check (_resolve_skill_dir tools/api/routes/system.py:1001). Reject if exists 409, refuse plugin dirs (tools/api/routes/system.py:1012). Write tmp → SKILL.md atomic, parse_skill_file validate, enforce parsed.name==name, clear_cache(). Returns {name, description, tags}.

DELETE /api/v1/skills/{name}remove_skill

tools/api/routes/system.py:1101 — bearer. Same name validation + root resolution; 404 if missing; refuse plugin-contributed dirs 400 skill_not_writable; shutil.rmtree + clear_cache(); response {name, deleted:true}.

All system routes include bearer; error envelope is tools/api/errors.py:42 and handlers at tools/api/errors.py:71.

source: repo docs (build sync)Edit this page on GitHub →