AI providers
One contract. Any backend. You choose where inference runs.
Every chat and generate call goes through the ModelClient contract and a provider registry. Engine code never talks to a vendor SDK. Swap Ollama, OpenCode Go, or ChatGPT in config and your runs, memory, and reports keep working unchanged.
Authorized testing only. Only run BreachPilot against systems you own or have explicit written permission to assess.
3 backends · one contract
Provider catalog
Pick the backend that fits your lab. Each card shows the config keys it reads and where its secret lives: always the environment, never the config file.
- Default · Local or Cloud
ollama
Ollama / Ollama Cloud
Default chat/generate path: a cloud endpoint with bearer key, or a local daemon. Handles context-window translation and the model catalog sync.
Local-first runs and air-gapped labs
- Local daemon or Cloud endpoint
- Context-window translation built in
- Model catalog sync
Config
ollama.host · ollama.model glm-5.2:cloud · ollama.embed_host
Auth · env only
OLLAMA_API_KEY (environment only, never in config)
- Hosted · OpenAI-compatible
opencode_go
OpenCode Go
OpenAI Responses API over HTTPS. Model discovery from the provider's /models endpoint with caching.
Strong reasoning over HTTPS
- OpenAI Responses API
- Automatic model discovery
- Cached /models listing
Config
providers.opencode_go · base_url https://opencode.ai/zen/go/v1
Auth · env only
OPENCODE_GO_API_KEY (environment only)
- Opt-in · Browser OAuth
chatgpt
ChatGPT
Opt-in route via the vendored oauth/ loopback proxy, backed by browser OAuth. Tokens stay in the OAuth store.
Reusing an existing ChatGPT plan
- Loopback OAuth proxy
- Tokens stay in OAuth store
- Existence-checked, never inlined
Config
models.provider: chatgpt · loopback 127.0.0.1:10531
Auth · env only
~/.codex/auth.json (existence-checked only, never read into config)
Architecture
Your run never touches a vendor SDK
Every backend behavior lives inside its adapter. The planner, swarm, and memory layers only ever see one stable interface. A provider swap is a config change, not a code change.
Step 1
Run asks for a role
Planning, exploit drafting, peer consult, or titling requests a named role, not a vendor.
Step 2
Router resolves the client
The model router maps each alias to a registered ModelClient built from your config.
Step 3
Adapter translates
The adapter handles context windows, endpoints, and model discovery for its backend.
Role-based routing
Run planning on your strongest model, titling on the cheapest, and peer consult on a different family for a second opinion. Consulted models receive no tool schemas and cannot execute anything.
Embeddings stay local and optional
Semantic memory and skill matching embed over a local model on your own host by default. Disable embeddings entirely to fall back to keyword storage, with zero network traffic and zero secret reads.
Routing
Give each job the right brain
One provider for everything works. Splitting roles across models works better, and peer consult stays advisory either way.
| Role | What it does | Tip |
|---|---|---|
| Planning | Breaks the authorized objective into scoped steps | Benefits from the strongest reasoning model |
| Exploitation | Selects capabilities and drafts payloads for targets the operator approved | Benefits from precise instruction following |
| Peer consult | Second opinion on findings and strategy | Advisory only, no tools, no execution |
| Titling | Names runs, steps, and report sections | A small fast model is plenty |
Secrets
Secrets live in the environment. Full stop.
Config files are shareable, diffable, and committable, so they must never contain a key. Each provider declares which variable it reads, and BreachPilot refuses to accept secrets any other way.
The rule, on every provider
- Ollama / Ollama Cloud: OLLAMA_API_KEY (environment only, never in config)
- OpenCode Go: OPENCODE_GO_API_KEY (environment only)
- ChatGPT: ~/.codex/auth.json (existence-checked only, never read into config)
If a key ever lands in a config file, log, or shared snippet by mistake, rotate it. Treat the config as public.
models:
provider: opencode_go # ollama | opencode_go | chatgpt
providers:
opencode_go:
api_key_env: OPENCODE_GO_API_KEY # env-only, never in config- Export the key in your shell. Never paste it into config.yaml.
- Set models.provider to the backend you want for this lab.
- Restart the run and confirm the router resolves each role.
Full reference: providers · provider development · GitHub
Start local, scale to hosted when you need it
Begin on a local backend for authorized lab work, then point the same contract at a hosted model for harder targets. Always within scope, always operator supervised.