Skills Config Reference: Schema, Allowlists, and Sandbox Settings
This page covers the full skills.* config schema, agent allowlists, workshop settings, and sandbox environment variable handling. It is intended for developers configuring custom skills and agent visibility in Neura Market.
Read this when
- Configuring skill loading, install, or gating behavior
- Setting per-agent skill visibility
- Adjusting Skill Workshop limits or approval policy
Most skills configuration is located under skills in ~/.openclaw/openclaw.json. Agent-specific visibility settings are under agents.defaults.skills and agents.entries.*.skills.
{
skills: {
allowBundled: ["gemini", "peekaboo"],
load: {
extraDirs: ["~/Projects/agent-scripts/skills"],
allowSymlinkTargets: ["~/Projects/manager/skills"],
watch: true,
},
install: {
preferBrew: true,
nodeManager: "npm",
allowUploadedArchives: false,
},
workshop: {
autonomous: { enabled: false },
allowSymlinkTargetWrites: false,
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 },
},
},
}
Note
For built-in image generation, use
agents.defaults.mediaModels.imageplus the coreimage_generatetool instead ofskills.entries. Skill entries are only for custom or third-party skill workflows.
Loading (skills.load)
-
skills.load.extraDirs(string[]), Extra skill directories to scan, with the lowest priority (below bundled and plugin skills). Paths are expanded with~support. -
skills.load.allowSymlinkTargets(string[]), Trusted real directories that symlinked skill folders may resolve into, even when the symlink is outside the configured root. Useful for intentional sibling-repo setups like<workspace>/skills/manager -> ~/Projects/manager/skills. Keep this list restricted; do not point at broad roots like~or~/Projects. -
skills.load.watch(boolean, default: true), Watch skill folders and refresh the skills snapshot whenSKILL.mdfiles change. Includes nested files under grouped skill roots.
Install (skills.install)
-
skills.install.preferBrew(boolean, default: true), Prefer Homebrew installers whenbrewis available. -
skills.install.nodeManager(npm" | "pnpm" | "yarn" | "bun, default: npm), Node package manager preference for skill installs. This only affects skill installs. The OpenClaw CLI and Gateway runtime require Node because the canonical state store usesnode:sqlite.openclaw setup --node-managerandopenclaw onboard --node-manageracceptnpm,pnpm, orbun; set"yarn"directly in config for Yarn-backed skill installs. -
skills.install.allowUploadedArchives(boolean, default: false), Allow trustedoperator.adminGateway clients to install private zip archives staged throughskills.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.enabled(boolean, default: false), Enables operator-owned install policy. When enabled without a validexeccommand, 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.command(string), Absolute path to the trusted policy executable. OpenClaw runs it without a shell and validates the path before use. -
security.installPolicy.exec.args(string[]), Static arguments passed aftercommand. -
security.installPolicy.exec.timeoutMs(number, default: 10000), Maximum wall-clock runtime for one policy decision. -
security.installPolicy.exec.noOutputTimeoutMs(number, default: timeoutMs), Maximum time without stdout or stderr output before the policy fails closed. -
security.installPolicy.exec.maxOutputBytes(number, default: 1048576), Maximum combined stdout and stderr bytes accepted from the policy process. -
security.installPolicy.exec.env(true), "> Literal environment variables provided to the policy process. -
security.installPolicy.exec.passEnv(string[]), Environment variable names copied from the OpenClaw process into the policy process. Only named variables are passed. -
security.installPolicy.exec.trustedDirs(string[]), Optional allowlist of directories that may contain the policy executable. -
security.installPolicy.exec.allowInsecurePath(boolean, default: false), Bypasses command path ownership and permission checks. Use only when the path is protected by another mechanism. -
security.installPolicy.exec.allowSymlinkCommand(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.
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:
{
"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:
#!/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.allowBundled(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.
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).
-
true, .enabled" type="boolean">falsedisables the skill even when bundled or installed. Thecoding-agentbundled skill is opt-in. Set it totrueand ensure one ofclaude,codex,opencode, or another supported CLI is installed and authenticated. -
true, .apiKey" type='string | { source, provider, id }'> Convenience field for skills that declaremetadata.openclaw.primaryEnv. Supports a plaintext string or a SecretRef:{ source: "env", provider: "default", id: "VAR_NAME" }. -
true, .env" type="Record<string, string>"> Environment variables injected for the agent run. Only injected when the variable is not already set in the process. -
true, .config" type="object"> Optional 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
},
list: [
{ id: "writer" }, // inherits github, weather
{ id: "docs", skills: ["docs-search"] }, // replaces defaults entirely
{ id: "locked-down", skills: [] }, // no skills
],
},
}
-
agents.defaults.skills(string[]), Shared baseline allowlist inherited by agents that omitagents.entries.*.skills. Omit entirely to leave skills unrestricted by default. -
agents.entries.*.skills(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.
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.
Workshop (skills.workshop)
skills.workshop.autonomous.enabled(boolean, default: false), Whentrue, OpenClaw can create pending proposals from durable corrections and can review successful, substantial completed work after the system becomes idle. This can add a background model run after eligible turns. User-prompted skill creation and/learncontinue to work when the setting isfalse.
See Self-learning for eligibility, privacy, cost, proposal-only permissions, and troubleshooting.
-
skills.workshop.approvalPolicy(pending" | "auto, default: auto),autoallows agent-initiated apply, reject, or quarantine without an additional approval prompt.pendingrequires operator approval. -
skills.workshop.allowSymlinkTargetWrites(boolean, default: false), Allow Skill Workshop apply to write through workspace skill symlinks whose real target is already trusted byskills.load.allowSymlinkTargets. Keep this disabled unless generated proposal applies should mutate that shared skill root. -
skills.workshop.maxPending(number, default: 50), Maximum pending and quarantined proposals retained per workspace (allowed range: 1-200). -
skills.workshop.maxSkillBytes(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.
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: ["~/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 allow Workshop apply to modify skills under already trusted symlink targets, opt in separately:
{
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>.envandapiKeyapply to host runs only. Inside a sandbox they have no effect. A skill that depends onGEMINI_API_KEYwill fail withapiKey not configuredunless the sandbox is given the variable separately.
Pass secrets into a Docker sandbox with:
{
agents: {
defaults: {
sandbox: {
docker: {
env: { GEMINI_API_KEY: "your-key-here" },
},
},
},
},
}
Note
Users with Docker daemon access can inspect
sandbox.docker.envvalues through Docker metadata. Use a mounted secret file, a custom image, or another delivery path when that exposure is not acceptable.
Loading order reminder
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
-
Skills reference, What skills are, loading order, gating, and SKILL.md format.
-
Creating skills, Authoring custom workspace skills.
-
Skill Workshop, Proposal queue for agent drafted skills.
-
Self learning, Conservative, opt in proposals from completed work.
-
Slash commands, Native slash command catalog and chat directives.