Skip to main content

openclaw policy

openclaw policy is provided by the bundled Policy plugin. It is an enterprise conformance layer over existing OpenClaw settings, not a second configuration system. You author requirements in policy.jsonc; OpenClaw observes the active workspace as evidence; policy reports drift through doctor --lint. Policy does not enforce tool calls or rewrite runtime behavior at request time, and it does not attest per-agent credential stores such as auth-profiles.json. Policy checks configured channels, MCP servers, model providers, network SSRF posture, ingress/channel access, Gateway exposure and node command posture, agent workspace access, sandbox posture, data-handling posture, secret provider/auth profile posture, and governed tool metadata (TOOLS.md). Use it when a workspace needs a durable, checkable statement such as “Telegram must not be enabled” or “governed tools must declare risk and owner metadata.” If you only need local behavior with no attestation or drift detection, plain config is enough.

Quick start

openclaw plugins enable policy
The plugin stays enabled even when policy.jsonc is missing, so doctor can report the missing artifact instead of silently skipping checks. Author policy.jsonc by hand; it is not generated from current settings. Each top-level section is a rule namespace: a check only runs when a concrete rule is present under it (unsupported sections or keys fail as policy/policy-jsonc-invalid instead of being silently ignored). Minimal example covering every supported section:
{
  "channels": {
    "denyRules": [
      {
        "id": "no-telegram",
        "when": { "provider": "telegram" },
        "reason": "Telegram is not approved for this workspace.",
      },
    ],
  },
  "mcp": {
    "servers": {
      "allow": ["docs"],
      "deny": ["untrusted"],
    },
  },
  "models": {
    "providers": {
      "allow": ["openai", "anthropic"],
      "deny": ["openrouter"],
    },
  },
  "network": {
    "privateNetwork": {
      "allow": false,
    },
  },
  "ingress": {
    "session": {
      "requireDmScope": "per-channel-peer",
    },
    "channels": {
      "allowDmPolicies": ["pairing", "allowlist", "disabled"],
      "denyOpenGroups": true,
      "requireMentionInGroups": true,
    },
  },
  "gateway": {
    "exposure": {
      "allowNonLoopbackBind": false,
      "allowTailscaleFunnel": false,
    },
    "auth": {
      "requireAuth": true,
      "requireExplicitRateLimit": true,
    },
    "controlUi": {
      "allowInsecure": false,
    },
    "remote": {
      "allow": false,
    },
    "http": {
      "denyEndpoints": ["chatCompletions", "responses"],
      "requireUrlAllowlists": true,
    },
    "nodes": {
      "denyCommands": ["system.run"],
    },
  },
  "agents": {
    "workspace": {
      "allowedAccess": ["none", "ro"],
      "denyTools": ["exec", "process", "write", "edit", "apply_patch"],
    },
  },
  "dataHandling": {
    "sensitiveLogging": {
      "requireRedaction": true,
    },
    "telemetry": {
      "denyContentCapture": true,
    },
    "retention": {
      "requireSessionMaintenance": true,
    },
    "memory": {
      "denySessionTranscriptIndexing": true,
    },
  },
  "secrets": {
    "requireManagedProviders": true,
    "denySources": ["exec"],
    "allowInsecureProviders": false,
  },
  "auth": {
    "profiles": {
      "requireMetadata": ["provider", "mode"],
      "allowModes": ["api_key", "token"],
    },
  },
  "execApprovals": {
    "requireFile": true,
    "defaults": { "allowSecurity": ["deny"] },
    "agents": {
      "allowSecurity": ["deny", "allowlist"],
      "allowAutoAllowSkills": false,
      "allowlist": { "expected": ["deploy", "status"] },
    },
  },
  "tools": {
    "requireMetadata": ["risk", "sensitivity", "owner"],
    "profiles": {
      "allow": ["messaging", "minimal"],
    },
    "fs": {
      "requireWorkspaceOnly": true,
    },
    "exec": {
      "allowSecurity": ["deny", "allowlist"],
      "requireAsk": ["always"],
      "allowHosts": ["sandbox"],
    },
    "elevated": {
      "allow": false,
    },
    "denyTools": ["group:runtime", "group:fs"],
  },
}
Cross-cutting notes not obvious from the rule tables below:
  • Omitting gateway.bind while denying non-loopback binds means you accept the runtime default; set gateway.bind: "loopback" for strict conformance.
  • For a read-only agent, set sandbox mode to all or non-main on the applicable defaults/agent and workspaceAccess to none or ro. Missing or off sandbox mode does not satisfy a read-only policy.
  • agents.workspace.denyTools accepts exec, process, write, edit, apply_patch. The config tool-deny groups group:fs (file mutation) and group:runtime (shell/process) satisfy the equivalent posture.
  • Exec-approvals checks read the live exec-approvals.json artifact only when an execApprovals rule is present; a missing or invalid artifact is unobservable evidence, not a synthetic pass.
  • Secret and auth-profile evidence records provider/source posture and SecretRef metadata only, never raw values. Policy does not read or attest per-agent credential stores such as auth-profiles.json.
  • Data-handling evidence is config-level posture only (redaction mode, telemetry capture toggle, session maintenance mode, transcript-indexing setting). It does not inspect logs, telemetry exports, transcripts, or memory files, and a clean result does not prove that no personal data or secrets exist in them.

