Skip to content
BreachPilot

CLI Reference

Complete reference for every command-line entry point, flag, interactive option, and environment variable in BreachPilot. See docs/getting-started.md for setup and first commands.

Entry Points

There are three top-level Python entry points, one per flow (see AGENTS.md Flow A / Flow B):

CommandFlowPurpose
python main.pyFlow A (modern, what users run)WebUI daemon + browser by default (no args), direct recon/attack runs, doctor/self-test/eval/demo, API-only daemon, --menu terminal menu
python app.pyFlow AASGI app factory — not a CLI; imported by main._run_daemon, never run directly
python cli.pyFlow B (legacy, SQLite-backed)Database-backed mission/scope/task/finding/report workflow commands

app.py contains no argument parser; it is invoked as python main.py --demon which imports create_app and serves it with uvicorn (main._run_daemon).

The MCP servers are separate entry points: python mcp_server.py (defensive), python mcp_exploit_server.py (exploit, target-lock enforced), python mcp_engine_server.py (engine advisory). All accept --transport stdio|http, --config, --host, --port (mcp_exploit_server.py:203-209).

python main.py — Flow A

Argument parser: main.parse_args. Dispatch order in main(): API-key bootstrap → --setup-api-keys exit → daemon (--demon/--daemon/--web) → --doctor--self-test--eval-list--eval--ctf--demo--skills-list--list-plugins--menu (terminal menu) → no-args default (WebUI daemon, --web)async_main.

Run flags

Parser group: targeting (plus --version). The "Group" column names the argparse argument group in main.parse_args, so references survive line drift.

FlagDefaultDescriptionGroup
--versionPrint BreachPilot <version> and exit
--target <ip-or-domain>""Target to attack or recon. Accepts an IP or a domain (Phase 4); domains resolve via tools/validation_utils.resolve_target_to_ip and thread EXPLOIT_TARGET/EXPLOIT_TARGET_IP/EXPLOIT_TARGET_DOMAIN into the MCP server (AGENTS.md rule 6)targeting
--mode {recon,attack,fast}""recon = gather intel only, attack = full exploitation, fast = parallel recon preset then attack. Recon is always read_only (tools/cli_exploit_settings.py:157-163)targeting
--goal <name>""Preset goal: backdoor, initial_access, privilege_escalation, …targeting
--custom-goal <text>""Free-text goal descriptiontargeting
--config <path>config.yamlConfig file pathtargeting
--model <alias>config defaultOverride model alias (glm/kimi/deepseek/deepseek_flash/minimax)targeting
--model-strategy {default,round-robin,random,specific}defaultHow to pick model across targetstargeting
--mcp-transport {stdio,http}NoneMCP transport. Ignored on the run path: always forced to http so the target-IP lock reaches the servertargeting
--http-port <n>NoneHTTP port for the MCP server (http transport)targeting
--reports-dir <path>reportsRoot dir for run artifacts (reports/<run_id>/)targeting
--resume <run_id|session_id>""Resume a prior run by run_id or session_idoperational

Swarm / reasoning flags

FlagDescriptionGroup
--swarmMulti-agent swarm mode: six specialists decompose a single target (parallel recon + vuln research, critic pre-check, reflection). Without it, attack mode runs the persistent autonomous campaign queue (recon → exploit → privesc → lateral → validation, resume + checkpoints). Combine both on high-value targets. See docs/swarm.md ("Swarm vs Campaign")swarm & reasoning
--parallel-swarmParallel sub-agents (recon + vuln-research parallelize; exploit/post_exploit stay sequential unless swarm.exploit_parallel)swarm & reasoning
--criticCritic agent pre-approval (requires --swarm)swarm & reasoning
--reflectionReflection agent (requires --swarm)swarm & reasoning
--adaptive-exploitsAdaptive exploit generation with mutation on failureswarm & reasoning
--long-sessionRaise context window, LLM call timeout, round/command/duration budgets, and the swarm cap for multi-hour runs; checkpointed messages for crash-safe resumeswarm & reasoning
--multi-model-consult / --no-multi-model-consultAllow / forbid the agent asking configured peer models for advisory helpswarm & reasoning
--observer-mode {heuristic,llm,hybrid}Observer fact-extraction mode (default hybrid)swarm & reasoning
--recon-first / --no-recon-firstForce recon-first (scan, suggest rated goals, then ask) / skip straight to goal selectionswarm & reasoning
--ultrathinkDeep reasoning: verbose chain-of-thought and frequent reflectionswarm & reasoning

Operational flags

