View as markdown

# Config Reference

A clawpatrol gateway config mixes operational settings in the required top-level gateway { ... } block with policy blocks. Policy blocks (approver, credential, tunnel, endpoint, rule) dispatch to a plugin chosen by the block's first label.

# How to read this page

Each block section lists the attributes the loader accepts, with:

Plugin-dispatched kinds (approver, credential, tunnel, endpoint, rule) list one subsection per registered type.

# Top-level blocks

Operational settings live under the required top-level gateway { ... } block. The optional defaults { ... } block carries policy fallbacks. Labeled policy blocks (profile, approver, credential, endpoint, rule, tunnel) are documented in their own sections.

AttributeTypeRequiredDescription
gatewayblockyesCarries every operational scalar and the two transport sub-blocks. Required: configs missing the block fail to load.
defaultsblocknoHolds the optional defaults { ... } block with the policy defaults (unknown_host, llm_*, human_*). nil when the block is absent — every field has a built-in default.
pluginblocknoLists every plugin "<name>" { source = "..." } block at the top of the file. The loader spawns each subprocess (and registers its declared types) before running pass-1 symbol building, so plugin-supplied (kind, type) pairs are available by the time policy blocks are dispatched.

# profile "<name>" { ... }

Names a set of credentials. Profiles bind to dashboard owners; an owner's profile determines which credentials — and, transitively via each credential's endpoint / endpoints binding, which endpoints — their gateway requests can reach. Rules ride along automatically because they're attached to endpoints.

AttributeTypeRequiredDescription
credentials[]credentialyesBare-name credential references, or { credential = name, <disambiguator> = "..." } object entries for multi-credential dispatch (e.g. placeholder for header-token credentials).
profile "default" {
  credentials = [bearer_token.github, postgres_credential.postgres-prod]
}

# approver blocks

Block syntax: approver "<type>" "<name>" { ... }

Registered types: human_approver, llm_approver.

# approver "human_approver" "<name>"

Targets one channel. Timeout / require_approvers override the global defaults block on a per-approver basis.

Credential references a credential whose body satisfies HITLNotifier (slack_tokens today; future Discord / Telegram / SMTP credentials). Leave empty for a dashboard-only approver (no channel notification; operator clicks approve/deny on the dashboard).

AttributeTypeRequiredDescription
channelstringyesThe destination channel, chat id, or equivalent notifier-specific target.
credentialref(credential)noReferences the notifier credential used to post approval requests. Leave empty for dashboard-only approval.
timeoutintnoOverrides the gateway's human_timeout for this approver, in seconds.
require_approversintnoThe number of separate human approvals required before the request is allowed.
interactiveboolnoToggles in-channel approve/deny buttons. Requires the referenced credential's signing_secret slot pasted via the dashboard AND Slack's Interactivity URL pointed at the gateway. Default false: message includes only an "Open dashboard" link.
classifierref(approver)noOptionally references an llm_approver by name. When set, the approver calls the classifier's Summarize method before posting the HITL notification, enriching the Slack card with classification metadata. Classifier failures are non-fatal — the generic card is used as fallback.
messagestringnoAn optional Go-template-style string with {{var}} placeholders. When set, the expanded text replaces the default section body in the Slack (or other notifier) card. Supported vars mirror the CEL facet namespace: {{http.method}}, {{http.path}}, {{k8s.verb}}, {{sql.tables}}, {{body_json.ticket}}, {{profile}}, {{endpoint}}, {{reason}}, etc. Classifier (if also set) still runs; Message takes display precedence.
approver "human_approver" "example" {
  channel = "#approvals"
}

# approver "llm_approver" "<name>"

Carries the model + the credential used to authenticate the call to the model API + the inline policy text the model judges against. policy is a heredoc-friendly string attribute on the approver block itself — no separate policy "<name>" {} block.

AttributeTypeRequiredDescription
modelstringyesThe model id used for policy judgment, such as a claude-, gpt-, or o*-prefixed model.
credentialref(credential)yesReferences the HTTP credential used to authenticate the model API call.
policystringnoThe prose the model judges requests against. Typically a heredoc on the approver block.
approver "llm_approver" "example" {
  model = "claude-haiku-4-5-20251001"
  credential = bearer_token.example
}

# credential blocks

Block syntax: credential "<type>" "<name>" { ... }

Registered types: anthropic_manual_key, anthropic_oauth_subscription, aws_credential, bearer_token, clickhouse_credential, cookie_token, discord_bot_token, gemini_api_key, github_oauth, google_gke_credential, header_token, mtls_credential, notion_mcp_oauth, notion_oauth, openai_codex_oauth, postgres_credential, slack_tokens, ssh_key, tailscale_auth, telegram_bot_token.

# credential "anthropic_manual_key" "<name>"

No configurable attributes.

credential "anthropic_manual_key" "example" {}

# credential "anthropic_oauth_subscription" "<name>"

No configurable attributes.

credential "anthropic_oauth_subscription" "example" {}

# credential "aws_credential" "<name>"

Schema is intentionally empty: access key id and secret access key (and optional session token) live in the secret store as named slots, filled via the dashboard or CLAWPATROL_SECRET__ env vars. Cluster + region come from the kubernetes endpoint at request time.

No configurable attributes.

credential "aws_credential" "example" {}

# credential "bearer_token" "<name>"

AttributeTypeRequiredDescription
idempotency_keyboolnoStamps a deterministic Idempotency-Key header on non-GET/HEAD HTTP requests when the agent did not provide one.
credential "bearer_token" "example" {}

# credential "clickhouse_credential" "<name>"

Database, when set, is the discriminator the dispatcher uses to pick this credential when several clickhouse_credential blocks bind the same endpoint(s). At request time the gateway reads the agent-declared database off the wire and picks the credential whose database matches; an unset database field is the catchall (one allowed per (profile, endpoint)).

AttributeTypeRequiredDescription
userstringnoThe upstream ClickHouse user the gateway injects.
databasestringnoLimits this credential to ClickHouse requests for that database. Empty acts as the catchall.
credential "clickhouse_credential" "example" {}
AttributeTypeRequiredDescription
cookie_namestringnoThe HTTP cookie name that receives the secret value.
credential "cookie_token" "example" {}

# credential "discord_bot_token" "<name>"

Injects Discord bot tokens for REST and Gateway SDK traffic.

No configurable attributes.

credential "discord_bot_token" "example" {}

# credential "gemini_api_key" "<name>"

No configurable attributes.

credential "gemini_api_key" "example" {}

# credential "github_oauth" "<name>"

No configurable attributes.

credential "github_oauth" "example" {}

# credential "google_gke_credential" "<name>"

No configurable attributes.

credential "google_gke_credential" "example" {}

# credential "header_token" "<name>"

AttributeTypeRequiredDescription
headerstringyesThe HTTP header name to overwrite with the secret value.
prefixstringnoPrepended to the secret before injection, for schemes such as "Bearer " or "Token ".
credential "header_token" "example" {
  header = "X-API-Key"
}

# credential "mtls_credential" "<name>"

No configurable attributes.

credential "mtls_credential" "example" {}

# credential "notion_mcp_oauth" "<name>"

No configurable attributes.

credential "notion_mcp_oauth" "example" {}

# credential "notion_oauth" "<name>"

No configurable attributes.

credential "notion_oauth" "example" {}

# credential "openai_codex_oauth" "<name>"

No configurable attributes.

credential "openai_codex_oauth" "example" {}

# credential "postgres_credential" "<name>"

Database, when set, is the discriminator the dispatcher uses to pick this credential when several postgres_credential blocks bind the same endpoint(s). At request time the gateway reads the agent-declared database off the StartupMessage and picks the credential whose database matches; an unset database field is the catchall (one allowed per (profile, endpoint)).

AttributeTypeRequiredDescription
userstringnoThe upstream Postgres role the gateway authenticates as.
databasestringnoLimits this credential to sessions whose StartupMessage declares the same database. Empty acts as the catchall.
credential "postgres_credential" "example" {}

# credential "slack_tokens" "<name>"

No configurable attributes.

credential "slack_tokens" "example" {}

# credential "ssh_key" "<name>"

No configurable attributes.

credential "ssh_key" "example" {}

# credential "tailscale_auth" "<name>"

Has no operator-facing fields — there is nothing to paste. Per-tailnet selection (control_url, tags) lives on the tunnel block instead.

No configurable attributes.

credential "tailscale_auth" "example" {}

# credential "telegram_bot_token" "<name>"

No configurable attributes.

credential "telegram_bot_token" "example" {}

# endpoint blocks

Block syntax: endpoint "<type>" "<name>" { ... }

Registered types: clickhouse_https, clickhouse_native, http, kubernetes, openai_codex_https, postgres, ssh.

# endpoint "clickhouse_https" "<name>"

Family: sql.

AttributeTypeRequiredDescription
hosts[]stringyesThe set of ClickHouse HTTPS hostnames or host:port pairs this endpoint intercepts.
endpoint "clickhouse_https" "example" {
  hosts = ["api.example.com"]
}

# endpoint "clickhouse_native" "<name>"

Addresses one ClickHouse server reachable via the binary native protocol. Operators bind a single clickhouse_credential; the runtime parses the agent's Hello and substitutes the credential's (user, password) where the agent embedded a placeholder.

TLS toggles TLS on both hops: the gateway terminates the agent's TLS using a leaf minted off the gateway CA, parses the Hello in plaintext, then re-wraps to upstream. The wrapped client therefore keeps speaking native-over-TLS exactly as it would against the real cloud ClickHouse — clawpatrol run is transparent to its TLS posture. Default false: WG-only deployments where the operator wants plaintext on the inner hop (typical self-hosted ClickHouse on 9000 behind a private network) leave it off.

AcceptInvalidCertificate mirrors clickhouse-client's flag of the same name: when true and tls is on, the gateway skips upstream cert validation. Use for self-hosted ClickHouse fronted by a private CA. Default false keeps full validation against system roots.

Family: sql.

AttributeTypeRequiredDescription
hosts[]stringyesThe set of ClickHouse native-protocol hostnames or host:port pairs this endpoint intercepts.
portintnoThe default upstream port for hosts that omit one. Defaults to 9000 without TLS and 9440 with TLS.
tlsboolnoEnables ClickHouse native-over-TLS on the upstream hop.
accept_invalid_certificateboolnoSkips upstream certificate validation when TLS is enabled.
endpoint "clickhouse_native" "example" {
  hosts = ["api.example.com"]
}

# endpoint "http" "<name>"

Family: http.

AttributeTypeRequiredDescription
hosts[]stringyesThe set of HTTPS hostnames or host:port pairs this endpoint intercepts.
endpoint "http" "example" {
  hosts = ["api.example.com"]
}

# endpoint "kubernetes" "<name>"

ClusterName + Region are EKS auth parameters: when the bound credential is aws_credential, the gateway presigns an STS GetCallerIdentity URL scoped to (region, cluster_name) and stamps the result as a k8s-aws-v1.<…> bearer. Leave both unset for self-hosted clusters with a non-EKS credential (bearer_token, mtls_credential).

Family: k8s.

AttributeTypeRequiredDescription
hosts[]stringnoAn optional list of Kubernetes API hostnames or host:port pairs to intercept.
serverstringnoThe Kubernetes API server URL or host:port used when hosts is not set.
ca_certstringnoThe PEM-encoded cluster CA, often loaded with <<file:cluster-ca.pem>>.
descriptionstringnoOperator-facing text for dashboard display.
cluster_namestringnoThe EKS cluster name used by aws_credential.
regionstringnoThe AWS region used by aws_credential for EKS auth.
endpoint "kubernetes" "example" {}

# endpoint "openai_codex_https" "<name>"

Family: http.