Policy rule reference

Every rule below is optional; a check runs only when the rule is present. The observed state is existing OpenClaw config or workspace metadata.

Scoped overlays

Use scopes.<scopeName> when specific agents or channels need stricter policy than the top-level baseline. The scope name is just a label; matching uses the selector inside the scope. Overlays are additive: the global rule still runs, and the scoped rule can add its own finding against the same evidence.
SelectorSupported sectionsUse when
agentIdstools, agents.workspace, sandbox, dataHandling.memory, execApprovalsOne or more runtime agents need stricter rules.
channelIdsingress.channelsOne or more channels need stricter ingress rules.
If an agentIds entry is not present in agents.list[], OpenClaw evaluates the scoped rule against inherited global/default posture for that runtime agent id instead of skipping it.
{
  "tools": {
    "exec": {
      "allowHosts": ["sandbox", "node"],
    },
  },
  "sandbox": {
    "requireMode": ["all", "non-main"],
  },
  "scopes": {
    "release-workspace": {
      "agentIds": ["release-agent", "review-agent"],
      "agents": {
        "workspace": {
          "allowedAccess": ["none", "ro"],
        },
      },
    },
    "release-lockdown": {
      "agentIds": ["release-agent"],
      "tools": {
        "exec": {
          "allowHosts": ["sandbox"],
          "allowSecurity": ["deny", "allowlist"],
          "requireAsk": ["always"],
        },
        "denyTools": ["exec", "process", "write", "edit", "apply_patch"],
      },
      "sandbox": {
        "requireMode": ["all"],
        "allowBackends": ["docker"],
      },
      "dataHandling": {
        "memory": {
          "denySessionTranscriptIndexing": true,
        },
      },
    },
    "shell-sandbox": {
      "agentIds": ["shell-agent"],
      "sandbox": {
        "allowBackends": ["openshell"],
        "containers": {
          "requireReadOnlyMounts": false,
        },
      },
    },
    "telegram-ingress": {
      "channelIds": ["telegram"],
      "ingress": {
        "channels": {
          "allowDmPolicies": ["pairing"],
          "denyOpenGroups": true,
          "requireMentionInGroups": true,
        },
      },
    },
  },
}
The same agent can appear in multiple scopes if each scope governs a different field, as above. A repeated scoped field for the same agent must be equally or more restrictive; a weaker duplicate claim is rejected (allow-lists are subsets, deny-lists are supersets, required booleans are fixed). Container posture rules (sandbox.containers.*) are checked only against evidence the matched agent’s sandbox backend can expose. If a backend cannot observe a rule you enabled for it, policy reports policy/sandbox-container-posture-unobservable instead of passing; scope container rules to the agent groups that use a backend which can expose them. Top-level ingress.session.requireDmScope stays global; session.dmScope is not channel-attributable evidence, so it cannot be scoped by channelIds. Every scope present in policy.jsonc must be valid and enforceable.

Channels

Policy fieldObserved stateUse when
channels.denyRules[].when.providerchannels.* provider and enabled stateDeny configured channels from a provider such as telegram.
channels.denyRules[].reasonFinding message and repair hint contextExplain why the provider is denied.

MCP servers

Policy fieldObserved stateUse when
mcp.servers.allowmcp.servers.* idsRequire every configured MCP server to be in an allowlist.
mcp.servers.denymcp.servers.* idsDeny specific configured MCP server ids.

Model providers

Policy fieldObserved stateUse when
models.providers.allowmodels.providers.* ids and selected model refsRequire configured providers and selected model refs to use approved providers.
models.providers.denymodels.providers.* ids and selected model refsDeny configured providers and selected model refs by provider id.

Network

Policy fieldObserved stateUse when
network.privateNetwork.allowPrivate-network SSRF escape hatchesSet to false to require private-network access to stay disabled.

Ingress and channel access