FlagDescriptionGroup
--doctorRun the self-check (Python, nmap, Ollama, config) and exit — tools.doctor.run_doctoroperational
--self-testRun the safe localhost smoke test against 127.0.0.1 and exit — tools.self_test.run_self_testoperational
--demoRun against a local sandbox target (DVWA via Docker on 127.0.0.1:8081, synthetic in-process HTTP server as fallback); writes reports/demo/demo_report.md (tools/demo_mode.py)operational
--yesSkip the ready-to-begin confirmation gate — use with cautionoperational
--jsonMachine-readable JSON to stdout where supported; also forces plain outputoutput
--quietWarnings/errors only; forces plain outputoutput
--debugVerbose debug output; sets AI_NMAP_DEBUG=1output
--plainDisable color output (ANSI)output
--menuForce the legacy interactive terminal menu even with other argsoutput

Eval & regression flags

FlagDescriptionGroup
--eval [TARGET ...]With target ids: run the graded eval suite (oracle v2) against those eval_targets/*.oracle.json targets. Bare --eval: all oracle targets — the graded path needs no --target and does not exit 2. With --target <ip>: the legacy single-target benchmark instead, writing reports/eval/<run_id>/ (tools/eval_harness.py:run_eval, whose exit-2 no-target check only applies to that legacy path)eval & regression
--eval-listPrint the graded-eval oracle targets (id + flag count) and exiteval & regression
--save-baselineWith --eval or --benchmark: persist the report as the regression baseline (eval.baseline_path / benchmark.baseline_path)eval & regression
--check-regressionWith --eval or --benchmark: exit 1 when score drops beyond tolerance (eval.regression_tolerance / benchmark tolerances); fails closed on a missing baselineeval & regression

Benchmark suite flags (--benchmark, tools/benchmark/)

FlagDescriptionGroup
--benchmark [SUITE ...]Run a benchmark suite (bare --benchmark = xben). Repeatable filters via --scenario/--tag, repeated trials via --trials. Results under reports/benchmarks/<suite>/<run_id>/. See docs/benchmarks.mdbenchmark suite
--benchmark-listList registered benchmark suites (id, scenario count, tags) and exitbenchmark suite
--scenario <ID>With --benchmark: restrict to specific scenario ids (repeatable)benchmark suite
--tag <TAG>With --benchmark: restrict to scenarios carrying a tag (repeatable)benchmark suite
--trials <N>With --benchmark: repeated trials per scenario (default benchmark.trials, 1-20)benchmark suite

API keys / config

FlagDescriptionGroup
--setup-api-keysPrompt for provider API keys and save them to the key file; exits after saving (when no other action is requested)api keys
--api-key-file <path>Local JSON file for saved provider API keys (default secr.json / DEFAULT_API_KEY_FILE)api keys
--no-api-key-promptSkip the interactive startup API-key promptapi keys

Startup key loading lives in tools/config_cli.py (bootstrap_startup_api_keys) and tools/api_key_store.py. The interactive prompt only fires in --menu mode; the WebUI daemon default and direct runs load keys without prompting.

Skills / plugins / CTF flags

FlagDescriptionGroup
--skills {on,off,hints,lookup}Override runtime-skills behavior: on=startup context, hints=hints only (default), lookup=MCP tools only, off=disabledruntime skills
--skills-listPrint the read-only runtime-skill catalog and exitruntime skills
--skills-include <name>Force-include a skill for this run. Repeatable (action="append")runtime skills
--skills-exclude <name>Exclude a skill for this run. Repeatableruntime skills
--no-skills-reselectDisable mid-run skill re-selectionruntime skills
--list-pluginsPrint discovered plugins (name/version/capabilities/loaded) and exitplugins
--ctfCTF autopilot: run against --target and stop when the goal is heuristically met (flag marker / uid=0 / port-marker)ctf autopilot
--ctf-flag-path <path>CTF goal: flag file path on the target (e.g. /root/flag.txt; default empty)ctf autopilot
--ctf-marker <str>CTF known-string marker expected from --ctf-portctf autopilot
--ctf-port <n>CTF port to probe for markerctf autopilot
--ctf-root-shellCTF: treat uid=0 in any output as goal-met (default False)ctf autopilot

WebUI / API daemon flags

FlagDescriptionGroup
--demon, --daemonStart the local WebUI API server instead of the terminal menu (main._run_daemon)webui
--webDaemon mode plus: build webui/dist/ if needed, serve it at /, open a browser (main._ensure_webui_build)webui
--api-host <host>Daemon bind host — loopback only (127.0.0.1/localhost/::1); any other host exits with code 2webui
--api-port <n>Daemon port (default 8765)webui

Daemon mode refuses to combine with target/goal/menu/doctor/demo/eval/self-test/skills-list/ list-plugins/setup-api-keys flags and exits 2 on conflict. The API is served by the FastAPI factory in app.py (create_app), mounted under /api/v1, bearer-token protected (BREACHPILOT_API_TOKEN env override; tools/api/auth.py).

Interactive menu (--menu)

--menu (or ui.ask_advanced_settings in an interactive session without --target) launches the questionary terminal menu (tools/interactive_menu.py):

  1. Recon & Suggest Goals — recon-first session
  2. Start New Session — interactive wizard → async_main
  3. Manage Missions — list/create/delete Flow B missions in research_workspace/research.db
  4. View Reports — browse reports/<run_id>/ sessions
  5. Settings — write a default config.yaml if missing
  6. Help — quick reference
  7. Exit

The wizard itself lives in tools/attack_ui.py: ask_advanced_settings covers all CLI flags with current values as defaults; ask_power_ups is a multi-select for swarm/critic/reflection/adaptive-exploits/long-session/ultrathink/debug/yes. When no --target is given, the flow prompts for one and, in interactive sessions, persists it to exploit.allowed_targets in the config (tools/config_cli.add_target_to_allowlist).

No-args default: python main.py with no arguments starts the WebUI daemon (--web: build webui/dist/ if needed, serve at http://127.0.0.1:8765/, open a browser). It does NOT open the terminal menu; use --menu for that.

python cli.py — Flow B (SQLite mission workflow)

Subcommands built at legacy/cli.py:530-598. All commands except init-mission accept --mission-id <id> (placed after the subcommand) to operate on a specific mission instead of the latest active one — the resume/reattach path (legacy/cli.py:533-544).

CommandFlagsDescriptionLine
init-mission--config <path> (required)Create a new mission from a YAML configcli.py:521
add-scope--allow <pattern>, --deny <pattern>, --notes <text>Add an allow or deny scope rule (domain, IP, CIDR, *.wildcard)cli.py:526
list-scope--mission-idShow all scope rules for the active missioncli.py:533
next-task--mission-idShow the next pending taskcli.py:537
list-tasks--mission-idList open (and blocked) taskscli.py:541
run-task[task_id] positional (empty = next pending), --mission-idExecute a task through scope gate → risk controller → executorcli.py:545
summarize-target--target <name>, --mission-idTarget memory summary + target graphcli.py:550
list-findings--mission-idList all findings with status iconscli.py:555
validate-findingfinding_id positional (e.g. F-00001), --mission-idRun validation, print JSON resultcli.py:559
generate-reportfinding_id positional, --mission-idGenerate a markdown report for a findingcli.py:564
status--mission-idAgent loop status: mission, risk, task counts, findingscli.py:569

Examples:

python cli.py init-mission --config mission.yaml
python cli.py add-scope --allow "*.example.com" --notes "main scope"
python cli.py add-scope --deny "payments.example.com"
python cli.py next-task
python cli.py run-task T-00001
python cli.py next-task --mission-id M-001     # resume/reattach a paused mission
python cli.py status

Data lives in research_workspace/research.db (override with RESEARCH_WORKSPACE, cli.py:39-48). Exit codes: 0 success, 1 error (including scope/risk blocks that set the task to needs_approval, cli.py:338-341), 130 on Ctrl-C (cli.py:588-590). No command → help, exit 1 (cli.py:582-584).

Exit Codes

CodeMeaningSource
0Success / clean exitthroughout
1Run failure, invalid config/target, aborted session, setup-only pathmain.main / async_main error paths; cli.py errors
2Daemon flag conflicts; non-loopback --api-host; --save-baseline/--check-regression without --evalmain._run_daemon; tools/eval_harness.py
130KeyboardInterruptmain.main; cli.py

Example Workflows

# Recon-only (read_only permission enforced — tools/cli_exploit_settings.py:157)
python main.py --target 10.0.0.50 --mode recon --goal initial_access

# Recon-first: scan, suggested rated goals, then operator picks
python main.py --target 10.0.0.50 --recon-first

# Full exploit (interactive ready-to-begin gate; --yes skips it)
python main.py --target 10.0.0.50 --mode attack --goal backdoor --yes

# Swarm mission with critic + reflection
python main.py --target 10.0.0.50 --mode attack --swarm --critic --reflection

# Long multi-hour run with checkpointed resume
python main.py --target 10.0.0.50 --mode attack --long-session

# WebUI: build, serve, and open the SPA
python main.py --web

# API daemon only (no SPA build)
python main.py --daemon --api-port 9000

# Diagnostics
python main.py --doctor
python main.py --self-test
python main.py --eval --target 10.0.0.50

# Graded eval suite (oracle v2)
python main.py --eval                # all eval_targets/ oracles, graded report
python main.py --eval dvwa juice_shop --save-baseline
python main.py --eval --check-regression   # exit 1 on score regression

# Demo against a local sandbox (Docker DVWA or synthetic server)
python main.py --demo

# Flow B mission workflow
python cli.py init-mission --config mission.yaml
python cli.py run-task; python cli.py status; python cli.py list-findings

Environment Variables

Target / allowlist (threaded into the MCP server)

VariableEffectSource
EXPLOIT_TARGETTarget IP lock for the exploit MCP server's terminal tool (tools/mcp_tools/terminal.py:31)tools/mcp_shared.py
EXPLOIT_TARGET_IP, EXPLOIT_TARGET_DOMAINResolved IP / original domain of the targetAGENTS.md rule 6
EXPLOIT_DISCOVERED_TARGETSComma-separated discovered targets unioned into the allowlist matchertools/mcp_shared.py:528-555
EXPLOIT_ALLOWED_TARGETSComma-separated operator/CI override unioned into the allowlist (set authorized targets without editing config.yaml; used by the nightly eval workflow)tools/kernel/allowlist.py
EXPLOIT_WORKSPACEWorkspace root (e.g. .kev_catalog.json path)tools/cve_lookup.py:171

API keys

VariableEffectSource
OLLAMA_API_KEYRequired for the Ollama Cloud default path; auto-attached to chat/generate requeststools/doctor.py:154; AGENTS.md rule 7
SERPAPI_API_KEYResearch web-search providertools/api_key_store.py:51
NVD_API_KEYCVE lookuptools/api_key_store.py:52
GITHUB_TOKENExploit search / CVE GitHub lookupstools/exploit_search.py:224; tools/api_key_store.py:53
SHODAN_API_KEYShodan recon (optional)tools/recon_pipeline.py:287

Keys are loaded from the --api-key-file JSON into os.environ when not already set (tools/api_key_store.py:110-118). Missing-key names come from configured_api_key_env_names (tools/api_key_store.py:33).

WebUI API

VariableEffectSource
BREACHPILOT_API_TOKENBearer token override for the API daemon (else .webui_secret_key file)app.py:69-73; tools/api/auth.py:46
BREACHPILOT_API_KEY_FILEAPI key file path used by the API routestools/api/routes/system.py:144, 181

Behavior / debug

VariableEffectSource
AI_NMAP_DEBUGVerbose nmap/exploit loop logging (--debug sets it)tools/exploit_agent/runner/_impl.py:_debug_enabled; main.py debug handling
AI_NMAP_ACTIVE_MODEL_ALIASActive model alias override for MCP registry/peer toolstools/mcp_tools/registry.py:201
AI_NMAP_MULTI_MODEL_ENABLEDForce multi-model enablement for the MCP servertools/mcp_tools/registry.py:220
AI_NMAP_AUDIT_VERIFY_VERBOSEVerbose audit verification outputtools/exploit_agent/policy.py:340
AI_NMAP_VAULT_KEYCredential-store vault key (else auto-generated)tools/credential_store.py:149
MCP_ALLOW_PUBLIC_BINDAllow MCP HTTP servers to bind non-loopbacktools/mcp_shared.py:1022
MCP_HTTP_TOKENBearer token for MCP HTTP transporttools/mcp_shared.py:1081
RESEARCH_WORKSPACEFlow B workspace root (default research_workspace)cli.py:39-43; tools/logging_setup.py:18

Windows vs Linux

  • Windows is the primary dev platform. The Makefile is Unix-only — make doctor etc. do not run on Windows; use the equivalent python main.py --doctor commands (Makefile:1-3).
  • scripts/setup-linux.sh is a one-shot Linux/macOS bootstrap: venv + requirements + external tool check (nmap, ollama, tmux, searchsploit, msfconsole, hydra, impacket) + ollama pull + python main.py --doctor (scripts/setup-linux.sh:21-54). There is no Windows equivalent.
  • Makefile targets map to: doctormain.py --doctor, self-testmain.py --self-test, evalmain.py --eval, testpytest tests/ -v, test-one F=... → focused pytest, runmain.py, mcp-defensive|exploit|engine → the three MCP servers (Makefile:22-48).
  • Linux nmap -O/-sS need root: set nmap.sudo: true (uses sudo -n) or run as root; else nmap.priv_fallback (default true) auto-downgrades. Windows attacker = Python-only exploits; Linux attacker = full Kali arsenal (searchsploit/metasploit/hydra/crackmapexec/impacket).
  • ANSI colors are auto-enabled on Windows terminals via _enable_windows_ansi (tools/attack_ui.py:122-141); --plain/--quiet/--json disable them.
  • The interactive menu renders a plain ASCII banner that works on Windows cmd (tools/interactive_menu.py:524-533); questionary fallbacks are numbered-input menus.
source: repo docs (build sync)Edit this page on GitHub →