Overview
DefenseClaw does not hold provider secrets in config.yaml. Every credential lives either in the process environment (for CI) or in ~/.defenseclaw/.env (for workstations) at mode 0600. The defenseclaw keys command group is the operator UX for inspecting, setting, and auditing these values. The registry itself (defenseclaw.credentials.CREDENTIALS) is compiled in, so keys list always reflects exactly what the current config actually needs — there is no drift between documentation and runtime.
For internal or self-hosted LLMs whose domain is not shipped in the baked-in internal/configs/providers.json, DefenseClaw also supports an additive overlay at ~/.defenseclaw/custom-providers.json managed by defenseclaw setup provider.
The defenseclaw keys commands
| Command | Purpose |
|---|---|
defenseclaw keys list | Print every credential the registry knows about with REQUIRED / OPTIONAL / NOT USED status and source (env, dotenv, or unset) |
defenseclaw keys list --json | Machine-readable equivalent; stable schema for CI gates |
defenseclaw keys list --missing-only | Only show REQUIRED-but-unset credentials |
defenseclaw keys set ENV_NAME | Prompt for a value and write it atomically to ~/.defenseclaw/.env |
defenseclaw keys set ENV_NAME --value $TOKEN | Write without prompting (non-interactive/CI) |
defenseclaw keys fill-missing | Walk every REQUIRED-but-unset credential and prompt for each; ideal for first-time setup |
defenseclaw keys check | Exit 0 if all REQUIRED are set, non-zero otherwise — use in preflight hooks |
Canonical credential patterns
| Credential | Where it's consumed | Resolution order |
|---|---|---|
OPENCLAW_GATEWAY_TOKEN | WebSocket auth with OpenClaw gateway | os.environ → ~/.defenseclaw/.env → auto-detected from ~/.openclaw/openclaw.json during init |
DEFENSECLAW_LLM_KEY | Unified LLM key (judge, local models, general fallbacks) | os.environ → .env |
CISCO_AI_DEFENSE_API_KEY | Remote scanner mode in guardrail.scanner_mode=remote|both | os.environ → .env |
DEFENSECLAW_SPLUNK_HEC_TOKEN | Splunk HEC sink | os.environ → .env |
DEFENSECLAW_PD_ROUTING_KEY | PagerDuty webhook | os.environ → .env |
DEFENSECLAW_WEBEX_TOKEN | Webex bot webhook | os.environ → .env |
DEFENSECLAW_WEBHOOK_SECRET | Generic HMAC-signed webhook | os.environ → .env |
The full list is exposed by defenseclaw keys list — every row is classified against the currently-loaded config, so a credential will appear as NOT USED when the corresponding feature is disabled (e.g. Splunk token when no HEC sink is configured).
~/.defenseclaw/.env format
# Auto-generated by defenseclaw init / keys set.
# Never check in to source control.
OPENCLAW_GATEWAY_TOKEN=op_live_7a...
DEFENSECLAW_LLM_KEY=sk-ant-api03-...
DEFENSECLAW_SPLUNK_HEC_TOKEN=01234567-89ab-cdef-...
The file is created with mode 0600 and is never read from any path other than the operator's data_dir. The gateway daemon loads it through godotenv at startup; keys set performs an atomic tmp+rename so the file is never observed in a half-written state by the running sidecar.
First-time credential setup
The most efficient path for a fresh install:
defenseclaw keys list --missing-only
defenseclaw keys fill-missing --yes
defenseclaw keys check && echo "ready"
defenseclaw-gateway restart # reload .env
fill-missing prompts only for the REQUIRED rows the current config actually needs. Skipping a prompt (empty value) leaves the credential unset and moves to the next one.
Custom provider overlay
When you run an internal or self-hosted LLM (e.g. llm.corp.example.com), the built-in provider list will not recognize it and the gateway will either block the request (strict mode) or flag it as a silent bypass in egress telemetry (default). Extend the registry in place:
defenseclaw setup provider add \
--name corp-llm \
--domain llm.corp.example.com \
--env-key CORP_LLM_TOKEN \
--profile-id corp
| Flag | Required | Effect |
|---|---|---|
--name | yes | Canonical provider name; case-insensitively merges with any existing baseline entry |
--domain | yes (repeatable) | Domains to treat as LLM traffic. URLs are accepted and scheme/path stripped. |
--env-key | no (repeatable) | API key env var names for defenseclaw keys discovery |
--profile-id | no | Matches openclaw auth-profiles.json profile ID |
--ollama-port | no (repeatable) | Extra Ollama-style loopback port |
--no-reload | no | Skip the POST /v1/config/providers/reload call to the guardrail proxy |
Writes go to ~/.defenseclaw/custom-providers.json via atomic tmp+rename with a .bak. On success the CLI calls the sidecar's reload endpoint; on failure (401, 403, connection refused) the file is still on disk — restart the sidecar for the overlay to take effect.
defenseclaw setup provider remove --name NAME deletes an overlay entry but cannot remove baked-in baseline providers. defenseclaw setup provider list prints the overlay only; defenseclaw setup provider show prints the merged view as reported by the live guardrail proxy (GET /v1/config/providers on port 4000) with an overlay-only fallback when the proxy is unreachable.
Verify it worked
defenseclaw keys list
cat ~/.defenseclaw/custom-providers.json
curl -s http://127.0.0.1:4000/v1/config/providers | jq '.providers | length'
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
MISSING in keys list but value is exported in your shell | defenseclaw-gateway inherited a different env (e.g. started by systemd) | Add to systemd unit's Environment= or write to ~/.defenseclaw/.env |
sidecar rejected reload: unauthorized | OPENCLAW_GATEWAY_TOKEN unset or stale | defenseclaw keys set OPENCLAW_GATEWAY_TOKEN and defenseclaw-gateway restart |
refusing to use DEFENSECLAW_CUSTOM_PROVIDERS_PATH | Overlay path outside allowed roots | Unset the env var or point it under ~/.defenseclaw/ |
cannot parse existing overlay | Hand-edit produced invalid JSON | Move the file aside and let setup provider add rewrite it |