Policy fieldObserved stateUse when
ingress.session.requireDmScopesession.dmScopeRequire a reviewed direct-message isolation scope.
ingress.channels.allowDmPolicieschannels.*.dmPolicy and legacy channel DM policy fieldsAllow only reviewed direct-message channel policies.
ingress.channels.denyOpenGroupsChannel, account, and group ingress policyDeny open group ingress for configured channels and accounts.
ingress.channels.requireMentionInGroupsChannel, account, group, guild, and nested mention gate configRequire mention gates when group ingress is open or mention-gated.

Gateway

Policy fieldObserved stateUse when
gateway.exposure.allowNonLoopbackBindgateway.bindSet to false to require loopback Gateway binding.
gateway.exposure.allowTailscaleFunnelTailscale serve/funnel Gateway postureSet to false to deny Tailscale Funnel exposure.
gateway.auth.requireAuthgateway.auth.modeSet to true to reject disabled Gateway auth.
gateway.auth.requireExplicitRateLimitgateway.auth.rateLimitSet to true to require explicit auth rate-limit config.
gateway.controlUi.allowInsecureControl UI insecure auth/device/origin togglesSet to false to deny insecure Control UI exposure toggles.
gateway.remote.allowRemote Gateway mode/configSet to false to deny remote Gateway mode.
gateway.http.denyEndpointsGateway HTTP API endpointsDeny endpoint ids such as chatCompletions or responses.
gateway.http.requireUrlAllowlistsGateway HTTP URL-fetch inputsSet to true to require URL allowlists on URL-fetch inputs.
gateway.nodes.denyCommandsgateway.nodes.denyCommandsRequire exact node command ids such as system.run to be denied in OpenClaw config.
gateway.nodes.denyCommands is an exact, case-sensitive deny-superset rule. Use it when policy must prove that privileged node commands are explicitly denied by OpenClaw config. A deployment that intentionally allows a privileged node command should update policy.jsonc after review instead of relying on gateway.nodes.allowCommands alone.

Agent workspace

Policy fieldObserved stateUse when
agents.workspace.allowedAccessagents.defaults.sandbox.workspaceAccess and agents.list[].sandbox.workspaceAccessAllow only sandbox workspace access values such as none or ro.
agents.workspace.denyToolsGlobal and per-agent tool deny configRequire mutation tools (exec, process, write, edit, apply_patch) to be denied.

Sandbox posture

Policy fieldObserved stateUse when
sandbox.requireModeagents.defaults.sandbox.mode and per-agent modeAllow only reviewed sandbox modes such as all or non-main.
sandbox.allowBackendsagents.defaults.sandbox.backend and per-agent backendAllow only reviewed sandbox backends such as docker.
sandbox.containers.denyHostNetworkContainer-backed sandbox/browser network modeDeny host network mode.
sandbox.containers.denyContainerNamespaceJoinContainer-backed sandbox/browser network modeDeny joining another container network namespace.
sandbox.containers.requireReadOnlyMountsContainer-backed sandbox/browser mount modeRequire mounts to be read-only.
sandbox.containers.denyContainerRuntimeSocketMountsContainer-backed sandbox/browser mount targetsDeny container runtime socket mounts.
sandbox.containers.denyUnconfinedProfilesContainer security profile postureDeny unconfined container security profiles.
sandbox.browser.requireCdpSourceRangeSandbox browser CDP source rangeRequire browser CDP exposure to declare a source range.
Policy treats missing sandbox.mode as its implicit default off, so sandbox.requireMode reports a fresh or unconfigured sandbox as outside an allowlist such as ["all"].

Data Handling

Policy fieldObserved stateUse when
dataHandling.sensitiveLogging.requireRedactionlogging.redactSensitiveSet to true to reject logging.redactSensitive: "off".
dataHandling.telemetry.denyContentCapturediagnostics.otel.captureContentSet to true to reject telemetry content capture.
dataHandling.retention.requireSessionMaintenancesession.maintenance.modeSet to true to require effective session maintenance mode enforce.
dataHandling.memory.denySessionTranscriptIndexingmemory.qmd.sessions.enabled and agents.*.memorySearch.experimental.sessionMemorySet to true to reject session transcript indexing into memory.

Secrets

Policy fieldObserved stateUse when
secrets.requireManagedProvidersConfig SecretRefs and secrets.providers.* declarationsSet to true to require SecretRefs to point at declared providers.
secrets.denySourcesSecret provider sources and SecretRef sourcesDeny sources such as exec, file, or another configured source name.
secrets.allowInsecureProvidersInsecure secret-provider posture flagsSet to false to reject providers that opt into insecure posture.

Exec approvals

Exec-approvals checks read the runtime exec-approvals.json artifact: ~/.openclaw/exec-approvals.json by default, or $OPENCLAW_STATE_DIR/exec-approvals.json when OPENCLAW_STATE_DIR is set. Posture rules under execApprovals.defaults.* or execApprovals.agents.* require readable artifact evidence; a missing or invalid artifact reports as unobservable evidence rather than a best-effort pass. Once readable, omitted fields inherit runtime defaults: missing defaults.security is full, and missing agent security inherits that default. Evidence includes defaults, agents.*, agents.*.allowlist[].pattern, optional argPattern, effective autoAllowSkills posture, and entry source — never socket path/token, commandText, lastUsedCommand, resolved paths, or timestamps.
Policy fieldObserved stateUse when
execApprovals.requireFileActive runtime exec-approvals.json pathSet to true to require the approvals artifact to exist and parse.
execApprovals.defaults.allowSecuritydefaults.security, defaulting to fullAllow only approved default approval security modes.
execApprovals.agents.allowSecurityagents.*.security, inheriting defaultsAllow only approved per-agent effective approval security modes.
execApprovals.agents.allowAutoAllowSkillsdefaults.autoAllowSkills and agents.*.autoAllowSkills, inheriting runtime defaultsSet to false to require strict manual allowlists without implicit skill CLI approval.
execApprovals.agents.allowlist.expectedAggregate agents.*.allowlist[] pattern and optional argPattern entriesRequire the approvals allowlist to match the reviewed pattern set.
Example: require the approvals artifact, deny permissive defaults, and allow only reviewed exec approval posture for selected agents.
{
  "execApprovals": {
    "requireFile": true,
    "defaults": {
      // Security modes: "deny", "allowlist", or "full".
      // This default permits only the locked-down deny posture.
      "allowSecurity": ["deny"],
    },
  },
  "scopes": {
    "restricted-shell": {
      "agentIds": ["family-agent", "groups-agent"],
      "execApprovals": {
        "agents": {
          // Selected agents may use reviewed allowlist posture, but not "full".
          "allowSecurity": ["allowlist"],
          // false means skill CLIs must appear in the reviewed allowlist instead of
          // being implicitly approved by autoAllowSkills.
          "allowAutoAllowSkills": false,
          "allowlist": {
            "expected": [
              // Simple entry: exact reviewed executable pattern with no argPattern.
              "travel-hub",
              // Constrained entry: pattern plus reviewed argument regex.
              { "pattern": "calendar-cli", "argPattern": "^sync\\b" },
              "/bin/date",
            ],
          },
        },
      },
    },
  },
}

Auth profiles

Policy fieldObserved stateUse when
auth.profiles.requireMetadataauth.profiles.* provider and mode metadataRequire metadata keys such as provider and mode on config auth profiles.
auth.profiles.allowModesauth.profiles.*.modeAllow only supported auth profile modes such as api_key, aws-sdk, oauth, or token.

Tool metadata

Policy fieldObserved stateUse when
tools.requireMetadataGoverned TOOLS.md declarationsRequire governed tools to declare metadata keys such as risk, sensitivity, or owner.

Tool posture

Policy fieldObserved stateUse when
tools.profiles.allowtools.profile and agents.list[].tools.profileAllow only tool profile ids such as minimal, messaging, or coding.
tools.fs.requireWorkspaceOnlytools.fs.workspaceOnly and per-agent tools.fs overridesSet to true to require workspace-only filesystem tool posture.
tools.exec.allowSecuritytools.exec.security and per-agent exec securityAllow only exec security modes such as deny or allowlist.
tools.exec.requireAsktools.exec.ask and per-agent exec ask modeRequire approval posture such as always.
tools.exec.allowHoststools.exec.host and per-agent exec host routingAllow only exec host routing modes such as sandbox.
tools.elevated.allowtools.elevated.enabled and per-agent elevated postureSet to false to require elevated tool mode to stay disabled.
tools.alsoAllow.expectedtools.alsoAllow and per-agent tools.alsoAllowRequire exact alsoAllow entries and report missing or unexpected additive tool grants.
tools.denyToolstools.deny and agents.list[].tools.denyRequire configured tool deny lists to include tool ids or groups such as group:runtime and group:fs.

Run checks

