---
title: Configuration
description: ~/.defenseclaw/config.yaml schema, environment variables, on-disk layout, and per-connector source-of-truth files. The single source of truth for "where does this setting live?"
keywords:
  - DefenseClaw configuration
  - DefenseClaw config.yaml
  - DefenseClaw environment variables
  - DefenseClaw on-disk layout
  - DEFENSECLAW_LLM_KEY
---

`~/.defenseclaw/config.yaml` is the single source of truth for operator-owned configuration. Most fields are managed by `defenseclaw setup *` commands; you can also hand-edit, then run `defenseclaw-gateway restart` (or `defenseclaw setup guardrail --restart`). Note `restart` is on the **Go sidecar binary** — the Python `defenseclaw` CLI has no top-level `gateway` group.

## On-disk layout

<Files>
  <Folder name="~/.defenseclaw" defaultOpen>
    <File name="config.yaml" />
    <File name="audit.db" />
    <File name="gateway-token" />
    <File name="picked_connector" />
    <Folder name=".venv">
      <File name="…" />
    </Folder>
    <Folder name="hooks">
      <File name="<connector>-hook.sh" />
    </Folder>
    <Folder name="policies" defaultOpen>
      <Folder name="guardrail">
        <Folder name="default">
          <File name="shell.yaml" />
          <File name="secrets.yaml" />
          <File name="prompt-injection.yaml" />
        </Folder>
        <Folder name="strict" />
        <Folder name="permissive" />
      </Folder>
      <Folder name="codeguard" />
    </Folder>
    <Folder name="backups">
      <File name="<connector>-<timestamp>.json" />
    </Folder>
  </Folder>
</Files>

## config.yaml schema

The shape below mirrors the dataclasses in `cli/defenseclaw/config.py` and the matching Go structs in `internal/config/`. Every block is optional — `defenseclaw setup *` writes only the fields it needs and the merge keeps your hand-edits intact.

```yaml
claw:
  mode: claudecode             # active connector; pinned by setup aliases

gateway:
  host: 127.0.0.1              # gateway proxy bind host
  port: 18789                  # gateway proxy port (sidecar -> upstream LLM traffic)
  api_port: 18970              # gateway REST API port (hooks + TUI dial this)
  api_bind: ""                 # optional override for the REST API bind address

# Top-level OTel exporter — wires the sidecar to a collector. Written by
# `setup local-observability up` or `setup observability add <preset>`.
otel:
  enabled: false
  protocol: grpc               # grpc | http/protobuf
  endpoint: ""                 # 127.0.0.1:4317 after local-observability up
  service_name: defenseclaw
  signals: [traces, metrics, logs]

guardrail:
  enabled: true
  connector: claudecode        # actively enforced connector
  mode: action                 # observe | action
  scanner_mode: local          # local | remote | both
  rule_pack_dir: ~/.defenseclaw/policies/guardrail/default  # path; --rule-pack picks the bundled profile dir
  port: 4000                   # guardrail proxy port
  block_message: ""            # custom; empty -> default
  detection_strategy: regex_only  # regex_only | regex_judge | judge_first

  cisco:
    endpoint: ""
    api_key_env: CISCO_AI_DEFENSE_API_KEY
    timeout_ms: 5000

  judge:
    enabled: false
    model: anthropic/claude-sonnet-4-20250514
    api_base: ""
    api_key_env: DEFENSECLAW_LLM_KEY

  hilt:
    enabled: false
    min_severity: HIGH         # stored uppercase; CLI accepts high|medium|low|critical

# Top-level redaction switches; persistent kill-switches are deliberately
# top-level so multi-tenant operators can audit them without grepping the
# guardrail block.
privacy:
  disable_redaction: false     # true bypasses redaction on every sink

# Audit fan-out is a top-level list; each entry is a kind-tagged sink.
# `setup observability add <preset>` and `setup splunk` both write here.
audit_sinks:
  - name: org-splunk
    kind: splunk_hec
    enabled: true
    endpoint: https://splunk.example.com/services/collector
    splunk_hec:
      token_env: SPLUNK_ACCESS_TOKEN
      index: defenseclaw
  - name: local-otlp-logs
    kind: otlp_logs
    enabled: true
    endpoint: 127.0.0.1:4317
    otlp_logs:
      protocol: grpc
      insecure: true

# Notifier webhooks (Slack / PagerDuty / Webex / generic). Distinct from
# audit sinks — these are managed by `defenseclaw setup webhook`.
webhooks:
  - name: oncall-slack
    type: slack
    enabled: true
    url: https://hooks.slack.com/services/T000/B000/XXX
    secret_env: ""             # optional HMAC for `type: generic`
    min_severity: HIGH
```

