Skills
Skills config
Most skills configuration lives under skills in
~/.openclaw/openclaw.json. Agent-specific visibility lives under
agents.defaults.skills and agents.entries.*.skills.
{ skills: { allowBundled: ["gemini", "peekaboo"], load: { extraDirs: ["~/path/to/agent-scripts/skills"], allowSymlinkTargets: ["~/path/to/skills"], watch: true, }, install: { preferBrew: true, nodeManager: "npm", allowUploadedArchives: false, }, workshop: { autonomous: { mode: "auto" }, approvalPolicy: "auto", maxPending: 50, maxSkillBytes: 40000, }, entries: { "image-lab": { enabled: true, apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" }, env: { GEMINI_API_KEY: "GEMINI_KEY_HERE" }, }, peekaboo: { enabled: true }, sag: { enabled: false }, }, },}Loading (skills.load)
skills.load.extraDirsstring[]Additional skill directories to scan, at the lowest precedence (below
bundled and plugin skills). Paths are expanded with ~ support.
skills.load.allowSymlinkTargetsstring[]Trusted real target directories that symlinked skill folders may resolve
into, even when the symlink lives outside the configured root. Use this for
intentional sibling-repo layouts such as
<workspace>/skills/manager -> ~/path/to/skills. Keep this list
narrow — do not point at broad roots like ~ or a whole projects directory.
skills.load.watchbooleandefault: trueWatch skill folders and refresh the skills snapshot when SKILL.md files
change. Covers nested files under grouped skill roots.
Install (skills.install)
skills.install.preferBrewbooleandefault: truePrefer Homebrew installers when brew is available.
skills.install.nodeManager"npm" | "pnpm" | "yarn" | "bun"default: "npm"Node package manager preference for skill installs. This only affects skill
installs. Node remains the primary and recommended OpenClaw runtime; Bun 1.4+
with WAL-reset-safe node:sqlite is supported as an explicit runtime opt-in.
openclaw setup --node-manager and openclaw onboard --node-manager accept
npm, pnpm, or bun; set "yarn" directly in config for Yarn-backed skill
installs. Setup preserves this preference unless you pass --node-manager;
fresh configurations default to npm.
skills.install.allowUploadedArchivesbooleandefault: falseAllow trusted operator.admin Gateway clients to install private zip
archives staged through skills.upload.*. Normal ClawHub installs do not
need this setting.
Operator Install Policy (security.installPolicy)
Use security.installPolicy when operators need a trusted local command to
approve or block skill and plugin installs with host-specific policy. The
policy runs after OpenClaw has staged source material and before the install
or update continues. It applies to ClawHub skills, uploaded skills, Git/local
skills, skill dependency installers, and plugin install/update sources.
{ security: { installPolicy: { enabled: true, // Omit targets to cover every supported target. targets: ["skill", "plugin"], exec: { source: "exec", command: "/usr/local/bin/openclaw-install-policy", args: ["--json"], timeoutMs: 10000, noOutputTimeoutMs: 10000, maxOutputBytes: 1048576, passEnv: ["OPENCLAW_STATE_DIR", "PATH"], env: { POLICY_MODE: "strict" }, trustedDirs: ["/usr/local/bin"], }, }, },}security.installPolicy.enabledbooleandefault: falseEnables operator-owned install policy. When enabled without a valid exec
command, installs fail closed.
security.installPolicy.targets("skill" | "plugin")[]Optional target filter. When omitted, policy applies to every supported target so new installs do not unexpectedly fail open.
security.installPolicy.exec.commandstringAbsolute path to the trusted policy executable. OpenClaw runs it without a shell and validates the path before use.
security.installPolicy.exec.argsstring[]Static arguments passed after command.
security.installPolicy.exec.timeoutMsnumberdefault: 10000Maximum wall-clock runtime for one policy decision.
security.installPolicy.exec.noOutputTimeoutMsnumberdefault: timeoutMsMaximum time without stdout or stderr output before the policy fails closed.
security.installPolicy.exec.maxOutputBytesnumberdefault: 1048576Maximum combined stdout and stderr bytes accepted from the policy process.
security.installPolicy.exec.envRecord<string, string>Literal environment variables provided to the policy process.
security.installPolicy.exec.passEnvstring[]Environment variable names copied from the OpenClaw process into the policy process. Only named variables are passed.
security.installPolicy.exec.trustedDirsstring[]Optional allowlist of directories that may contain the policy executable.
The policy command and interpreter script arguments must be direct regular files with trusted ownership, restricted permissions, and verifiable parent directories. Symlinks and insecure paths are rejected.
The policy receives one JSON object on stdin with protocolVersion: 1,
openclawVersion, targetType, targetName, sourcePath, sourcePathKind,
optional structured source, structured origin, and request. It must
write one JSON object on stdout with an allow, warn, or block decision.
warn and block require a non-empty reason; every decision may include a
findings array. Each finding requires non-empty string ruleId and message
fields plus a severity of info, warn, or critical. Optional file and
evidence values must be non-empty strings; a finite numeric line is rounded
down and clamped to the safe-integer range from 1 through Number.MAX_SAFE_INTEGER.
Malformed finding entries are ignored, and
invalid optional fields are omitted. A non-array findings value is treated as
absent. Operator-facing reason and finding text are limited to 1,000 characters.
OpenClaw retains at most 100 normalized findings for display. Only a warn
response with more than 100 valid findings fails closed and cannot be
acknowledged; allow and block retain the first 100. A warning stops the
install before commit. A warn review whose fully rendered notice, including
its title, target, sanitized reason and findings, and recovery guidance, exceeds
the 4,000-character aggregate display limit fails closed without presenting a
partial review. An over-budget block remains terminal with a
bounded denial, while over-budget findings on allow are summarized in bounded
diagnostic output. Interactive CLI
plugin and skill commands ask the operator to type the target name using the
same install anyway or update anyway copy as suspicious ClawHub releases,
then run policy again before continuing. Declined and non-interactive commands
on the direct CLI may use --acknowledge-install-policy-warning as explicit
approval after review for every warning in that command invocation;
every approved warning is re-evaluated before continuing.
The Control UI can review and approve warnings for its plugin install request;
that approval covers every warning in the invocation, and each warning is
still re-evaluated. Other Gateway-backed and automatic installs remain blocked
when they have no operator-confirmation flow. Use an equivalent direct plugin
or skill command to review and approve the warning when one exists. Otherwise,
change security.installPolicy to return allow for the reviewed request,
then retry the managed flow. --force does not approve policy warnings. A block,
non-zero exit, timeout, invalid JSON, non-object response, missing or invalid
protocol version or decision, or missing or empty warn/block reason always
fails closed.
OpenClaw does not execute install policy during normal Gateway startup.
Installs and updates fail closed when policy is enabled but unavailable.
openclaw doctor performs static validation; openclaw doctor --deep
executes a synthetic install probe against the configured command.
Bulk updates apply policy per target: a blocked skill or plugin update fails that target without disabling the policy or skipping later targets in the batch.
Example stdin:
{ "protocolVersion": 1, "openclawVersion": "2026.6.1", "targetType": "skill", "targetName": "weather", "sourcePath": "/var/folders/.../openclaw-skill-clawhub/root", "sourcePathKind": "directory", "source": { "kind": "clawhub", "authority": "openclaw", "mutable": false, "network": true }, "origin": { "type": "clawhub", "registry": "https://clawhub.openclaw.ai", "slug": "weather", "version": "1.0.0" }, "request": { "kind": "skill-install", "mode": "install", "requestedSpecifier": "clawhub:[email protected]" }, "skill": { "installId": "clawhub" }}Minimal policy command:
#!/usr/bin/env node let input = "";process.stdin.setEncoding("utf8");process.stdin.on("data", (chunk) => { input += chunk;});process.stdin.on("end", () => { const request = JSON.parse(input); if (request.targetType === "plugin" && request.source?.kind === "local-path") { process.stdout.write( JSON.stringify({ protocolVersion: 1, decision: "block", reason: "local plugin paths are not approved on this host", }), ); return; } process.stdout.write(JSON.stringify({ protocolVersion: 1, decision: "allow" }));});Bundled skill allowlist
skills.allowBundledstring[]Optional allowlist for bundled skills only. When set, only bundled skills in the list are eligible. Managed, agent-level, and workspace skills are unaffected.
Per-skill entries (skills.entries)
Keys under entries match the skill name by default. If a skill defines
metadata.openclaw.skillKey, use that key instead. Quote hyphenated names
(JSON5 allows quoted keys).
skills.entries.<key>.enabledbooleanfalse disables the skill even when bundled or installed. The
coding-agent bundled skill is opt-in — set it to true and ensure one of
claude, codex, opencode, or another supported CLI is installed and
authenticated.
skills.entries.<key>.apiKeystring | { source, provider, id }Convenience field for skills that declare metadata.openclaw.primaryEnv.
Supports a plaintext string or a SecretRef: { source: "env", provider: "default", id: "VAR_NAME" }.
skills.entries.<key>.envRecord<string, string>Environment variables injected for the agent run. Only injected when the variable is not already set in the process.
skills.entries.<key>.configobjectOptional bag for custom per-skill configuration fields.
Agent allowlists (agents)
Use agent config when you want the same machine/workspace skill roots but a different visible skill set per agent.
{ agents: { defaults: { skills: ["github", "weather"], // shared baseline }, entries: { writer: { default: true }, // inherits github, weather docs: { skills: ["docs-search"] }, // replaces defaults entirely "locked-down": { skills: [] }, // no skills }, },}agents.defaults.skillsstring[]Shared baseline allowlist inherited by agents that omit
agents.entries.*.skills. Omit entirely to leave skills unrestricted by
default.
agents.entries.*.skillsstring[]Explicit final skill set for that agent. Explicit lists replace
inherited defaults — they do not merge. Set to [] to expose no skills for
that agent.
Workshop (skills.workshop)
skills.workshop.autonomous.mode"off" | "propose" | "auto"default: "auto"off disables autonomous capture while keeping the durable-instruction
suggestion nudge. propose creates pending proposals from corrections and
substantial completed work. auto uses normal agent tools for direct per-turn
and weekly Workshop maintenance, without proposal scanning or automatic rollback
snapshots. Immediate foreground repairs still use scanner-gated proposal apply.
User-prompted skill creation,
/learn, and manual learning sessions continue to work in every mode.
See Self-learning for eligibility, privacy, cost, proposal-only permissions, and troubleshooting.
skills.workshop.approvalPolicy"pending" | "auto"default: "auto"auto allows agent-initiated apply, reject, or quarantine without an
additional approval prompt. pending requires operator approval.
skills.workshop.maxPendingnumberdefault: 50Maximum pending and quarantined proposals retained per agent (allowed range: 1-200).
skills.workshop.maxSkillBytesnumberdefault: 40000Maximum proposal body size in bytes (allowed range: 1024-200000). Proposal descriptions are hard-capped at 160 bytes separately, because they appear in discovery and listing output.
See Skill Workshop for the proposal lifecycle, CLI commands, agent tool parameters, and Gateway methods this config controls.
Symlinked skill roots
By default, workspace, project-agent, extra-dir, and bundled skill roots are
containment boundaries. A symlinked skill folder under <workspace>/skills
that resolves outside the root is skipped with a log message.
To allow an intentional symlink layout, declare the trusted target:
{ skills: { load: { extraDirs: ["~/path/to/skills"], allowSymlinkTargets: ["~/path/to/skills"], }, },}With this config, <workspace>/skills/manager -> ~/path/to/skills
is accepted after realpath resolution. extraDirs scans the sibling repo
directly; allowSymlinkTargets preserves the symlinked path for existing
layouts.
Skill Workshop uses each agent's <state-dir>/agents/<agentId>/agent/workshop-skills
containment boundary. It does not use allowSymlinkTargets, and it rejects
symlinked skills that resolve outside that directory.
Managed ~/.openclaw/skills and personal ~/.agents/skills directories
already accept skill-directory symlinks unconditionally (per-skill
SKILL.md containment still applies) — allowSymlinkTargets is only needed
for workspace, extra-dir, and project-agent (<workspace>/.agents/skills)
roots.
Sandboxed skills and env vars
Pass secrets into a Docker sandbox with:
{ agents: { defaults: { sandbox: { docker: { env: { GEMINI_API_KEY: "your-key-here" }, }, }, }, },}Loading order reminder
See Loading order for source precedence, including the per-agent Workshop tier, and Snapshots and refresh for when changes become visible.
Related
What skills are, loading order, gating, and SKILL.md format.
Authoring custom workspace skills.
Proposal queue for agent-drafted skills.
Conservative, opt-in proposals from completed work.
Native slash-command catalog and chat directives.