Run policy-only checks during authoring:
openclaw policy check
openclaw policy check --json
openclaw policy check --severity-min error
policy check runs only the policy check set and emits evidence, findings, and attestation hashes. The same findings also appear in openclaw doctor --lint when the Policy plugin is enabled. Compare an operator policy file against an authored baseline:
openclaw policy compare --baseline official.policy.jsonc
openclaw policy compare --baseline official.policy.jsonc --policy policy.jsonc --json
policy compare checks policy-file syntax against policy-file syntax; it does not inspect runtime state, evidence, credentials, or secrets. It uses the same rule metadata that governs scoped overlays: allowlists must stay equal or narrower, denylists must stay equal or broader, required booleans must keep their value, ordered strings may only move toward the stricter end of the configured order, and exact lists must match. The baseline can be an organization-authored policy; the checked policy may add stricter values or extra rules. A top-level checked rule can satisfy a scoped baseline rule when it is equally or more restrictive. Scope names do not need to match between files; comparison is keyed by selector (agentIds/channelIds) and field. Clean compare (--json):
{
  "ok": true,
  "baselinePath": "official.policy.jsonc",
  "policyPath": "policy.jsonc",
  "rulesChecked": 3,
  "findings": []
}
Clean policy check --json output includes stable hashes an operator or supervisor can record:
{
  "ok": true,
  "attestation": {
    "policy": {
      "path": "policy.jsonc",
      "hash": "sha256:..."
    },
    "workspace": {
      "scope": "policy",
      "hash": "sha256:..."
    },
    "findingsHash": "sha256:...",
    "attestationHash": "sha256:..."
  },
  "checksRun": 5,
  "checksSkipped": 0,
  "findings": []
}

Configure policy

Policy config lives under plugins.entries.policy.config.
{
  "plugins": {
    "entries": {
      "policy": {
        "enabled": true,
        "config": {
          "enabled": true,
          "path": "policy.jsonc",
          "workspaceRepairs": false,
          "expectedHash": "sha256:...",
          "expectedAttestationHash": "sha256:...",
        },
      },
    },
  },
}
SettingPurpose
enabledEnable policy checks even before policy.jsonc exists.
workspaceRepairsAllow doctor --fix to edit policy-managed workspace settings.
expectedHashOptional hash-lock for the approved policy artifact.
expectedAttestationHashOptional hash-lock for the last accepted clean policy check.
pathWorkspace-relative location of the policy artifact.
Set plugins.entries.policy.config.enabled to false to disable policy checks for a workspace while leaving the plugin installed.

Accept policy state

Example JSON output:
{
  "ok": true,
  "attestation": {
    "checkedAt": "2026-05-10T20:00:00.000Z",
    "policy": {
      "path": "policy.jsonc",
      "hash": "sha256:..."
    },
    "workspace": {
      "scope": "policy",
      "hash": "sha256:..."
    },
    "findingsHash": "sha256:...",
    "attestationHash": "sha256:..."
  },
  "evidence": {
    "channels": [
      {
        "id": "telegram",
        "provider": "telegram",
        "source": "oc://openclaw.config/channels/telegram",
        "enabled": false
      }
    ],
    "mcpServers": [
      {
        "id": "docs",
        "transport": "stdio",
        "source": "oc://openclaw.config/mcp/servers/docs",
        "command": "npx"
      }
    ],
    "modelProviders": [
      {
        "id": "openai",
        "source": "oc://openclaw.config/models/providers/openai"
      }
    ],
    "modelRefs": [
      {
        "ref": "openai/gpt-5.5",
        "provider": "openai",
        "model": "gpt-5.5",
        "source": "oc://openclaw.config/agents/defaults/model"
      }
    ],
    "network": [
      {
        "id": "browser-private-network",
        "source": "oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork",
        "value": false
      }
    ],
    "gatewayExposure": [
      {
        "id": "gateway-bind",
        "kind": "bind",
        "source": "oc://openclaw.config/gateway/bind",
        "value": "loopback",
        "nonLoopback": false,
        "explicit": true
      }
    ],
    "agentWorkspace": [
      {
        "id": "agents-defaults-workspace-access",
        "kind": "workspaceAccess",
        "source": "oc://openclaw.config/agents/defaults/sandbox/workspaceAccess",
        "scope": "defaults",
        "value": "ro",
        "sandboxMode": "all",
        "sandboxModeSource": "oc://openclaw.config/agents/defaults/sandbox/mode",
        "sandboxEnabled": true,
        "explicit": true
      },
      {
        "id": "agents-defaults-tool-exec",
        "kind": "toolDeny",
        "source": "oc://openclaw.config/tools/deny",
        "scope": "defaults",
        "tool": "exec",
        "denied": true,
        "explicit": true
      }
    ],
    "secrets": [
      {
        "id": "vault",
        "kind": "provider",
        "source": "oc://openclaw.config/secrets/providers/vault",
        "providerSource": "env"
      },
      {
        "id": "oc://openclaw.config/models/providers/openai/apiKey",
        "kind": "input",
        "source": "oc://openclaw.config/models/providers/openai/apiKey",
        "provenance": "secretRef",
        "refSource": "env",
        "refProvider": "vault"
      }
    ],
    "authProfiles": [
      {
        "id": "github",
        "source": "oc://openclaw.config/auth/profiles/github",
        "validMetadata": true,
        "provider": "github",
        "mode": "token"
      }
    ],
    "tools": [
      {
        "id": "deploy",
        "source": "oc://TOOLS.md/tools/deploy",
        "line": 12,
        "risk": "critical",
        "sensitivity": "restricted",
        "capabilities": ["IRREVERSIBLE_EXTERNAL"]
      }
    ]
  },
  "checksRun": 30,
  "checksSkipped": 0,
  "findings": []
}
attestation.policy.hash identifies the authored rule artifact. evidence records the observed OpenClaw state used by the checks, and workspace.hash identifies that evidence payload. findingsHash identifies the exact finding set. checkedAt records when the check ran. attestationHash identifies the stable claim (policy hash, evidence hash, findings hash, and clean/dirty state) and deliberately excludes checkedAt, so the same policy state always produces the same attestation hash. Together these four values form the audit tuple for one policy check. If a gateway or supervisor uses policy to block, approve, or annotate a runtime action, it should record the attestation hash from the last clean check. checkedAt stays in JSON output for audit logs but is not part of the stable hash. Lifecycle for accepting policy state:
  1. Author or review policy.jsonc.
  2. Run openclaw policy check --json.
  3. If clean, record attestation.policy.hash as expectedHash.
  4. Record attestation.attestationHash as expectedAttestationHash.
  5. Re-run openclaw doctor --lint in CI or release gates.
