Policy: add agent-scoped policy overlays#85817
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 25, 2026, 11:24 AM ET / 15:24 UTC. Summary PR surface: Source +881, Tests +939, Docs +257. Total +2077 across 5 files. Reproducibility: not applicable. this is a new Policy plugin capability, not a reported current-main bug. The PR body does include a concrete after-patch policy-check proof showing the intended scoped/global finding behavior. Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land this as the canonical Policy overlay implementation only after maintainer/security approval of the new grammar and upgrade semantics, with maintainer-owned changelog/release notes handled during landing. Do we have a high-confidence way to reproduce the issue? Not applicable: this is a new Policy plugin capability, not a reported current-main bug. The PR body does include a concrete after-patch policy-check proof showing the intended scoped/global finding behavior. Is this the best way to solve the issue? Unclear pending maintainer approval: the implementation is coherent and covered, but it chooses additive stricter-only policy overlays for a security-sensitive config surface. If maintainers want exception-style weakening of global rules, the product direction should change before merge. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 89aea9b84333. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +881, Tests +939, Docs +257. Total +2077 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Clockwork Crabkin Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
|
PR #85817 adds docs for agent-scoped policy overlays on top of a stacked Policy plugin runtime/docs change. The important surface is By: Gio Della-Libera (@giodl73-repo, acct 2025-09-30) | OpenClaw: 125 PRs, 14 issues, 88 commits/12mo | GitHub: 5305 commits, 150 PRs, 29 issues, 18 reviews Findings [P1] Provenance: introduced by this PR's stacked tool-posture implementation, commit Best-fix verdict: don't merge as-is. The overlay design doc is reasonable, but the carried runtime policy stack has a real false-negative in the tool posture evidence contract. Verification I performed: |
777f1cf to
54b063d
Compare
|
Thanks @galiniliev. I rebased this on current main after #85482/#85795 landed, so the stacked This PR is now the initial implementation for generic agent-scoped policy overlays under I also added exact-list conformance for Focused proof on the rebased head:
Latest commit is signed and rebased on current |
13dc474 to
dfde965
Compare
b3bfc75 to
28d0690
Compare
|
@clawsweeper re-review Cleaned this PR back to policy-only scope. What changed:
Current #85817 diff is only:
Validation on the cleaned policy-only head:
No merge performed. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review Added the requested redacted real policy-check proof to the PR body. What changed:
Note: in this WSL source checkout, No merge performed. |
|
🦞👀 Command router queued. I will update this comment with the next step. Re-review progress:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
Summary
Implements Policy overlays under
scopes.<scopeName>for policy sections whose evidence is attributable to runtime agent ids:toolsandagents.workspace.Scopes are purpose-named policy blocks. The scope name is descriptive only; matching uses selector values inside the block. In this PR every scoped block must declare
agentIds, and those ids are normalized OpenClaw runtime agent ids. A scope without a selector is invalid, and a selector can only carry sections that Policy can evaluate for that selector.Selector rule in this PR:
agentIdstools,agents.workspaceagents.list[]are evaluated against inherited global/default posture.Scope blocks reuse the normal policy section grammar instead of a per-agent mini-language. The overlay model is additive and config-conformance only: top-level
toolsandagents.workspaceclaims still run, while scoped blocks can add stricter claims and emit their own findings. Unsupported scoped sections are rejected rather than ignored. This PR does not add runtime enforcement.Strictness metadata validates scoped overlays: allowlist-subset, denylist-superset, requires-true, requires-false, and exact-list.
tools.denyToolsuses existing group and glob coverage semantics.Example shape:
{ "scopes": { "release-workspace": { "agentIds": ["release-agent", "support-agent"], "agents": { "workspace": { "allowedAccess": ["none", "ro"], "denyTools": ["write", "edit", "apply_patch"] } } }, "release-tools-lockdown": { "agentIds": ["release-agent"], "tools": { "exec": { "allowHosts": ["sandbox"], "requireAsk": ["always"] }, "alsoAllow": { "expected": [] }, "denyTools": ["group:runtime", "group:fs"] } } } }The same agent can appear in multiple scopes when the scopes cover different policy fields. If two scopes set the same field for the same agent, the later value must be equally or more restrictive according to the field metadata.
The adjacent agent CI fixes previously carried in this branch were split out:
Verification
OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-85817-policy-only node scripts/run-vitest.mjs extensions/policy/src/doctor/register.test.ts extensions/policy/src/cli.test.ts -- --reporter=dot-> 2 files, 184 tests passedpnpm exec oxfmt --check docs/cli/policy.md docs/plan/policy-agent-scoped-overlays.md docs/plugins/reference/policy.md extensions/policy/src/doctor/register.ts extensions/policy/src/doctor/register.test.tspnpm exec oxlint extensions/policy/src/doctor/register.ts extensions/policy/src/doctor/register.test.tspnpm docs:check-mdx docs/cli/policy.md docs/plugins/reference/policy.md docs/plan/policy-agent-scoped-overlays.mdgit diff --checkReal behavior proof
Behavior addressed: Policy conformance can express stricter workspace and tool posture claims for named policy scopes that target explicit runtime agent ids without weakening deployment-wide policy claims.
Real environment tested: WSL Ubuntu 24.04 checkout under
/root/src/openclaw-policy-agent-scoped-design, with disposableOPENCLAW_HOME, disposableOPENCLAW_CONFIG_PATH, and a disposable workspace containingpolicy.jsonc.Exact steps or command run after this patch: created a disposable config/workspace, then invoked the policy check command implementation from this source-tree head with
json: trueand the disposable workspace cwd. The normalpnpm openclaw policy check --jsonwrapper attempted an unrelated source rebuild first in this checkout, so this proof calls the same Policy command path directly throughextensions/policy/src/cli.ts.Evidence after fix: the redacted output below shows global/default workspace and tool findings, scoped
release-workspacefindings for both listed agents, and scopedrelease-tools-lockdownfindings only forrelease-agent.Observed result after fix: the policy check returned
ok: falsewith an attestation and expected findings for top-level and scoped requirements. Focused tests also passed: 2 files and 184 tests; formatter, linter, docs MDX, andgit diff --checkpassed.Redacted
policy check --jsonoutput excerpt:{ "ok": false, "attestation": { "checkedAt": "2026-05-25T15:00:41.933Z", "policy": { "path": "policy.jsonc", "hash": "sha256:06c55213e533b5e00b8f99d4c1c62e552a91d78d344213be05c5b333bcf5884f" }, "workspace": { "scope": "policy", "hash": "sha256:dbe4edab363158aa5579067522b4bbffc4f1c11c135628ebce131f1f542810ab" }, "findingsHash": "sha256:56be99850c4d0df97a7e98a1ffa3ff1fd44e07d4ce18ca1196bb30d36bdbee7f", "attestationHash": "sha256:636272a96eccb7c9dadabc635c5c317532c15962929ca2e7b0ac833c30b1d4bc" }, "evidence": { "agentWorkspace": [ { "id": "agents-defaults-workspace-access", "kind": "workspaceAccess", "source": "oc://openclaw.config/agents/defaults/sandbox/workspaceAccess", "scope": "defaults", "value": "rw", "sandboxMode": "all", "sandboxModeSource": "oc://openclaw.config/agents/defaults/sandbox/mode", "sandboxEnabled": true, "explicit": true }, { "id": "release-agent-workspace-access", "kind": "workspaceAccess", "source": "oc://openclaw.config/agents/list/#0/sandbox/workspaceAccess", "scope": "agent", "agentId": "release-agent", "value": "rw", "sandboxMode": "all", "sandboxModeSource": "oc://openclaw.config/agents/list/#0/sandbox/mode", "sandboxEnabled": true, "explicit": true }, { "id": "support-agent-workspace-access", "kind": "workspaceAccess", "source": "oc://openclaw.config/agents/list/#1/sandbox/workspaceAccess", "scope": "agent", "agentId": "support-agent", "value": "ro", "sandboxMode": "all", "sandboxModeSource": "oc://openclaw.config/agents/list/#1/sandbox/mode", "sandboxEnabled": true, "explicit": true } ], "toolPosture": [ { "id": "release-agent-alsoAllow", "kind": "alsoAllow", "source": "oc://openclaw.config/agents/list/#0/tools/alsoAllow", "scope": "agent", "agentId": "release-agent", "entries": [ "read", "gateway" ], "explicit": true }, { "id": "release-agent-deny", "kind": "deny", "source": "oc://openclaw.config/agents/list/#0/tools/deny", "scope": "agent", "agentId": "release-agent", "entries": [], "explicit": false }, { "id": "release-agent-exec-host", "kind": "execHost", "source": "oc://openclaw.config/agents/list/#0/tools/exec/host", "scope": "agent", "agentId": "release-agent", "value": "node", "explicit": true }, { "id": "support-agent-alsoAllow", "kind": "alsoAllow", "source": "oc://openclaw.config/agents/list/#1/tools/alsoAllow", "scope": "agent", "agentId": "support-agent", "entries": [], "explicit": false }, { "id": "support-agent-deny", "kind": "deny", "source": "oc://openclaw.config/agents/list/#1/tools/deny", "scope": "agent", "agentId": "support-agent", "entries": [], "explicit": false }, { "id": "support-agent-exec-host", "kind": "execHost", "source": "oc://openclaw.config/agents/list/#1/tools/exec/host", "scope": "agent", "agentId": "support-agent", "value": "sandbox", "explicit": true }, { "id": "tools-alsoAllow", "kind": "alsoAllow", "source": "oc://openclaw.config/tools/alsoAllow", "scope": "global", "entries": [], "explicit": false }, { "id": "tools-deny", "kind": "deny", "source": "oc://openclaw.config/tools/deny", "scope": "global", "entries": [], "explicit": false }, { "id": "tools-exec-host", "kind": "execHost", "source": "oc://openclaw.config/tools/exec/host", "scope": "global", "value": "node", "explicit": true } ] }, "findings": [ { "checkId": "policy/agents-workspace-access-denied", "message": "agents.defaults sandbox workspaceAccess 'rw' is not allowed by policy.", "ocPath": "oc://openclaw.config/agents/defaults/sandbox/workspaceAccess", "requirement": "oc://policy.jsonc/agents/workspace/allowedAccess" }, { "checkId": "policy/agents-workspace-access-denied", "message": "agent 'release-agent' sandbox workspaceAccess 'rw' is not allowed by policy.", "ocPath": "oc://openclaw.config/agents/list/#0/sandbox/workspaceAccess", "requirement": "oc://policy.jsonc/agents/workspace/allowedAccess" }, { "checkId": "policy/agents-workspace-access-denied", "message": "agent 'release-agent' sandbox workspaceAccess 'rw' is not allowed by policy.", "ocPath": "oc://openclaw.config/agents/list/#0/sandbox/workspaceAccess", "requirement": "oc://policy.jsonc/scopes/release-workspace/agents/workspace/allowedAccess" }, { "checkId": "policy/agents-workspace-access-denied", "message": "agent 'support-agent' sandbox workspaceAccess 'ro' is not allowed by policy.", "ocPath": "oc://openclaw.config/agents/list/#1/sandbox/workspaceAccess", "requirement": "oc://policy.jsonc/scopes/release-workspace/agents/workspace/allowedAccess" }, { "checkId": "policy/tools-exec-host-unapproved", "message": "agent 'release-agent' uses unapproved exec host 'node'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/exec/host", "requirement": "oc://policy.jsonc/tools/exec/allowHosts" }, { "checkId": "policy/tools-exec-host-unapproved", "message": "global tools config uses unapproved exec host 'node'.", "ocPath": "oc://openclaw.config/tools/exec/host", "requirement": "oc://policy.jsonc/tools/exec/allowHosts" }, { "checkId": "policy/tools-exec-host-unapproved", "message": "agent 'release-agent' uses unapproved exec host 'node'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/exec/host", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/exec/allowHosts" }, { "checkId": "policy/tools-also-allow-unexpected", "message": "agent 'release-agent' has unexpected tools.alsoAllow entry 'gateway'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/alsoAllow", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/alsoAllow/expected" }, { "checkId": "policy/tools-also-allow-unexpected", "message": "agent 'release-agent' has unexpected tools.alsoAllow entry 'read'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/alsoAllow", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/alsoAllow/expected" }, { "checkId": "policy/tools-required-deny-missing", "message": "agent 'release-agent' does not deny required tool 'exec'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/deny", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/denyTools" }, { "checkId": "policy/tools-required-deny-missing", "message": "agent 'release-agent' does not deny required tool 'process'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/deny", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/denyTools" }, { "checkId": "policy/tools-required-deny-missing", "message": "agent 'release-agent' does not deny required tool 'code_execution'.", "ocPath": "oc://openclaw.config/agents/list/#0/tools/deny", "requirement": "oc://policy.jsonc/scopes/release-tools-lockdown/tools/denyTools" } ] }What was not tested: live channel, sandbox, ingress, or runtime tool enforcement. This PR intentionally adds Policy conformance and audit grammar, not runtime enforcement.