AttributeTypeRequiredDescription
hosts[]stringyesThe chatgpt.com host list intercepted for Codex subscription-auth traffic.
endpoint "openai_codex_https" "example" {
  hosts = ["api.example.com"]
}

# endpoint "postgres" "<name>"

Addresses a single RDS-or-equivalent server. Tunnel topologies (kubectl-portforward-ssh and friends) aren't supported in this iteration — operators run the gateway with network reachability already arranged.

SSLMode mirrors libpq's sslmode names — "disable" / "prefer" / "require" / "verify-full". Default "prefer": try TLS, fall back to plain when the upstream replies 'N'. "require" hard-fails on 'N'. "verify-full" additionally validates the upstream cert against Host. "disable" skips the SSLRequest probe entirely — fine for self-hosted pg on a private network where WG already encrypts the path.

Family: sql.

AttributeTypeRequiredDescription
hoststringyesThe upstream Postgres host:port pair.
sslmodestringnoControls upstream TLS negotiation. Valid values mirror libpq: "disable", "prefer", "require", and "verify-full".
endpoint "postgres" "example" {
  host = "db.internal:5432"
}

# endpoint "ssh" "<name>"

Binds one or more host:port tuples. The credentials that authenticate against it live on credential blocks via the framework-level endpoint = X / endpoints = [...] binding. When a profile wields more than one SSH credential at the endpoint, each ambiguous credential carries a user = "..." disambiguator — either on its profile-inline entry ({ credential = X, user = "..." }) or on the credential block itself — and the agent's wire-protocol username picks the matching entry. The agent's username is also passed through verbatim as the upstream SSH user; credentials carry only auth material (key / password / host_pubkey), never a username override.

Family: ssh.

AttributeTypeRequiredDescription
hosts[]stringyesThe set of SSH host:port pairs this endpoint intercepts.
endpoint "ssh" "example" {
  hosts = ["api.example.com"]
}

# rule blocks

Block syntax: rule "<name>" { ... }

# rule "<name>"

The gohcl-tagged decode target. The match predicate is family-agnostic at the HCL layer (just a CEL string); the facet's *cel.Env decides which variables are valid once the family has been inferred from the endpoint refs.

AttributeTypeRequiredDescription
endpointref(endpoint)noThe single endpoint this rule attaches to. Use endpoint or endpoints, not both.
endpoints[]ref(endpoint)noThe list of endpoints this rule attaches to. All referenced endpoints must share one protocol family.
priorityintnoOrders matching rules. Higher values run first; equal priorities preserve declaration order.
disabledboolnoKeeps the rule in config while excluding it from runtime evaluation.
conditionstringnoA CEL expression evaluated against the family-specific variable set. An absent / empty condition matches everything — the catch-all pattern (rule "X-default" { priority = -100; verdict = "deny" }) relies on this.
credentialref(credential)noCredential, if set, is a bare-name reference to a credential block. The runtime treats it as an extra match predicate (request must have been dispatched against this credential) evaluated before the CEL expression.
verdictstringnoThe outcome when the rule matches. Set exactly one of verdict ("allow" / "deny") or approve.
reasonstringnoThe operator-facing explanation recorded when the rule matches.
approve[]ref(approver)noA list of bare-name approver references. The approvers run in order; the request is allowed only if every stage approves. Set this or verdict, not both.
rule {}

# tunnel blocks

Block syntax: tunnel "<type>" "<name>" { ... }

Registered types: kubernetes_port_forward, local_command, ssh_port_forward, tailscale.

# tunnel "kubernetes_port_forward" "<name>"

Configures the tunnel runtime.

