Multi-connector

One DefenseClaw gateway enforces guardrail policy for several hook connectors at once, each with its own mode, fail mode, HITL, block message and rule pack under guardrail.connectors. Add a connector with setup <connector> and choose Add.

A single DefenseClaw gateway can enforce guardrail policy for several hook connectors at once — Codex, Claude Code, Antigravity, and the other hook-based agents — each with its own independent posture. This is the multi-connector model. It replaces the older "one active connector at a time" mental model: you no longer have to switch wiring to protect a second agent.

One gateway governing Claude Code and Antigravity at once. These examples are tool calls, so each reaches its connector's documented synchronous PreToolUse gate: a benign `echo` flows through, a HIGH-severity `chmod 777` finding can request native confirmation, and a CRITICAL credential-read finding can return deny. Antigravity ask/deny authority does not extend to its other four lifecycle events. Every verdict is attributed per connector in the Splunk pane. The demo closes by deleting each agent's hook config and watching DefenseClaw's self-heal detect the tamper and reinstall the hooks automatically (connector-hook-tampered → connector-hook-repaired).

Multi-connector is a hook-connector feature. The proxy-backed connectors (OpenClaw, ZeptoClaw) cannot be multi peers — they bind a proxy listener and own the agent traffic plane, so only one runs at a time. Hook connectors post structured events to the gateway and never contend for a listener, which is what lets the gateway fan out to N of them.

How one gateway enforces N connectors

Each connector you add gets an entry under guardrail.connectors.<name> in ~/.defenseclaw/config.yaml. The gateway resolves policy per connector: when a hook event arrives tagged with its connector source, the gateway looks up that connector's override block and falls back to the global guardrail.* values for anything the block leaves unset.

~/.defenseclaw/config.yaml (two connectors)
claw:
  mode: claudecode             # one primary connector, kept for backward compatibility

guardrail:
  enabled: true
  mode: action                 # global default — inherited by any connector that doesn't override
  hook_fail_mode: closed
  rule_pack_dir: ~/.defenseclaw/policies/guardrail/default

  connectors:
    codex:
      mode: action             # Codex enforces
      hook_fail_mode: open         # explicit softer override for Codex
    claudecode:
      mode: observe            # Claude Code only logs
      hilt:
        enabled: true
        min_severity: HIGH

Every field under a connector is optional. An unset field inherits the global value, so a connector block only needs to carry what differs.

claw.mode keeps one primary connector

claw.mode and guardrail.connector name the active connector on a single-connector install. With more than one connector they keep naming one primary connector for backward compatibility: the existing one when you add a connector, otherwise the first name in sorted order. Read the full roster from guardrail.connectors, defenseclaw status or defenseclaw guardrail status.

In telemetry, the gateway leaves out the process-level defenseclaw.claw.mode resource attribute while more than one connector is active. Dashboards and SIEM rules should use the per-event connector metric label or the defenseclaw.connector.source span attribute instead. See Configuration → guardrail.connectors for the full schema.

Adding a connector (Add vs Replace)

Run the hook setup for the connector you want to add:

defenseclaw setup codex

If a different connector is already configured, setup asks whether to Add or Replace. This is the prompt from a real run of defenseclaw setup codex on a host that already guarded Claude Code:

defenseclaw setup codex
  DefenseClaw is already configured for: claudecode
  You are setting up: codex
    [a] Add     — run it alongside the existing connector(s) (multi-connector)
    [r] Replace — switch to only this connector
    [c] Cancel  — make no changes
  Choose (a, r, c) [a]: a
  ✓ Codex: version codex-cli 0.154.0 is supported by codex-hooks-v4.
  ✓ Config saved to ~/.defenseclaw/config.yaml
  ✓ Desired roster staged for 'codex' — 2 connectors selected: claudecode, codex
  ✓ codex mode=observe
  ℹ Saved. It takes effect once the gateway restarts and confirms the connector (defenseclaw-gateway restart).

Add

Keep the existing connectors wired and add the new one. The gateway enforces all of them. This is the default, also with --yes.

Replace

Tear down the previous connector's wiring and make the new one the only connector. Non-interactively, pass --replace.

Choose Add to build a multi-connector install. Each added connector starts from the global guardrail defaults; tune it afterward with the per-connector CLI (below).

Stand up several at once with defenseclaw init

The Add/Replace flow above is the incremental path — layer in one connector at a time. To stand up a multi-connector install in a single pass, use defenseclaw init: it auto-detects every installed hook connector, configures them all in observe, and asks you to name the action-mode subset in one question. Everything you have lands under guardrail.connectors.<name> in one go; tune individual connectors afterward with the per-connector CLI below.

For scripted or non-interactive setups, the same fan-out is available through two init-only flags — --observe-all (every detected hook connector in observe) and --action-connectors codex,claudecode (the named subset in action, composable with --observe-all):

defenseclaw init --non-interactive --yes --observe-all --action-connectors codex

init only fans out to hook connectors. Proxy connectors (OpenClaw, ZeptoClaw) are detected but excluded from the observe-all set — they can't be multi peers — and init points you at defenseclaw setup <proxy> to configure them on their own.

Per-connector overrides

The fields you can override per connector mirror the global guardrail knobs:

FieldMeaningInherits when unset
enabledPer-connector on/off switch (guardrail disable --connector X). Drops it from the active set and removes its hooks; re-enable restores it with no re-prompt.enabled
modeobserve (log only) or action (enforce).global guardrail.mode
hook_fail_modeopen / closed — what to do when hook delivery/authentication fails or the gateway response is invalid.global guardrail.hook_fail_mode
hilt{ enabled, min_severity } — per-connector human-in-the-loop policy.global guardrail.hilt
block_messageCustom block text shown to this connector's agent.global guardrail.block_message
rule_pack_dirPath to the rule pack this connector scans against.global guardrail.rule_pack_dir
block_atLowest severity that blocks: CRITICAL, HIGH, MEDIUM or LOW.global guardrail.block_at, then the rule pack's level
alert_atLowest severity that alerts, same values; never above the block level.global guardrail.alert_at, then the rule pack's level

Set any of these with the per-connector CLI:

defenseclaw guardrail fail-mode closed --connector codex
defenseclaw guardrail hilt on --min-severity HIGH --connector claudecode
defenseclaw guardrail block-message "Codex policy blocked this" --connector codex
defenseclaw guardrail disable --connector codex      # scoped kill switch
defenseclaw guardrail enable  --connector codex      # restore it

--connector X is the explicit scope selector: it writes only that connector's override. On a multi-connector install, unscoped writes are the broad operator-intent path. guardrail fail-mode, guardrail hilt, and guardrail block-message reconcile the active connector posture so an existing override does not silently keep the old value. block-message also updates the shared guardrail.block_message fallback. On a single-connector install, --connector is rejected because there is only one posture to set.

To read the resolved posture, run bare defenseclaw guardrail status — it prints one per-connector block for every active connector. Pass --connector X to narrow the status view to one active connector:

defenseclaw guardrail status     # per-connector blocks for ALL active connectors
defenseclaw guardrail status --connector codex

With Claude Code in action mode with the judge on and Codex in observe mode, the output looks like this:

defenseclaw guardrail status
  Guardrail status
  ----------------
  • enabled:    yes
      - Claude Code
          key:         claudecode
          state:       enabled
          mode:        action
          fail:        open
          rule-pack:   default
          block/alert: CRITICAL/MEDIUM+
          hilt:        off
          scan:        regex_judge
          judge:       on
      - Codex
          key:         codex
          state:       enabled
          mode:        observe
          fail:        open
          rule-pack:   default
          block/alert: CRITICAL/MEDIUM+
          hilt:        off
          scan:        regex_only
          judge:       off
  • fail = invalid, unauthorized, incomplete, or unreachable gateway responses

  Disable with:  defenseclaw guardrail disable

See Reference → CLI for the full command group.

CLI contract: read vs write

Once a gateway protects more than one connector, the CLI splits cleanly into commands that read across the whole roster, commands that write broad state by default, and commands that write to one connector when you pass --connector:

CommandsScopeHow to narrow
defenseclaw status, defenseclaw doctor, bare defenseclaw guardrail status, bare guardrail fail-mode / hilt / block-message, skill list, mcp list, plugin list, tool list, tool status, codeguard statusRead — fans out to every active connector. Identical layout whether one or N are wired.guardrail status --connector X, skill list --connector X, mcp list --connector X, plugin list --connector X, tool list --connector X, and tool status --connector X narrow where supported.
alertsRead — recent decisions across connectors by default.alerts --connector X filters to events attributed to one connector.
guardrail enable / disable / fail-mode / hilt / block-message (with an argument)Guardrail write — scoped or broad. --connector X writes one connector override. Omit --connector for the broad path: enable/disable is global; fail-mode, HITL, and block-message update active connector posture.--connector X to target a specific connector.
skill, mcp, plugin, and tool policy verbs (block, allow, unblock, plus runtime verbs such as disable / enable where supported)Asset-policy write — broad fallback by default, connector-scoped when requested. Bare writes apply across matching configured connector copies or the unscoped fallback tier for that asset family.--connector X writes or clears only that connector's scoped state.
mcp set / mcp unset, skill install, plugin remove, codeguard install / install-skillConfig/install write — all configured/active connectors by default.--connector X writes or removes only one connector's source.
skill scan / mcp scan / plugin scan / aibom scanScan — configured/active connectors by default.Positional target and --connector X narrow where supported.

defenseclaw doctor and defenseclaw status both render a per-connector block for each active connector when more than one is wired — the same fan-out the rest of the read commands use. There is no single "active connector" summary to misread.

The firewall stays global

Per-connector policy is a guardrail concept. The egress firewall is not per-connector: DefenseClaw compiles one firewall.yaml into the host-wide pf/iptables packet filter, and merges each active connector's allowed hosts into that destination allowlist at boot. Those rules see network-level attributes, not DefenseClaw's semantic connector label, so they cannot safely attribute a connection to one of several agents sharing the host. A true per-connector egress boundary would require a separately attributable network context, such as a dedicated namespace; the normal multi-hook topology does not create one.

See also