Skip to content

feat: trusted-operator security defaults for OpenClaw 2026.5.x#40

Merged
TimBeyer merged 9 commits into
mainfrom
feat/openclaw-security-defaults
May 13, 2026
Merged

feat: trusted-operator security defaults for OpenClaw 2026.5.x#40
TimBeyer merged 9 commits into
mainfrom
feat/openclaw-security-defaults

Conversation

@TimBeyer
Copy link
Copy Markdown
Owner

Summary

  • Recent upstream OpenClaw releases split exec policy into multiple independent layers (tools.exec.* config + ~/.openclaw/exec-approvals.json host file + tools.elevated.* + commands.*). On a fresh clawctl create, agents could not exec, install packages, or use /config — bootstrap left an unusable gateway.
  • Root cause: the one-password capability was hardcoding defaults.security=deny + agents.main.security=allowlist in the approvals file. Under the new per-agent-override-wins rule, this silently capped effective policy at allowlist even when the gateway config asked for full.
  • This PR applies a trusted-operator default profile (clawctl owns host+VM, gateway is local/Tailscale-only) so a fresh bootstrap produces a working gateway with no manual post-install editing.

What changed

  1. one-password capability writes permissive approvals (security=full everywhere). The op pattern stays in the allowlist as forward-compat documentation for operators who later flip to restrictive mode.
  2. bootstrap.ts post-onboard now:
    • Runs openclaw exec-policy preset yolo.
    • Sets commands.config=true (enable the /config slash).
    • Sets tools.elevated.enabled=true and auto-derives tools.elevated.allowFrom.<channel> from each channel's allowFrom.
    • Sets commands.ownerAllowFrom to <channel>:<id> from the same map — required separately because commands.config gates the slash and commands.ownerAllowFrom gates the sender.
    • Defaults agents.defaults.sandbox.mode=off (was opt-in). Flip back on with agent.sandbox: true.
  3. Typed surface: new agent.elevated.allowFrom field lets operators override the auto-derived elevated map without falling back to the openclaw passthrough.
  4. Pre-existing bug: telegram.allowFrom was declared type: "text" but the runtime accepts arrays and tests use arrays; the derived z.string() blocked any headless config with sender IDs. New stringList field type fixes the validation.
  5. Docs: docs/config-reference.md documents the trusted-operator defaults, the new agent.elevated.allowFrom field, and the new sandbox default.

The full schema map and design rationale are in tasks/2026-05-14_0030_openclaw-security-defaults/TASK.md.

Test plan

  • bun test (266 pass, 10 skip, 0 fail)
  • bun run lint
  • E2E: bootstrap fresh sam VM via vm-bootstrap.json (zai provider, Telegram channel, Tailscale serve, 1Password secrets)
  • Verify all keys: tools.exec.security=full, commands.config=true, tools.elevated.enabled=true, tools.elevated.allowFrom.telegram auto-derived, commands.ownerAllowFrom=["telegram:<id>"], agents.defaults.sandbox.mode=off
  • Verify openclaw exec-policy show reports effective security=full, ask=off
  • Verify openclaw doctor no longer flags the command-owner warning
  • Verify idempotent re-apply (clawctl-dev create against the same instance is a no-op for security config)

Follow-ups (separate PRs)

  • Wizard editing UI for the new stringList field type (currently renders read-only; the existing wizard never produced valid arrays either, so no regression).
  • Investigate FailoverError: No API key found for provider "zai" during first-run bootstrap-prompt — looks like a timing issue between auth-profile patching and the prompt send. Bootstrap completes regardless.

🤖 Generated with Claude Code

TimBeyer and others added 9 commits May 14, 2026 00:31
Adopt a trusted-operator default config profile so a fresh clawctl
create produces a working gateway without manual post-bootstrap edits.
Recent upstream OpenClaw split sandbox into multiple independent
policy layers; we currently only configure two of them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase A introspection of a live VM. Real schema differs from web
research in important ways: tools.exec.security is the primary gate,
~/.openclaw/exec-approvals.json is a parallel host-side policy that
intersects with the config policy, and the 1Password capability
hardcodes defaults.security=deny + agents.main.security=allowlist —
which is the proximate cause of the observed exec denial.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The capability previously wrote security=deny defaults and
agents.main.security=allowlist with just op in the allowlist. After
the recent upstream policy-intersection model where per-agent overrides
beat config requests, this silently blocked all exec on a fresh
clawctl install — only the op binary ran, everything else returned
security=deny.

clawctl's trust model is single-trusted-operator (same person owns
host and VM, gateway is reachable only over localhost/Tailscale), so
this file is now clawctl-managed in the permissive direction:
security=full everywhere. The op allowlist entry stays as
forward-compatible documentation for operators who later flip to
restrictive mode via openclaw approvals or by editing the file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Post-onboard now runs \`openclaw exec-policy preset yolo\` (sets
tools.exec.security=full and matching approvals defaults) and writes:

- commands.config=true        — agent may use the /config slash command
- tools.elevated.enabled=true — privileged surfaces available
- tools.elevated.allowFrom.<channel> — auto-derived from each channel's
  allowFrom list, so the operator never repeats IDs

Sandbox now defaults to off (was opt-in). Trusted-operator model:
clawctl owns the host+VM, sandbox-as-protection adds friction without
buying isolation we don't already have. Flip via \`agent.sandbox: true\`
if desired.

New typed field \`agent.elevated.allowFrom\` lets operators override the
auto-derived elevated allowFrom map. Channel-derived defaults are the
common case, so most operators won't need this.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover what clawctl writes to the gateway config (tools.exec.security,
commands.config, tools.elevated.*) and the auto-derivation of
elevated.allowFrom from channel allowFrom. Document the new
agent.elevated.allowFrom field and the new sandbox default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
telegram.allowFrom was declared as type "text" but the runtime accepts
arrays (channels.ts postCommands.run uses Array.isArray) and the canonical
form in configs and tests is array. The derived zod schema z.string()
rejected array values during loadConfig validation, blocking any
headless config that listed allowFrom IDs.

New "stringList" field type derives to z.array(z.string()). Wizard
editing for this type is not wired up yet (the field still renders
read-only); the existing wizard never produced valid arrays anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
commands.config=true gates whether the /config slash command exists,
but commands.ownerAllowFrom gates whether *this sender* is allowed to
call it (and other owner-only commands: /diagnostics,
/export-trajectory, exec approvals). Doctor warns about an empty
ownerAllowFrom even after we enable commands.config.

We reuse the channel-derived allowFrom map: the senders the operator
trusted at the channel layer are the same humans they want as owners.
Flatten to "<channel>:<id>" strings, which is the format
commands.ownerAllowFrom expects.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E2E verified on a fresh sam instance: tools.exec.security=full,
commands.config=true, tools.elevated.enabled=true,
commands.ownerAllowFrom auto-derived as telegram:<id>, sandbox off,
1Password no longer overrides defaults. openclaw doctor no longer
flags the command-owner warning. Re-apply is idempotent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TimBeyer TimBeyer merged commit 9c04a34 into main May 13, 2026
4 checks passed
@TimBeyer TimBeyer deleted the feat/openclaw-security-defaults branch May 13, 2026 23:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant