Reference

Gateway API

defenseclaw-gateway HTTP surface — every route registered in internal/gateway/api.go, the auth + CSRF model, and the verdict shape that inspect/scan endpoints return. Authoritative source is api.go itself.

defenseclaw-gateway exposes an HTTP surface used by hook scripts, the OpenClaw plugin, the CLI, and the TUI. This page lists every route the gateway actually registers, grouped by purpose.

This is not an OpenAPI spec — request/response shapes evolve and the canonical source is the handler code. For exact field names, point at the handler in internal/gateway/api.go (which mux.HandleFunc references the implementing file).

Last audited: every entry on this page was verified against mux.HandleFunc calls in internal/gateway/api.go:Run() (lines 304-393). If you add or rename a route in code, update this page in the same PR — make docs-check will fail otherwise.

Bind address

KnobDefaultWhere set
gateway.api_port18970~/.defenseclaw/config.yaml
gateway.api_bind127.0.0.1~/.defenseclaw/config.yaml (override only when running the sandbox/lan profile)
gateway.port (proxy)18789Separate from the API server — the proxy port the LiteLLM/Bifrost upstream listens on

The gateway binds to localhost by default. To expose the API to the LAN (sandbox/multi-host setups), set gateway.api_bind: "0.0.0.0" in ~/.defenseclaw/config.yaml. defenseclaw setup does this for you when it provisions the sandbox profile.

Auth

Every request — except GET /health — must carry the gateway token. Two header forms are accepted (constant-time compared in tokenAuth):

X-DefenseClaw-Token: <token>
Authorization:        Bearer <token>

The token is resolved from one of, in priority order:

  1. The env var named by gateway.token_env (custom name — operator intent wins)
  2. DEFENSECLAW_GATEWAY_TOKEN (canonical)
  3. OPENCLAW_GATEWAY_TOKEN (legacy fallback for existing installs)
  4. gateway.token directly in ~/.defenseclaw/config.yaml

There is no standalone token file (~/.defenseclaw/gateway-token is sometimes mentioned in older docs but the gateway never reads it). Use defenseclaw keys set DEFENSECLAW_GATEWAY_TOKEN to set the canonical env var; it lands in ~/.defenseclaw/.env.

CSRF protection

Every state-changing request also requires X-DefenseClaw-Client (any non-empty value identifying the caller — cli, openclaw-plugin, inspect-hook/1.0, etc.). The middleware rejects same-origin browser requests that lack it with 403. See apiCSRFProtect in api.go.

X-DefenseClaw-Client: cli

Endpoints

Health & status

MethodPathAuthPurpose
GET/healthexemptLiveness + uptime + version. Used by k8s probes and defenseclaw doctor.
GET/statusrequiredActive connector roster + enforcement flags + legacy connector_mode + connector_modes (one entry per active connector: connector, mode, policy_mode, enforcement_surface, telemetry, proxy_intercept) + provenance. Used by defenseclaw status and the TUI.
GET/v1/connectorsrequiredLists every connector registered in the gateway (name, description, source, hook capabilities, tool inspection mode).

GET /status carries both a singular and a plural connector field so old and new clients keep working on a multi-connector install:

GET /status (excerpt)
{
  "connector_mode": "action",
  "connector_modes": [
    {
      "connector": "codex",
      "mode": "observability",
      "policy_mode": "action",
      "enforcement_surface": "agent_lifecycle_hooks",
      "telemetry": ["hook", "otel"],
      "proxy_intercept": false
    },
    {
      "connector": "claudecode",
      "mode": "observability",
      "policy_mode": "observe",
      "enforcement_surface": "agent_lifecycle_hooks",
      "telemetry": ["hook"],
      "proxy_intercept": false
    }
  ]
}
  • connector_mode (singular) is a legacy compatibility value for clients written before multi-connector existed. On a fan-out install it is not the authoritative roster view; use connector_modes instead.
  • connector_modes (plural) is the authoritative per-connector view: one entry per active connector. mode is the backward-compatible data-path value (guardrail or observability); policy_mode is the operator posture (observe or action); enforcement_surface identifies the proxy, lifecycle-hook, or OmniGent policy API path; telemetry lists active signal sources; and proxy_intercept is true only for proxy connectors. defenseclaw-gateway status renders its per-connector "Connector Mode" section directly from this array.

