Reference

Gateway API

The defenseclaw-gateway sidecar API — its registered route patterns, authentication and CSRF model, and inspection verdict shape. The handler code is authoritative.

defenseclaw-gateway exposes a sidecar management API used by hook scripts, connector plugins, the CLI, and the TUI. This page lists every static route pattern registered by APIServer.Run, the two provider routes installed by registerProviderRoutes, and every built-in connector hook route.

This is not an OpenAPI specification. The canonical request and response shapes are the handlers in internal/gateway/api.go and the files referenced from its mux.HandleFunc registrations.

This page covers the sidecar API on gateway.api_port. The separate guardrail proxy listener on guardrail.port has its own OpenAI-compatible and provider routes in internal/gateway/proxy.go.

Bind address

KnobConfig defaultEffective value
gateway.api_port18970Sidecar API port
gateway.api_bindempty127.0.0.1 when empty, except for the standalone OpenShell rule below

The API binds to loopback by default. In standalone OpenShell mode only, an empty gateway.api_bind inherits guardrail.host when that value is non-empty and is not the literal localhost; set gateway.api_bind: 127.0.0.1 explicitly when the management API must remain loopback-only. A non-loopback value expands access to the authenticated management surface; protect that listener with host firewalling and a deployment-specific network design. gateway.host and gateway.port describe the upstream agent gateway, while guardrail.host/guardrail.port control the separate guardrail proxy listener.

Auth

Every request except GET /health must authenticate. The shared authentication middleware accepts these header forms; all three are constant-time compared with the resolved gateway token:

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

PATCH /v1/guardrail/config performs a second, endpoint-local check and accepts only Authorization: Bearer or X-DefenseClaw-Token; X-DC-Auth alone is rejected there. The endpoint also rejects every patch in managed_enterprise deployment mode.

Loopback connector hook routes can instead use the matching connector's narrow hook token. The Codex token also authorizes /api/v1/codex/notify. A connector-scoped token on a generic /api/v1/inspect/* route is accepted only over loopback and only when X-DefenseClaw-Connector names the same registered connector.

OTLP credentials are source-specific only for the closed scope set geminicli, codex, and claudecode. Codex and Claude Code send their scoped bearer to /v1/{logs,metrics,traces} together with X-DefenseClaw-Source: codex|claudecode; that header selects and must match the scoped credential. Gemini CLI uses /otlp/geminicli/{token}/v1/{signal} because its exporter cannot add the authorization header. Once a scoped token exists for one of those sources, the master gateway token is rejected for that source's scoped route. Other native OTLP sources, including Copilot and OmniGent, currently authenticate their loopback /v1/{signal} requests with the master gateway 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 ~/.defenseclaw/gateway-token file. On first boot the sidecar creates DEFENSECLAW_GATEWAY_TOKEN in ~/.defenseclaw/.env when neither a supported environment nor dotenv source already supplies a token. Rotate a locally managed token with defenseclaw setup rotate-token --yes; that transaction restarts the gateway and refreshes active connector credentials. An externally managed custom gateway.token_env must be rotated by its owning secret system.

CSRF protection

Every non-GET/HEAD request also requires X-DefenseClaw-Client (any non-empty caller identifier such as cli, openclaw-plugin, or inspect-hook/1.0). POST, PUT, PATCH, and DELETE requests require Content-Type: application/json; OTLP requests instead require application/json or application/x-protobuf. OPTIONS requires the client header but no content type. The only narrow client-header exception is a loopback scoped-token OTLP path, because some native exporters cannot add arbitrary headers; that path still requires an OTLP content type and rejects a non-local browser origin.

All ordinary mutations also reject a non-local Origin; cross-site requests are rejected when Sec-Fetch-Site identifies them. A shared middleware caps ordinary POST, PUT, and PATCH bodies at 1 MiB. Exact authenticated OTLP-HTTP ingest routes use a separate 64 MiB receiver limit so exporter batches can follow the protocol's recommended bound without increasing the limit for the rest of the API.

X-DefenseClaw-Client: cli

Endpoints

Health & status

