mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-15 11:46:05 +00:00
Source-grounded rewrite of 529 published docs pages with per-unit information-loss verification: 1,713 factual corrections cited to src/**, generated surfaces regenerated, frontmatter titles preserved for i18n, release notes pages untouched. All docs gates green. Closes #100141
485 lines
16 KiB
Markdown
485 lines
16 KiB
Markdown
---
|
|
title: "Skills config"
|
|
sidebarTitle: "Skills config"
|
|
summary: "Full reference for the skills.* config schema, agent allowlists, workshop settings, and sandbox env var handling."
|
|
read_when:
|
|
- Configuring skill loading, install, or gating behavior
|
|
- Setting per-agent skill visibility
|
|
- Adjusting Skill Workshop limits or approval policy
|
|
---
|
|
|
|
Most skills configuration lives under `skills` in
|
|
`~/.openclaw/openclaw.json`. Agent-specific visibility lives under
|
|
`agents.defaults.skills` and `agents.list[].skills`.
|
|
|
|
```json5
|
|
{
|
|
skills: {
|
|
allowBundled: ["gemini", "peekaboo"],
|
|
load: {
|
|
extraDirs: ["~/Projects/agent-scripts/skills"],
|
|
allowSymlinkTargets: ["~/Projects/manager/skills"],
|
|
watch: true,
|
|
watchDebounceMs: 250,
|
|
},
|
|
install: {
|
|
preferBrew: true,
|
|
nodeManager: "npm",
|
|
allowUploadedArchives: false,
|
|
},
|
|
workshop: {
|
|
autonomous: { enabled: false },
|
|
allowSymlinkTargetWrites: false,
|
|
approvalPolicy: "pending",
|
|
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 },
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
<Note>
|
|
For built-in image generation, use `agents.defaults.imageGenerationModel`
|
|
plus the core `image_generate` tool instead of `skills.entries`. Skill
|
|
entries are for custom or third-party skill workflows only.
|
|
</Note>
|
|
|
|
## Loading (`skills.load`)
|
|
|
|
<ParamField path="skills.load.extraDirs" type="string[]">
|
|
Additional skill directories to scan, at the lowest precedence (below
|
|
bundled and plugin skills). Paths are expanded with `~` support.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.load.allowSymlinkTargets" type="string[]">
|
|
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 -> ~/Projects/manager/skills`. Keep this list
|
|
narrow — do not point at broad roots like `~` or `~/Projects`.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.load.watch" type="boolean" default="true">
|
|
Watch skill folders and refresh the skills snapshot when `SKILL.md` files
|
|
change. Covers nested files under grouped skill roots.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.load.watchDebounceMs" type="number" default="250">
|
|
Debounce window for skill watcher events in milliseconds.
|
|
</ParamField>
|
|
|
|
## Install (`skills.install`)
|
|
|
|
<ParamField path="skills.install.preferBrew" type="boolean" default="true">
|
|
Prefer Homebrew installers when `brew` is available.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.install.nodeManager" type='"npm" | "pnpm" | "yarn" | "bun"' default='"npm"'>
|
|
Node package manager preference for skill installs. This only affects skill
|
|
installs — the Gateway runtime should still use Node (Bun is not
|
|
recommended for WhatsApp/Telegram). `openclaw setup --node-manager` and
|
|
`openclaw onboard --node-manager` accept `npm`, `pnpm`, or `bun`; set
|
|
`"yarn"` directly in config for Yarn-backed skill installs.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.install.allowUploadedArchives" type="boolean" default="false">
|
|
Allow trusted `operator.admin` Gateway clients to install private zip
|
|
archives staged through `skills.upload.*`. Normal ClawHub installs do not
|
|
need this setting.
|
|
</ParamField>
|
|
|
|
## 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.
|
|
|
|
```json5
|
|
{
|
|
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"],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
<ParamField path="security.installPolicy.enabled" type="boolean" default="false">
|
|
Enables operator-owned install policy. When enabled without a valid `exec`
|
|
command, installs fail closed.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.targets" type='("skill" | "plugin")[]'>
|
|
Optional target filter. When omitted, policy applies to every supported
|
|
target so new installs do not unexpectedly fail open.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.command" type="string">
|
|
Absolute path to the trusted policy executable. OpenClaw runs it without a
|
|
shell and validates the path before use.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.args" type="string[]">
|
|
Static arguments passed after `command`.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.timeoutMs" type="number" default="10000">
|
|
Maximum wall-clock runtime for one policy decision.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.noOutputTimeoutMs" type="number" default="timeoutMs">
|
|
Maximum time without stdout or stderr output before the policy fails
|
|
closed.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.maxOutputBytes" type="number" default="1048576">
|
|
Maximum combined stdout and stderr bytes accepted from the policy process.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.env" type="Record<string, string>">
|
|
Literal environment variables provided to the policy process.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.passEnv" type="string[]">
|
|
Environment variable names copied from the OpenClaw process into the
|
|
policy process. Only named variables are passed.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.trustedDirs" type="string[]">
|
|
Optional allowlist of directories that may contain the policy executable.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.allowInsecurePath" type="boolean" default="false">
|
|
Bypasses command path ownership and permission checks. Use only when the
|
|
path is protected by another mechanism.
|
|
</ParamField>
|
|
|
|
<ParamField path="security.installPolicy.exec.allowSymlinkCommand" type="boolean" default="false">
|
|
Allows the configured command path to be a symlink. The resolved target
|
|
must still satisfy the other path checks. Interpreter script arguments must
|
|
be direct regular files, not symlinks.
|
|
</ParamField>
|
|
|
|
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: `{ "protocolVersion": 1, "decision": "allow" }`
|
|
or `{ "protocolVersion": 1, "decision": "block", "reason": "..." }`. Non-zero
|
|
exit, timeout, malformed JSON, missing fields, or unsupported protocol
|
|
versions fail 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:
|
|
|
|
```json
|
|
{
|
|
"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:weather@1.0.0"
|
|
},
|
|
"skill": {
|
|
"installId": "clawhub"
|
|
}
|
|
}
|
|
```
|
|
|
|
Minimal policy command:
|
|
|
|
```js
|
|
#!/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
|
|
|
|
<ParamField path="skills.allowBundled" type="string[]">
|
|
Optional allowlist for **bundled** skills only. When set, only bundled
|
|
skills in the list are eligible. Managed, agent-level, and workspace
|
|
skills are unaffected.
|
|
</ParamField>
|
|
|
|
## 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).
|
|
|
|
<ParamField path="skills.entries.<key>.enabled" type="boolean">
|
|
`false` 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.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.entries.<key>.apiKey" type='string | { 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" }`.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.entries.<key>.env" type="Record<string, string>">
|
|
Environment variables injected for the agent run. Only injected when the
|
|
variable is not already set in the process.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.entries.<key>.config" type="object">
|
|
Optional bag for custom per-skill configuration fields.
|
|
</ParamField>
|
|
|
|
## Agent allowlists (`agents`)
|
|
|
|
Use agent config when you want the same machine/workspace skill roots but a
|
|
different visible skill set per agent.
|
|
|
|
```json5
|
|
{
|
|
agents: {
|
|
defaults: {
|
|
skills: ["github", "weather"], // shared baseline
|
|
},
|
|
list: [
|
|
{ id: "writer" }, // inherits github, weather
|
|
{ id: "docs", skills: ["docs-search"] }, // replaces defaults entirely
|
|
{ id: "locked-down", skills: [] }, // no skills
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
<ParamField path="agents.defaults.skills" type="string[]">
|
|
Shared baseline allowlist inherited by agents that omit
|
|
`agents.list[].skills`. Omit entirely to leave skills unrestricted by
|
|
default.
|
|
</ParamField>
|
|
|
|
<ParamField path="agents.list[].skills" type="string[]">
|
|
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.
|
|
</ParamField>
|
|
|
|
<Warning>
|
|
Agent skill allowlists are a visibility and loading filter for OpenClaw
|
|
skill discovery, prompts, slash-command discovery, sandbox sync, and skill
|
|
snapshots. They are not a shell-time authorization boundary. If an agent
|
|
can run host `exec`, that shell can still run external clients or read
|
|
host files that are visible to the execution user, including MCP client
|
|
registries such as `~/.openclaw/skills/config/mcporter.json`. For
|
|
per-agent MCP isolation, combine skill allowlists with sandbox/OS-user
|
|
isolation, deny or tightly allowlist host exec, and prefer per-agent
|
|
credentials at the MCP server.
|
|
</Warning>
|
|
|
|
## Workshop (`skills.workshop`)
|
|
|
|
<ParamField path="skills.workshop.autonomous.enabled" type="boolean" default="false">
|
|
When `true`, agents can create pending proposals from durable conversation
|
|
signals after successful turns. User-prompted skill creation always goes
|
|
through Skill Workshop regardless of this setting.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.workshop.approvalPolicy" type='"pending" | "auto"' default='"pending"'>
|
|
`pending` requires operator approval before agent-initiated apply, reject,
|
|
or quarantine. `auto` allows those actions without approval.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.workshop.allowSymlinkTargetWrites" type="boolean" default="false">
|
|
Allow Skill Workshop apply to write through workspace skill symlinks whose
|
|
real target is already trusted by `skills.load.allowSymlinkTargets`. Keep
|
|
this disabled unless generated proposal applies should mutate that shared
|
|
skill root.
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.workshop.maxPending" type="number" default="50">
|
|
Maximum pending and quarantined proposals retained per workspace (allowed
|
|
range: 1-200).
|
|
</ParamField>
|
|
|
|
<ParamField path="skills.workshop.maxSkillBytes" type="number" default="40000">
|
|
Maximum 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.
|
|
</ParamField>
|
|
|
|
See [Skill Workshop](/tools/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:
|
|
|
|
```json5
|
|
{
|
|
skills: {
|
|
load: {
|
|
extraDirs: ["~/Projects/manager/skills"],
|
|
allowSymlinkTargets: ["~/Projects/manager/skills"],
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
With this config, `<workspace>/skills/manager -> ~/Projects/manager/skills`
|
|
is accepted after realpath resolution. `extraDirs` scans the sibling repo
|
|
directly; `allowSymlinkTargets` preserves the symlinked path for existing
|
|
layouts.
|
|
|
|
Skill Workshop apply does not write through those symlinks by default. To
|
|
let Workshop apply mutate skills under already-trusted symlink targets, opt
|
|
in separately:
|
|
|
|
```json5
|
|
{
|
|
skills: {
|
|
load: {
|
|
allowSymlinkTargets: ["~/Projects/manager/skills"],
|
|
},
|
|
workshop: {
|
|
allowSymlinkTargetWrites: true,
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
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
|
|
|
|
<Warning>
|
|
`skills.entries.<skill>.env` and `apiKey` apply to **host** runs only.
|
|
Inside a sandbox they have no effect — a skill that depends on
|
|
`GEMINI_API_KEY` will fail with `apiKey not configured` unless the sandbox
|
|
is given the variable separately.
|
|
</Warning>
|
|
|
|
Pass secrets into a Docker sandbox with:
|
|
|
|
```json5
|
|
{
|
|
agents: {
|
|
defaults: {
|
|
sandbox: {
|
|
docker: {
|
|
env: { GEMINI_API_KEY: "your-key-here" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
<Note>
|
|
Users with Docker daemon access can inspect `sandbox.docker.env` values
|
|
through Docker metadata. Use a mounted secret file, a custom image, or
|
|
another delivery path when that exposure is not acceptable.
|
|
</Note>
|
|
|
|
## Loading order reminder
|
|
|
|
```text
|
|
workspace/skills (highest)
|
|
workspace/.agents/skills
|
|
~/.agents/skills
|
|
~/.openclaw/skills
|
|
bundled skills
|
|
skills.load.extraDirs (lowest)
|
|
```
|
|
|
|
Changes to skills and config take effect on the next new session when the
|
|
watcher is enabled, or on the next agent turn when the watcher detects a
|
|
change.
|
|
|
|
## Related
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Skills reference" href="/tools/skills" icon="puzzle-piece">
|
|
What skills are, loading order, gating, and SKILL.md format.
|
|
</Card>
|
|
<Card title="Creating skills" href="/tools/creating-skills" icon="hammer">
|
|
Authoring custom workspace skills.
|
|
</Card>
|
|
<Card title="Skill Workshop" href="/tools/skill-workshop" icon="flask">
|
|
Proposal queue for agent-drafted skills.
|
|
</Card>
|
|
<Card title="Slash commands" href="/tools/slash-commands" icon="terminal">
|
|
Native slash-command catalog and chat directives.
|
|
</Card>
|
|
</CardGroup>
|