Connector hooks

Each connector registers exactly one hook endpoint. The path is owned by the connector, declared in its HookAPIPath() method, and registered dynamically in registerConnectorHookRoutes.

MethodPathConnectorSource
POST/api/v1/claude-code/hookClaude Codeinternal/gateway/connector/claudecode.go
POST/api/v1/codex/hookCodexinternal/gateway/connector/codex.go
POST/api/v1/cursor/hookCursorinternal/gateway/connector/hook_only.go
POST/api/v1/windsurf/hookWindsurfsame
POST/api/v1/geminicli/hookGemini CLIsame
POST/api/v1/copilot/hookGitHub Copilot CLIsame
POST/api/v1/openhands/hookOpenHandssame
POST/api/v1/antigravity/hookAntigravitysame
POST/api/v1/hermes/hookHermessame
POST/api/v1/opencode/hookOpenCodesame
POST/api/v1/omnigent/hookOmniGentinternal/gateway/connector/omnigent.go

All hook endpoints share a per-IP rate limiter (20 RPS, burst 40 — see hookLimiter in api.go). Loopback callers (the on-host hook scripts) are exempt; the limit only applies to remote callers.

Inspection (rate-limited)

The /api/v1/inspect/* family is the hot path that connector hooks and the OpenClaw plugin call to score a single tool call, prompt, or completion. Mounted under a sub-mux that wraps the per-IP rate limiter.

MethodPathUsed byPurpose
POST/api/v1/inspect/toolHook scripts, OpenClaw pluginInspect a tool call (name + args). Returns the verdict envelope below.
POST/api/v1/inspect/requestHook scripts, pluginInspect an outbound LLM request (prompt).
POST/api/v1/inspect/responseHook scripts, pluginInspect a model response.
POST/api/v1/inspect/tool-responseHook scripts, pluginInspect a tool's output before it returns to the agent.

Code & network scanning

MethodPathPurpose
POST/api/v1/scan/codeCodeGuard scan of a code blob — used by the codeguard skill and the IDE plugin to lint with the rule pack's code-scanning rules.
POST/api/v1/network-egressEgress decision for an outbound HTTP/DNS lookup (used by the OpenShell sandbox profile).

Asset scanning

MethodPathPurpose
POST/v1/skill/scanRun the skill scanner against a fetched skill bundle.
POST/v1/plugin/scanRun the plugin scanner against a plugin manifest + sources.
POST/v1/mcp/scanRun the MCP scanner against an MCP server's mcpServers.json.
POST/v1/skill/fetchFetch a skill from a registry source so it can be scanned before install.

Asset enable / disable

MethodPathPurpose
POST/skill/disableQuarantine a skill by key.
POST/skill/enableRe-enable a previously disabled / quarantined skill.
POST/plugin/disableQuarantine a plugin by name.
POST/plugin/enableRe-enable a plugin.

Inventory

MethodPathPurpose
GET/skillsList discovered skills with admission state.
GET/mcpsList discovered MCP servers with admission state.
GET/tools/catalogAggregate tool catalogue across the active connector roster.

Policy

MethodPathPurpose
POST/policy/evaluateEvaluate the OPA admission policy against an arbitrary input. Used internally by the scanners and exposed for debugging.
POST/policy/evaluate/firewallEvaluate the firewall sub-policy in isolation.
POST/policy/evaluate/auditEvaluate the audit sub-policy.
POST/policy/evaluate/skill-actionsEvaluate the skill_actions sub-policy by severity.
POST/policy/reloadReload the OPA policy bundle (policies/<name>.yaml + policies/rego/) without restarting the gateway. Returns 200 + the active policy name.

Guardrail control plane

MethodPathPurpose
POST/v1/guardrail/eventSubmit a guardrail event (used by the LiteLLM bridge for upstream-triggered events).
POST/v1/guardrail/evaluateEvaluate the guardrail rule pack against arbitrary content.
GET/v1/guardrail/configReturn the active guardrail config snapshot.
PATCH/v1/guardrail/configPatch supported guardrail runtime fields into ~/.defenseclaw/config.yaml, validate the full YAML file, and let the config watcher/reconciler apply the latest config. Body is JSON, not raw YAML. Supported fields: mode, scanner_mode, block_message, connector, hilt_enabled, hilt_min_severity.

Changing connector uses restart semantics so listener and hook state are rebuilt; it is not accepted as an in-place connector swap.

Enforcement logging

These endpoints are write-only "I just blocked / allowed something" telemetry endpoints used by the OpenClaw plugin to keep the gateway's audit DB authoritative when the plugin makes the decision out-of-band.

MethodPathPurpose
POST/enforce/blockLog an enforcement-time block.
POST/enforce/allowLog an enforcement-time allow.
POST/enforce/blockedList recent enforcement-time blocks.
POST/enforce/allowedList recent enforcement-time allows.

Audit

MethodPathPurpose
POST/audit/eventAppend a single audit-event JSON to the SQLite store. Used by hook scripts and plugin SDKs.

The gateway has no streaming or query HTTP endpoint — read history through the CLI:

defenseclaw-gateway audit export --output audit.jsonl     # JSONL of audit_events
tail -f ~/.defenseclaw/gateway.jsonl | jq                  # Live tail of the JSONL fallback
defenseclaw alerts                                          # Recent alerts (paginated)
defenseclaw tui                                             # Live dashboard

Audit export includes the legacy details text and, when present, first-class structured JSON from audit_events.structured_json. Connector hook rows use schema: "defenseclaw.hook.v1"; older parsers can still read the mirrored details_json= token inside details.

Config

MethodPathPurpose
POST/config/patchApply a JSON-Patch-style update to a single config key (used by the TUI's quick-edit panel). The gateway watches config.yaml and reconciles valid changes; set gateway.config_reload.mode: restart when a deployment should use a fresh process for substantive config edits.

AI usage / discovery (AIBOM)

The continuous AI Discovery surface — see AI Discovery for the operator workflow.

MethodPathPurpose
POST/api/v1/agents/discoveryReceive an agent-discovery report from the on-host scanner.
GET/api/v1/ai-usageSnapshot of detected AI components by ecosystem.
POST/api/v1/ai-usage/scanTrigger an on-demand AI usage scan.
GET/api/v1/ai-usage/discoveryDiscovered agents, models, tools.
GET/api/v1/ai-usage/componentsAggregated components across the active workspace.
GET/api/v1/ai-usage/components/{ecosystem}/{name}/locationsWhere the component was detected.
GET/api/v1/ai-usage/components/{ecosystem}/{name}/historyDetection history for one component.
GET/api/v1/ai-usage/confidence/policyShow the active confidence-scoring policy.
POST/api/v1/ai-usage/confidence/policy/validateDry-run validation of a candidate policy file.

Codex bridge

MethodPathPurpose
POST/api/v1/codex/notifyCodex agent-turn-complete notifier. The Codex notify-bridge.sh shim posts each turn's JSON arg here so the gateway can audit turn counts and completion reasons.

OTLP receiver

The gateway accepts OTLP-HTTP from connectors (Codex, Gemini CLI native OTLP) and from the local-observability stack. Body is OTLP-JSON.

MethodPathPurpose
POST/v1/logsIngest OTLP logs.
POST/v1/metricsIngest OTLP metrics.
POST/v1/tracesIngest OTLP traces.
POST/otlp/{token}/...Same three signals, but the token rides in the URL path instead of a header. Useful for OTLP exporters (Gemini CLI's settings.json) that can't set arbitrary HTTP headers.

See internal/gateway/otel_ingest.go for the parsing details and Local observability for the operator setup.

Verdict envelope

The four /api/v1/inspect/* endpoints return a ToolInspectVerdict (or its near-twin for response-side inspection):

{
  "action":     "block | confirm | alert | allow",
  "raw_action": "block",
  "severity":   "CRITICAL | HIGH | MEDIUM | LOW | INFO",
  "confidence": 0.93,
  "reason":     "matched: CMD-RM-RF:Recursive force delete from critical root path",
  "findings":   ["CMD-RM-RF:Recursive force delete from critical root path"],
  "detailed_findings": [
    {
      "rule_id":    "CMD-RM-RF",
      "title":      "Recursive force delete from critical root path",
      "severity":   "CRITICAL",
      "confidence": 0.95,
      "evidence":   "rm -rf /",
      "tags":       ["destructive", "shell"]
    }
  ],
  "mode":       "action | observe",
  "would_block": true,
  "approval_timeout_ms": 0
}

Notable behaviours:

  • action is the effective verdict, not the raw policy decision. applyMode() downgrades block / confirm / alert to allow when the gateway is in observe mode, but preserves the raw verdict in raw_action and the would-have-blocked signal in would_block. This is how observe-mode dry-runs work without touching the rule pack.
  • confirm is surface-dependent. A native OpenClaw tool-approval caller can receive confirm and resolve it in the plugin. A non-native caller cannot safely pause, so the inspect handler fails the confirmation closed to block. Proxy-lane confirmations may be resolved by the in-process HILTApprovalManager; unsupported proxy surfaces degrade to alert. There is no /v1/hilt/* HTTP API.
  • evaluation_id is the runtime join key in telemetry. Inspect calls stamp it on structured JSONL and audit/scan rows, but the current ToolInspectVerdict HTTP schema does not expose evaluation_id or rule_ids. Connector-hook responses do expose both fields, as shown below. See Observability §1.4 for the per-surface contract.
  • detailed_findings and findings are both populated for backward compatibility. Existing clients that only read findings: []string are unaffected by the new structured field.

Other endpoints return their own shapes — /v1/skill/scan returns a scanner-specific envelope, /audit/event returns {"status": "stored", "id": "..."}, /skill/disable returns {"status": "disabled", "skillKey": "..."}. Always confirm against the handler code before assuming a shape.

Connector hook responses

Every connector hook response (/api/v1/{claude-code,codex,cursor,...}/hook) carries evaluation_id and the top rule_ids so clients can join it to the audit and scan rows:

{
  "action":        "allow | block | deny | …",
  "reason":        "rule X fired",
  "findings":      ["rule.id.one", "rule.id.two"],
  "evaluation_id": "eval-c1d0…",
  "rule_ids":      ["rule.id.one", "rule.id.two"]
}

Connector-hook responses expose the string findings list, not the inspect endpoint's detailed_findings objects. For /api/v1/inspect/*, evidence is redacted by default; X-DefenseClaw-Reveal-PII: 1 requests raw evidence and creates an audited reveal event. evaluation_id and rule_ids are populated when the hook evaluation produced correlation data.

Headers

HeaderDirectionPurpose
X-DefenseClaw-Token (or Authorization: Bearer ...)requestGateway auth. Required on every endpoint except GET /health.
X-DefenseClaw-ClientrequestCSRF marker — any non-empty caller identifier (cli, openclaw-plugin, inspect-hook/1.0). Required on every state-changing endpoint.
X-DefenseClaw-Request-Idrequest (optional)Caller-supplied correlation id. The gateway also accepts the more common X-Request-Id and X-Correlation-Id for compatibility with OpenTelemetry / Envoy / Microsoft / New Relic instrumentation. If unset, the gateway mints one. See requestctx.go.