MethodPathAuthPurpose
GET/healthexemptLiveness + uptime + version. Used by k8s probes and defenseclaw doctor.
GET/statusrequiredHealth, process identity, provenance, an optional connected-gateway hello, legacy singular connector_mode, and connector_modes (one entry per active connector). 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 (connector fields example)
{
  "connector_mode": {
    "connector": "codex",
    "mode": "observability",
    "policy_mode": "action",
    "guardrail_mode": "action",
    "hook_fail_mode": "closed",
    "enabled": true,
    "hook_enforcement": true,
    "enforcement_surface": "agent_lifecycle_hooks",
    "telemetry": ["hooks", "otel", "notify"],
    "proxy_intercept": false
  },
  "connector_modes": [
    {
      "connector": "codex",
      "mode": "observability",
      "policy_mode": "action",
      "guardrail_mode": "action",
      "hook_fail_mode": "closed",
      "enabled": true,
      "hook_enforcement": true,
      "enforcement_surface": "agent_lifecycle_hooks",
      "telemetry": ["hooks", "otel", "notify"],
      "proxy_intercept": false
    },
    {
      "connector": "claudecode",
      "mode": "observability",
      "policy_mode": "observe",
      "guardrail_mode": "observe",
      "hook_fail_mode": "closed",
      "enabled": true,
      "hook_enforcement": false,
      "enforcement_surface": "agent_lifecycle_hooks",
      "telemetry": ["hooks", "otel"],
      "proxy_intercept": false
    }
  ]
}
  • connector_mode (singular) is the same summary object for the primary connector, retained 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 and guardrail_mode report the effective observe/action posture; hook_fail_mode is the effective hook fail mode; enabled is the connector's effective guardrail enablement; hook_enforcement is true for a non-proxy connector in action mode; enforcement_surface identifies the proxy, lifecycle-hook, or OmniGent policy API path; telemetry lists reported wiring channels; 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

Built-in connectors that implement the hook endpoint contract register the following routes. Proxy connectors such as OpenClaw and ZeptoClaw do not register a connector hook route.

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/amp/hookAmpinternal/gateway/connector/amp.go
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/toolShared hook script; OpenClaw extensionInspect a tool call (tool + args), or inspect message content when tool: "message" supplies content and direction. Returns the verdict envelope below.
POST/api/v1/inspect/requestShared hook scriptInspect an outbound LLM request (prompt).
POST/api/v1/inspect/responseShared hook scriptInspect a model response.
POST/api/v1/inspect/tool-responseShared hook scriptInspect a tool's output before it returns to the agent.

/api/v1/inspect/tool is the existing trusted tool-call entry point; there is no client-supplied is_tool_call switch and no additional semantic endpoint. The authenticated route and its server-side connector identity establish the boundary. A request with tool: "message" stays in the message lane and is not eligible for structured tool-call CEL rules.

Single-call semantic findings are available on this inspect route and on native pre-tool hooks. Ordered tool chains are stricter: they use durable SQLite state only on authenticated /api/v1/{connector}/hook events with canonical connector and session correlation. Inspect, proxy, router, and OTLP traffic never advance a chain. Only a matching authoritative successful result can promote an existing pending proposal into a durable successful predecessor. A chain can deny only its final step on an enforcement-safe synchronous pre-tool event. Post-tool and tool-result events never make the final blocking decision. See the stateful connector lifecycle for the pairing, state-level, and cutoff contracts. Chain matches are HIGH severity and retain the connector's effective action matrix: default and permissive profiles alert, strict blocks, and eligible human-in-the-loop configurations confirm. A durable deny receipt is created only when that effective action is block.

Ordered chainWall-clock windowEvent horizon
Guardrails disabled → external egress30 minutes64 events
Permission denied → runtime bypass5 minutes16 events
Privilege discovery → elevation15 minutes32 events
Secret-manager read → external egress30 minutes64 events
Sensitive secret read → external egress30 minutes64 events
Workload identity access → lateral execution15 minutes32 events

Chain state contains bounded masks and fingerprints rather than raw commands, arguments, paths, endpoints, or tool payloads. Durable deny receipts replay the same final decision without duplicating finding telemetry.

Code & network scanning

MethodPathPurpose
POST/api/v1/scan/codeRun the built-in CodeGuard scanner on the local filesystem path supplied in the JSON body.
GET/api/v1/network-egressList persisted egress audit records. Supports limit, hostname, session_id, agent_id, root_agent_id, user_id, blocked, and RFC3339 since filters.
POST/api/v1/network-egressIngest one observed egress event into the audit logger; this is not an allow/block decision endpoint.

Asset scanning

MethodPathPurpose
POST/v1/skill/scanRun the skill scanner against the local target directory named in the request.
POST/v1/plugin/scanRun the plugin scanner against the local target directory named in the request.
POST/v1/mcp/scanRun the MCP scanner against a target URL or local path.
POST/v1/skill/fetchStream a tar.gz of an existing local directory after resolving it beneath a configured skill or plugin root; this does not fetch from a registry.
POST/scan/resultPersist a scanner result through the canonical audit logger.