## Environment variables

A handful of high-traffic env vars are inlined below. The full inventory — every variable the CLI and gateway read, with file:line references — lives on [Reference → Environment variables](/docs/reference/env-vars).

<TypeTable
  type={{
    "DEFENSECLAW_HOME": {
      type: "path",
      description: "Override the data directory (default ~/.defenseclaw). Affects config.yaml, .env, audit.db, gateway.jsonl, hook scripts, and the registries cache.",
    },
    "DEFENSECLAW_LLM_KEY": {
      type: "string",
      description: "Unified LLM key. Used by the guardrail upstream, the judge, and any scanner that calls an LLM.",
    },
    "CISCO_AI_DEFENSE_API_KEY": {
      type: "string",
      description: "Cisco AI Defense API key. Required when guardrail.scanner_mode is remote or both.",
    },
    "DEFENSECLAW_STRICT_AVAILABILITY": {
      type: "0|1",
      description: "When 1, transport-layer hook failures (gateway unreachable / 5xx) block the tool. Default is fail-open on transport.",
    },
    "DEFENSECLAW_FAIL_MODE": {
      type: "open|closed",
      description: "Per-shell override of guardrail.hook_fail_mode for response-layer failures. Read by every connector hook script.",
    },
    "DEFENSECLAW_DISABLE_REDACTION": {
      type: "0|1",
      description: "When 1, every persistent sink (audit DB, OTel logs, webhooks, Splunk HEC) emits raw values. The strongest opt-out; mirrors privacy.disable_redaction in config.",
    },
    "SPLUNK_ACCESS_TOKEN": {
      type: "string",
      description: "Splunk HEC token. Default consumer for any audit_sinks entry whose splunk_hec.token_env points at it.",
    },
  }}
/>

There is **no** `DEFENSECLAW_GATEWAY_BIND`, `DEFENSECLAW_DATA_DIR`, or `DEFENSECLAW_LOG_LEVEL` env var today. Use `DEFENSECLAW_HOME` to relocate the data directory, edit `gateway.host` / `gateway.port` / `gateway.api_port` in `config.yaml` to change bind addresses, and set log verbosity through the sidecar's `--log-level` flag (see `defenseclaw-gateway start --help`).

## Per-connector source-of-truth files

| Connector | File DefenseClaw mutates |
| --- | --- |
| OpenClaw | `~/.openclaw/openclaw.json`, `~/.openclaw/extensions/defenseclaw/` |
| ZeptoClaw | `~/.zeptoclaw/config.json` |
| Claude Code | `~/.claude/settings.json` |
| Codex | `~/.codex/config.toml` |
| Cursor | `~/.cursor/hooks.json` |
| Windsurf | `~/.codeium/windsurf/hooks.json` |
| Gemini CLI | `~/.gemini/settings.json` |
| GitHub Copilot CLI | `<workspace>/.github/hooks/defenseclaw.json` |
| Hermes | `~/.hermes/config.yaml` |

A hash-checked backup of each file is stored in `~/.defenseclaw/backups/` before any mutation. Teardown (or `--disable`) restores the backup byte-for-byte; if the file has drifted since the backup, only DefenseClaw-owned entries are surgically removed.

## Reload without restart

```bash
defenseclaw-gateway policy reload
```

Tells the running sidecar to re-read OPA policies from disk without bouncing the daemon. Connector wiring (hook scripts, agent files) is not re-applied — use `defenseclaw setup guardrail --restart` for that. Note this is on the **Go sidecar binary** (`defenseclaw-gateway`), not the Python CLI; the Python CLI has no top-level `gateway` group.