If policy rules change intentionally, update both accepted hashes from a clean check. If only workspace settings change (policy stays the same), typically only expectedAttestationHash changes. Enabling or upgrading agents.workspace rules adds agentWorkspace evidence to the workspace hash and attestation hash; review the new evidence and refresh accepted attestation hashes after enabling. Enabling or upgrading tool posture rules adds toolPosture evidence the same way. openclaw policy watch re-runs the check and reports when current evidence no longer matches expectedAttestationHash:
openclaw policy watch --json
Use --once in CI or scripts that need a single drift evaluation. Without --once, it polls every two seconds by default; use --interval-ms to change the interval.

Findings

Check idFinding
policy/policy-jsonc-missingPolicy is enabled but policy.jsonc is missing.
policy/policy-jsonc-invalidPolicy cannot be parsed or contains malformed rule entries.
policy/policy-hash-mismatchPolicy does not match configured expectedHash.
policy/attestation-hash-mismatchCurrent policy evidence no longer matches the accepted attestation.
policy/policy-conformance-invalidA baseline or checked policy file has invalid comparison syntax.
policy/policy-conformance-missingA checked policy file is missing a rule required by the baseline policy file.
policy/policy-conformance-weakerA checked policy file has a weaker value than the baseline policy file.
policy/channels-denied-providerAn enabled channel matches a channel deny rule.
policy/mcp-denied-serverA configured MCP server is denied by policy.
policy/mcp-unapproved-serverA configured MCP server is outside the allowlist.
policy/models-denied-providerA configured model provider or model ref uses a denied provider.
policy/models-unapproved-providerA configured model provider or model ref is outside the allowlist.
policy/network-private-access-enabledA private-network SSRF escape hatch is enabled when policy denies it.
policy/ingress-dm-policy-unapprovedA channel DM policy is outside the policy allowlist.
policy/ingress-dm-scope-unapprovedsession.dmScope does not match the policy-required DM isolation scope.
policy/ingress-open-groups-deniedA channel group policy is open while policy denies open group ingress.
policy/ingress-group-mention-requiredA channel or group entry disables mention gates while policy requires them.
policy/gateway-non-loopback-bindGateway bind posture permits non-loopback exposure when policy denies it.
policy/gateway-auth-disabledGateway authentication is disabled when policy requires auth.
policy/gateway-rate-limit-missingGateway auth rate-limit posture is not explicit when policy requires it.
policy/gateway-control-ui-insecureGateway Control UI insecure exposure toggles are enabled.
policy/gateway-tailscale-funnelGateway Tailscale Funnel exposure is enabled when policy denies it.
policy/gateway-remote-enabledGateway remote mode is active when policy denies it.
policy/gateway-http-endpoint-enabledA Gateway HTTP API endpoint is enabled while denied by policy.
policy/gateway-http-url-fetch-unrestrictedGateway HTTP URL-fetch input lacks a required URL allowlist.
policy/gateway-node-command-deniedA node command denied by policy is not denied by OpenClaw config.
policy/agents-workspace-access-deniedAgent sandbox mode or workspace access is outside the policy allowlist.
policy/agents-tool-not-deniedAn agent or default config does not deny a tool required by policy.
policy/tools-profile-unapprovedA configured global or per-agent tool profile is outside the allowlist.
policy/tools-fs-workspace-only-requiredFilesystem tools are not configured with workspace-only path posture.
policy/tools-exec-security-unapprovedExec security mode is outside the policy allowlist.
policy/tools-exec-ask-unapprovedExec ask mode is outside the policy allowlist.
policy/tools-exec-host-unapprovedExec host routing is outside the policy allowlist.
policy/tools-elevated-enabledElevated tool mode is enabled when policy denies it.
policy/tools-also-allow-missingA configured alsoAllow list is missing an entry required by policy.
policy/tools-also-allow-unexpectedA configured alsoAllow list includes an entry not expected by policy.
policy/tools-required-deny-missingA global or per-agent tool deny list does not include a required denied tool.
policy/sandbox-mode-unapprovedSandbox mode is outside the policy allowlist.
policy/sandbox-backend-unapprovedSandbox backend is outside the policy allowlist.
policy/sandbox-container-posture-unobservableA container posture rule is enabled for a backend that cannot observe it.
policy/sandbox-container-host-network-deniedA container-backed sandbox or browser uses host network mode.
policy/sandbox-container-namespace-join-deniedA container-backed sandbox or browser joins another container namespace.
policy/sandbox-container-mount-mode-requiredA container-backed sandbox or browser mount is not read-only.
policy/sandbox-container-runtime-socket-mountA container-backed sandbox or browser mount exposes the container runtime socket.
policy/sandbox-container-unconfined-profileContainer sandbox profile is unconfined when policy denies it.
policy/sandbox-browser-cdp-source-range-missingSandbox browser CDP source range is missing when policy requires one.
policy/data-handling-redaction-disabledSensitive logging redaction is disabled when policy requires it.
policy/data-handling-telemetry-content-captureTelemetry content capture is enabled when policy denies it.
policy/data-handling-session-retention-not-enforcedSession retention maintenance is not enforced when policy requires it.
policy/data-handling-session-transcript-memory-enabledSession transcript memory indexing is enabled when policy denies it.
policy/secrets-unmanaged-providerA config SecretRef references a provider not declared under secrets.providers.
policy/secrets-denied-provider-sourceA config secret provider or SecretRef uses a source denied by policy.
policy/secrets-insecure-providerA secret provider opts into insecure posture when policy denies it.
policy/auth-profile-invalid-metadataA config auth profile is missing valid provider or mode metadata.
policy/auth-profile-unapproved-modeA config auth profile mode is outside the policy allowlist.
policy/exec-approvals-missingPolicy requires exec-approvals.json, but the artifact is missing.
policy/exec-approvals-invalidThe configured exec approvals artifact cannot be parsed.
policy/exec-approvals-default-security-unapprovedExec approval defaults use a security mode outside the policy allowlist.
policy/exec-approvals-agent-security-unapprovedA per-agent effective exec approval security mode is outside the allowlist.
policy/exec-approvals-auto-allow-skills-enabledAn exec approval agent implicitly auto-allows skill CLIs when policy denies it.
policy/exec-approvals-allowlist-missingThe approvals allowlist is missing a pattern required by policy.
policy/exec-approvals-allowlist-unexpectedThe approvals allowlist includes a pattern not expected by policy.
policy/tools-missing-risk-levelA governed tool declaration is missing risk metadata.
policy/tools-unknown-risk-levelA governed tool declaration uses an unknown risk value.
policy/tools-missing-sensitivity-tokenA governed tool declaration is missing sensitivity metadata.
policy/tools-missing-ownerA governed tool declaration is missing owner metadata.
policy/tools-unknown-sensitivity-tokenA governed tool declaration uses an unknown sensitivity value.
A finding can include both target (the observed workspace thing that does not conform) and requirement (the authored rule that made it a finding). Both are oc:// address strings today, but the field names describe policy role rather than address format. Example findings:
{
  "checkId": "policy/channels-denied-provider",
  "severity": "error",
  "message": "Channel 'telegram' uses denied provider 'telegram'.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/channels/telegram",
  "target": "oc://openclaw.config/channels/telegram",
  "requirement": "oc://policy.jsonc/channels/denyRules/#0",
  "fixHint": "Telegram is not approved for this workspace."
}
{
  "checkId": "policy/tools-missing-risk-level",
  "severity": "error",
  "message": "TOOLS.md tool 'deploy' has no explicit risk classification.",
  "source": "policy",
  "path": "TOOLS.md",
  "line": 12,
  "ocPath": "oc://TOOLS.md/tools/deploy",
  "target": "oc://TOOLS.md/tools/deploy",
  "requirement": "oc://policy.jsonc/tools/requireMetadata"
}
{
  "checkId": "policy/mcp-unapproved-server",
  "severity": "error",
  "message": "MCP server 'remote' is not in the policy allowlist.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/mcp/servers/remote",
  "target": "oc://openclaw.config/mcp/servers/remote",
  "requirement": "oc://policy.jsonc/mcp/servers/allow"
}
{
  "checkId": "policy/models-unapproved-provider",
  "severity": "error",
  "message": "Model ref 'anthropic/claude-sonnet-4.7' uses unapproved provider 'anthropic'.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/agents/defaults/model/fallbacks/#0",
  "target": "oc://openclaw.config/agents/defaults/model/fallbacks/#0",
  "requirement": "oc://policy.jsonc/models/providers/allow"
}
{
  "checkId": "policy/network-private-access-enabled",
  "severity": "error",
  "message": "Network setting 'browser-private-network' allows private-network access.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork",
  "target": "oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork",
  "requirement": "oc://policy.jsonc/network/privateNetwork/allow"
}
{
  "checkId": "policy/gateway-non-loopback-bind",
  "severity": "error",
  "message": "Gateway bind setting 'gateway-bind' permits non-loopback exposure.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/gateway/bind",
  "target": "oc://openclaw.config/gateway/bind",
  "requirement": "oc://policy.jsonc/gateway/exposure/allowNonLoopbackBind"
}
{
  "checkId": "policy/gateway-node-command-denied",
  "severity": "error",
  "message": "Gateway node command 'system.run' is denied by policy but not denied by OpenClaw config.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/gateway/nodes/denyCommands",
  "target": "oc://openclaw.config/gateway/nodes/denyCommands",
  "requirement": "oc://policy.jsonc/gateway/nodes/denyCommands",
  "fixHint": "Add 'system.run' to gateway.nodes.denyCommands or update policy after review."
}
{
  "checkId": "policy/agents-workspace-access-denied",
  "severity": "error",
  "message": "agents.defaults sandbox workspaceAccess 'rw' is not allowed by policy.",
  "source": "policy",
  "path": "openclaw config",
  "ocPath": "oc://openclaw.config/agents/defaults/sandbox/workspaceAccess",
  "target": "oc://openclaw.config/agents/defaults/sandbox/workspaceAccess",
  "requirement": "oc://policy.jsonc/agents/workspace/allowedAccess"
}

Repair

doctor --lint and policy check are read-only. doctor --fix only edits policy-managed workspace settings when workspaceRepairs is explicitly enabled; otherwise checks report what they would repair and leave settings unchanged. In this version, repair can disable channels denied by channels.denyRules and apply the automatic narrowing repairs listed below. Enable workspaceRepairs only after the policy file has been reviewed, because a valid rule can change workspace config:
  • set tools.elevated.enabled=false when a global policy forbids elevated tools
  • add missing required-deny tool ids to tools.deny or agents.list[].tools.deny when policy requires those tools to be denied
  • set insecure gateway.controlUi.* toggles to false
  • set gateway.mode=local when policy denies remote gateway mode
  • set reported channel ingress groupPolicy paths to allowlist when policy denies open group ingress
  • set reported channel ingress requireMention paths to true when policy requires group mentions
  • set logging.redactSensitive=tools when policy requires sensitive logging redaction
  • set diagnostics.otel.captureContent=false, or diagnostics.otel.captureContent.enabled=false for object-form telemetry capture settings, when policy denies telemetry content capture
Scoped elevated-tools repairs are detect-only. Scoped data-handling repairs are also skipped when the finding reports shared logging or telemetry config, because changing the shared setting would affect more than the scoped policy target. Scoped required-deny repairs are skipped when the finding reports inherited root tools.deny, because adding the required tool to root config would affect more than the scoped policy target. Agent-local required-deny repairs can update the reported agents.list[].tools.deny path. Scoped channel ingress repairs are skipped when the finding reports inherited channels.defaults.*, because changing the shared channel default would affect more than the scoped policy target.
{
  "plugins": {
    "entries": {
      "policy": {
        "config": {
          "workspaceRepairs": true,
        },
      },
    },
  },
}

Exit codes

Command012
policy checkNo findings at the threshold.One or more findings met the threshold.Argument or runtime failure.
policy compareThe policy file is at least as strict as the baseline.The policy file is invalid, missing, or weaker than baseline rules.Argument or runtime failure.
policy watchNo findings and accepted hash is current.Findings exist or accepted attestation is stale.Argument or runtime failure.