Asset enable / disable

MethodPathPurpose
POST/skill/disableAsk the connected upstream agent gateway to disable a skill by key (skills.update).
POST/skill/enableAsk the connected upstream agent gateway to enable a skill by key (skills.update).
POST/plugin/disableAsk the connected upstream/OpenClaw gateway to disable a plugin through its config RPC.
POST/plugin/enableAsk the connected upstream/OpenClaw gateway to enable a plugin through its config RPC.

These routes return 503 when no upstream client object is present and 502 when an attempted upstream RPC fails, including a disconnected client. They do not perform the CLI's filesystem quarantine/restore workflow.

Inventory

MethodPathPurpose
GET/skillsReturn the connected upstream agent gateway's skills.status RPC payload. An absent client returns 503; an RPC/disconnection failure returns 502.
GET/mcpsReturn MCP server entries for the singular active connector from DefenseClaw configuration; unavailable or malformed input returns an empty list.
GET/tools/catalogReturn the one connected upstream agent gateway's tools.catalog RPC payload. An absent client returns 503; an RPC/disconnection failure returns 502.

Provider registry

MethodPathPurpose
GET, HEAD/v1/config/providersReturn the merged built-in and operator provider registry plus overlay status.
POST/v1/config/providers/reloadReload the built-in registry and custom-providers.json overlay.

Policy

MethodPathPurpose
POST/policy/evaluateDebug the admission policy with {domain?: "admission", input: {target_type, target_name, path?, scan_result?}}. DefenseClaw injects its current allow/block lists and returns {ok: true, data: <AdmissionOutput>}; arbitrary OPA domains/input are rejected.
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/reloadAtomically reload .rego modules plus optional data.json from configured policy_dir (or its rego child), invalidate the judge cache, and return {"status":"reloaded","policy_dir":"..."}.

Guardrail control plane

MethodPathPurpose
POST/v1/guardrail/eventEmit canonical telemetry for an already-decided guardrail event. Requires evaluation_id, direction (prompt or completion), action (allow, alert, or block), and severity (NONE through CRITICAL); latency/token values must be finite and nonnegative. Returns {"status":"ok"}. No production caller is wired in this repository.
POST/v1/guardrail/evaluateCombine caller-supplied, precomputed local_result / cisco_result values with required evaluation_id, direction, and mode, plus optional model/scanner/content/timing metadata. It does not scan arbitrary content and returns a GuardrailOutput with action, severity, reason, and scanner_sources.
GET/v1/guardrail/configReturn the active guardrail config snapshot.
PATCH/v1/guardrail/configPatch supported guardrail runtime fields into the active DefenseClaw config file, validate the full YAML file, and apply it through the central reloader. Body is JSON, not raw YAML. Supported fields: mode, scanner_mode, block_message, connector, hilt_enabled, hilt_min_severity. Managed-enterprise deployments reject this API mutation.

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 manage the durable enforcement allow/block lists.

MethodPathPurpose
POST, DELETE/enforce/blockAdd a block, or remove it with DELETE.
POST/enforce/allowAdd an allow and re-enable a disabled skill/plugin when required.
GET/enforce/blockedList blocked entries.
GET/enforce/allowedList allowed entries.

Audit

MethodPathPurpose
POST/audit/eventAppend one audit-event JSON object to the configured audit logger/store. The production OpenClaw extension client uses this route; managed connector hook routes audit internally.
GET/alertsReturn recent alert rows. limit defaults to 50 and is capped at 500.
POST/api/v1/alerts/dispositionPreview or apply an audited alert acknowledgement/disposition operation.

There is no general audit-history or streaming HTTP route. Read/export history through the CLI:

defenseclaw-gateway audit export --output audit.jsonl     # JSONL of audit_events
tail -f ~/.defenseclaw/gateway.jsonl | jq                  # Optional configured v8 JSONL destination
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/patchBridge {path, value} to the connected upstream agent gateway's WebSocket config.patch RPC. It does not write DefenseClaw's config.yaml; an absent client returns 503, while an RPC/disconnection failure returns 502.

Runtime control and internal observability bridges

These authenticated routes are narrow process/CLI integration seams rather than general-purpose ingestion APIs.