AttributeTypeRequiredDescription
contextstringnoSelects a kubeconfig context; empty uses the current context. Ignored when Server is set (the plugin builds its own per-tunnel kubeconfig).
namespacestringnoSelects the Kubernetes namespace for kubectl commands.
podstringnoNames an existing pod to port-forward to. Exactly one of pod, service, selector, or template must be set.
servicestringnoNames a service to port-forward to.
selectormap[string]stringnoMatches a ready pod to port-forward to.
templatestringnoA pod manifest to apply and port-forward to.
portintyesThe pod-side port the forwarder targets. For service mode it's the service port; kubectl resolves the matching targetPort.
cleanupstringnoControls whether a template-created pod is deleted on tunnel teardown. "delete" (default) is right for the common create-on-demand case; "keep" disables deletion.
serverstringnoThe Kubernetes apiserver URL. When set the plugin writes a per-tunnel kubeconfig (server + ca_cert + bearer minted from the bound credential) and invokes kubectl with --kubeconfig pointing at it; no external kubeconfig or KUBECONFIG env is needed. The Context field is then ignored.
ca_certstringnoThe cluster CA PEM. Supports <<file:path.pem>> for out-of-line storage; the loader inlines the file contents. Required when Server is set against EKS (the apiserver presents a per-cluster CA that no system trust store carries).
cluster_namestringnoThe EKS cluster name, used by an aws_credential to scope the STS presign (sets the X-K8s-Aws-Id header). Only meaningful alongside Server + an aws_credential.
regionstringnoThe AWS region the EKS cluster lives in; SigV4 needs it. Only meaningful alongside Server + an aws_credential.
sharestringnoControls whether runtime instances are singleton, per-endpoint, or per-request.
keepalivestringnoKeeps an idle tunnel runtime warm for the given duration.
viaref(tunnel)noChains kubectl access through another tunnel.
credentialref(credential)noReferences an optional credential block for Kubernetes access.
tunnel "kubernetes_port_forward" "example" {
  port = 30
}

# tunnel "local_command" "<name>"

Configures the tunnel runtime.

AttributeTypeRequiredDescription
command[]stringyesThe argv vector to spawn for the tunnel process.
listenstringyesThe local address the spawned command exposes.
ready_probestringnoAn optional TCP address to poll before the tunnel is ready.
ready_timeoutstringnoOverrides the default readiness wait duration.
envmap[string]stringnoAdds environment variables to the spawned command.
sharestringnoControls whether runtime instances are singleton, per-endpoint, or per-request.
keepalivestringnoKeeps an idle tunnel runtime warm for the given duration.
viaref(tunnel)noChains this tunnel through another tunnel.
credentialref(credential)noReferences an optional credential block for the tunnel runtime.
tunnel "local_command" "example" {
  command = ["example"]
  listen = "example"
}

# tunnel "ssh_port_forward" "<name>"

Configures the tunnel runtime.

AttributeTypeRequiredDescription
bastionstringnoThe SSH server host:port; required when via is unset.
userstringyesThe SSH username for the bastion login.
sharestringnoControls whether runtime instances are singleton, per-endpoint, or per-request.
keepalivestringnoKeeps an idle tunnel runtime warm for the given duration.
viaref(tunnel)noChains the SSH connection through another tunnel.
credentialref(credential)yesReferences an ssh credential block used for bastion authentication.
tunnel "ssh_port_forward" "example" {
  bastion = "bastion.example:22"
  user = "example"
  credential = bearer_token.example
}

# tunnel "tailscale" "<name>"

Configures the tunnel runtime.

AttributeTypeRequiredDescription
authkeystringnoThe Tailscale auth key; env fallback is CLAWPATROL_TUNNEL__AUTHKEY.
control_urlstringnoOverrides the Tailscale control-plane URL.
hostnamestringnoThe tsnet node name; defaults to clawpatrol-tunnel-.
state_dirstringnoStores tsnet node state; defaults under the gateway CA directory.
tags[]stringnoTailscale tags requested for the tsnet node.
sharestringnoControls whether runtime instances are singleton, per-endpoint, or per-request.
keepalivestringnoKeeps an idle tunnel runtime warm for the given duration.
viaref(tunnel)noChains this tunnel through another tunnel.
credentialref(credential)noReferences an optional credential block for the tunnel runtime.
tunnel "tailscale" "example" {}