MethodPathPurpose
POST/api/v1/admin/shutdownRequest graceful shutdown. Restricted to loopback and requires the current PID and data directory identity.
POST/api/v1/telemetry/canaryEmit a trace canary through the active observability-v8 runtime.
POST/api/v1/watchdog/recoveryRecord a watchdog recovery metric. Restricted to loopback.
POST/api/v1/observability/destination-test/activityPersist destination-test compliance activity. Restricted to loopback and the exact python-cli client marker.
POST/api/v1/observability/cliHand validated CLI events to the process-owned observability-v8 runtime.

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-usageCurrent continuous-discovery snapshot (summary plus agent, process, endpoint, component, and local-model signals). Local responses retain each model's dedicated model block.
POST/api/v1/ai-usage/scanTrigger an on-demand AI usage scan.
POST/api/v1/ai-usage/discoveryIngest a validated external AI-discovery report.
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.

Correlation ledger

Each read route accepts exactly one supported identity anchor (for example record_id, evaluation_id is not a supported query key here, semantic_event_id, session_id, trace_id, or tool_invocation_id) and supports bounded cursor pagination.

MethodPathPurpose
GET/api/v1/correlation/graphReturn the evidence-backed identity graph around an anchor.
GET/api/v1/correlation/explainExplain how records are linked to an anchor.
GET/api/v1/correlation/timelineReturn the correlated event timeline.
GET/api/v1/correlation/conflictsReturn identity conflicts associated with an anchor.

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 configured connectors. Both OTLP JSON and protobuf content types are accepted; decoding is implemented in internal/gateway/otel_ingest.go. Request bodies are bounded at 64 MiB; larger batches receive HTTP 413 and are recorded as content-free body_too_large rejections.

MethodPathPurpose
POST/v1/logsIngest OTLP logs.
POST/v1/metricsIngest OTLP metrics.
POST/v1/tracesIngest OTLP traces.
POST/otlp/{source}/{token}/v1/{signal}Connector-scoped logs, metrics, or traces for a native exporter that cannot set an auth header. The path token is accepted only over loopback and is removed from route telemetry.

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

Verdict envelope

All four /api/v1/inspect/* endpoints serialize the same ToolInspectVerdict:

{
  "action":     "block | confirm | alert | allow",
  "raw_action": "block",
  "severity":   "CRITICAL | HIGH | MEDIUM | LOW | INFO | NONE",
  "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": 45000
}

Notable behaviours:

  • action is the effective verdict, not the raw policy decision. applyMode() downgrades block, confirm, and alert to allow outside action mode and preserves the original in raw_action. It sets would_block: true only for a downgraded block; downgraded confirm and alert leave that field false/omitted.
  • 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.
  • Pattern-rule and CodeGuard decisions populate both representations for backward compatibility. Clean verdicts can omit details, and static, MCP, or AID merge paths can add string findings without synthesizing matching detailed_findings.
  • Optional fields are omitted when empty. In particular, approval_timeout_ms is absent when its value is zero. A structured finding can additionally include tool_capability_class; it has no remediation field.
  • Empty request/response content takes a fast allow path. Those two handlers return the same verdict type, but before applyMode() runs; the serialized non-optional mode is therefore an empty string and raw_action is absent.

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

Connector hook responses

A connector hook response can carry evaluation_id and the top rule_ids when the evaluation produced correlation data:

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

Connector-hook responses expose a string findings list, not the inspect endpoint's detailed_findings objects. Pattern-rule entries normally use rule_id:title; some scanner-specific paths emit bare finding IDs. rule_ids is the canonical bare-ID list populated with evaluation_id when the hook evaluation produced correlation data.

For /api/v1/inspect/*, evidence is redacted by default. X-DefenseClaw-Reveal-PII: 1 requests raw response evidence. The tool-inspect handler records an inspect-reveal audit event; the request, response, and tool-response handlers currently reveal without that extra audit event.

Headers

HeaderDirectionPurpose
X-DefenseClaw-Token, Authorization: Bearer ..., or X-DC-Auth: Bearer ...requestMaster gateway authentication. Every route except GET /health requires either this credential or a route-appropriate scoped token.
X-DefenseClaw-ClientrequestCSRF marker — any non-empty caller identifier (cli, openclaw-plugin, inspect-hook/1.0). Required on non-GET/HEAD requests except the constrained loopback path-token OTLP case described above.
X-DefenseClaw-ConnectorrequestOn loopback generic inspect routes, selects the registered connector whose scoped hook token is being presented.
X-DefenseClaw-SourcerequestOn loopback /v1/{logs,metrics,traces}, selects and must match a header-scoped Codex or Claude Code OTLP token.
X-DefenseClaw-Request-Idrequest (optional), responseCaller-supplied correlation id. The gateway also accepts X-Request-Id and X-Correlation-Id; if none is supplied, it mints one. The chosen ID is always returned as X-DefenseClaw-Request-Id. See requestctx.go.