| name | openclaw-management |
| description | Reference for managing an OpenClaw gateway — the multi-agent runtime distributed as the `openclaw` npm package. Use when the user asks about agents (list, add, configure), skills (how they load, where to put them, symlinks), sandbox (Docker shared container, binds, setupCommand), Discord bot routing (allowFrom, allowBots), gateway service (restart, logs, config validation), or any OpenClaw config edit. Install-specific details (bot roster, host paths, custom URLs) live in CLAUDE.md — this skill is about OpenClaw itself. |
OpenClaw Management
Reference for operating an OpenClaw gateway from outside. Assumes the
openclaw npm package is globally installed and a systemd user unit is
running the gateway. All commands are safe to run as the owning Unix
user; the gateway itself does not need root.
1. Locating this install
npm ls -g openclaw --depth 0 2>&1 | tail -3
systemctl --user status openclaw-gateway --no-pager | head -6
ls ~/.openclaw/
Key paths (conventions — exact hostnames/users vary per install, which is
what CLAUDE.md documents):
| Path | Role |
|---|
~/.openclaw/openclaw.json | Top-level config (schema-validated). |
~/.openclaw/openclaw.json.bak* | Auto-backups (last 5). |
~/.openclaw/skills/ | Central skill dir — auto-loaded for every agent. |
~/.openclaw/workspace[-<id>]/ | Per-agent workspace. workspace/ = main. |
~/.openclaw/sandbox/ | Docker build + container registry. |
~/.openclaw/agents/<id>/agent/ | Per-agent model + auth profiles. |
~/.openclaw/logs/config-audit.jsonl | Only tracks openclaw-native writes. |
~/.openclaw/logs/config-health.json | lastObservedSuspiciousSignature should be null. |
2. Service control
systemctl --user status openclaw-gateway
systemctl --user restart openclaw-gateway
journalctl --user -u openclaw-gateway -f
journalctl --user -u openclaw-gateway -n 100 --no-pager
If the gateway is failing to start, it almost always means the config
is invalid. The gateway validates on boot and exits 0 (clean) on
schema failure to avoid a crashloop, which makes this silent unless you
read the journal. Always check logs first.
If systemctl --user errors with "Failed to connect to user scope bus",
export XDG_RUNTIME_DIR=/run/user/$(id -u) first.
3. Config editing rules (hard-learned)
openclaw.json
Edit in place; the gateway backs up to openclaw.json.bak* automatically
on each write. After any edit, restart the gateway and scan the journal
for error / invalid / suspicious entries — validation failures
are not reported to stdout of the editor.
Never check in credentials: the file has channels.discord.accounts.*.token
and similar inline secrets. .gitignore the whole dir.
Direct vs native writes
- Writes via
openclaw CLI are logged to logs/config-audit.jsonl.
- Direct file edits (any editor) are not audited — the health log
picks them up only if they alter cryptographic signatures of tracked
sections. Prefer
openclaw ... commands when the CLI supports the
operation; only hand-edit for structures the CLI doesn't expose (like
nested Discord account policy).
Discord allowFrom — account-level only
"channels": {
"discord": {
"accounts": {
"<bot>": {
"token": "...",
"groupPolicy": "allowlist",
"dmPolicy": "allowlist",
"allowFrom": [
"<discord-user-id>", ...
],
"allowBots": "mentions"
}
}
}
}
Never put allowFrom inside a guild object — schema refuses to load
and ALL bots go offline. Same for allowBots: must be at account level.
For bot-to-bot conversations (e.g. narrator addressing characters), the
receiver needs both allowBots: "mentions" and the sender's Discord
ID in allowFrom.
4. Agents
openclaw agents list
openclaw agents list --json
openclaw agents add <id>
Per-agent config shape (inside agents.list[]):
{
"id": "<name>",
"name": "<Display Name>",
"workspace": "/absolute/path/to/workspace",
"skills": ["agent-runtime", "..."],
"tools": { "alsoAllow": ["message", ...] },
"sandbox": { "mode": "all" }
}
Each workspace starts from
<npm-global>/lib/node_modules/openclaw/docs/reference/templates/AGENTS.md.
If the template is updated, existing workspaces keep their version —
you update them deliberately.
5. Skills
Resolution order
At skill-resolution time, OpenClaw merges these sources by skill name:
openclaw-bundled — shipped with the package.
openclaw-extra — skills.load.extraDirs in openclaw.json.
openclaw-managed — skills.managedSkillsDir (default ~/.openclaw/skills/).
agents-skills-personal — ~/.agents/skills/.
agents-skills-project — $workspace/.agents/skills/.
openclaw-workspace — $workspace/skills/ (wins last).
Later sources win on name conflict. Central skills go in ~/.openclaw/skills/
(source openclaw-managed). Only put a skill in $workspace/skills/ if
it is truly agent-local and not a duplicate.
Per-agent allowlist
agents.list[].skills is a filter, not an installer. A name must both
resolve from a source above AND appear in the agent's allowlist to be
callable by that agent. Dropping from the allowlist hides the skill;
dropping from the source dir makes it unavailable fleet-wide.
Skill directory shape
<name>/
├── SKILL.md # Required. YAML frontmatter + body.
├── scripts/ # Optional. Executables the bot invokes via `exec`.
├── references/ # Optional. Docs the skill body can point bots at.
└── ...
SKILL.md frontmatter (minimum):
---
name: my-skill
description: One-sentence trigger — mention the keywords a bot would look for. The runtime routes skills to bots based on this description.
---
Symlink-escape protection
The loader rejects any $workspace/skills/<name> that is a symlink
resolving outside that workspace's skills root. Rejected skills are
silently unavailable to the agent — journalctl shows lines like
Skipping escaped skill path outside its configured root: reason=symlink-escape.
Rule: do NOT symlink or copy skills into per-workspace skill dirs. Skills
in ~/.openclaw/skills/ are already loaded for every agent via
openclaw-extra + openclaw-managed.
Inspecting skills
openclaw skills list
openclaw skills list --json
openclaw skills info <name>
openclaw skills search <query>
source=openclaw-managed / openclaw-extra = central (desired).
source=openclaw-workspace = per-agent (only correct when truly local).
Adding a new skill to the central dir
- Create
~/.openclaw/skills/<name>/SKILL.md with frontmatter.
- Add
<name> to each agent's agents.list[<id>].skills allowlist
that should get access (see §4).
- Restart the gateway.
- Verify with
openclaw skills info <name> — must show ✓ Ready.
6. Sandbox (Docker)
Sandbox config: agents.defaults.sandbox (override per agent).
{
"backend": "docker",
"scope": "shared",
"mode": "all",
"image": "openclaw-sandbox:bookworm-slim",
"dangerouslyAllowExternalBindSources": true,
"docker": {
"binds": [ { "source": "<host>", "target": "<inside>", "readonly": true } ],
"setupCommand": "<shell snippet run once per container boot>"
}
}
Conventions:
scope: shared = one container serves every agent marked mode: all
(container name usually openclaw-sbx-shared, image tag includes any
extra layers the setup script installs — e.g. Node, mcporter).
/workspace (rw) and /agent (ro) target paths are reserved —
don't bind over them.
dangerouslyAllowExternalBindSources: true is required to bind-mount
anything under $HOME (normally blocked by policy). Use it for the
central skills dir; otherwise keep binds under /srv / /var.
Rebuild the container
Container rebuilds on the next agent turn after it's gone:
docker rm -f openclaw-sbx-shared
You need this for changes to setupCommand or binds. Per-agent
AGENTS.md / skill file edits do NOT require a rebuild (they're read
from the workspace bind mount every invocation).
7. Canonical path conventions
Write AGENTS.md / SKILL.md paths so the same string works on host and
inside the sandbox:
| Thing | Canonical form | Why it works |
|---|
| Skill files / scripts | ~/.openclaw/skills/<name>/... | Host resolves to $HOME/.openclaw/skills. Sandbox: the sandbox user's home contains a symlink pointing at the readonly setupCommand-created bind mount (often /tmp/openclaw-skills). |
| Agent workspace (from sandbox) | /workspace/... | Bound rw to the agent's host workspace. |
Attachment media: arg (message tool) | /absolute/host/path/... | The message tool resolves on the host — sandbox paths won't work. |
| MCP config | ~/.openclaw/config/mcporter.json | Written by setupCommand. |
Anti-patterns
~ inside strings passed to exec: tilde doesn't always expand
(depends on whether the runtime spawns via bash or execve). Write
absolute paths in scripts meant for exec — tildes are fine only in
interactive docs and top-level bash.
- Hard-coding sandbox user names that differ from the install (e.g.
/home/openclaw/... when the user is actually sandbox).
sys.path.insert(0, '<host-path>') inside a sandbox exec — use the
sandbox-visible absolute path instead.
$workspace/skills/<name> symlinked to ~/.openclaw/skills/<name> —
the symlink-escape protection rejects it (§5).
8. Diagnostics
30-second smoke test
systemctl --user status openclaw-gateway --no-pager | head -4
openclaw config get agents.list | head -5
journalctl --user -u openclaw-gateway --since "2 min ago" --no-pager | \
grep -iE "error|invalid|suspicious|symlink-escape|denied" | head
openclaw skills list --json | python3 -c '
import json, sys
avail = {s["name"] for s in json.load(sys.stdin)["skills"]}
import pathlib
cfg = json.loads(pathlib.Path("~/.openclaw/openclaw.json").expanduser().read_text())
for a in cfg["agents"]["list"]:
missing = [s for s in a.get("skills", []) if s not in avail]
if missing:
print(a["id"], "missing:", missing)
'
After editing openclaw.json
Always:
systemctl --user restart openclaw-gateway
- Wait ~3 s
- Scan
journalctl for validation errors
openclaw config get <your edit path> to confirm it round-trips
If validation fails, the gateway stays down — DO NOT leave a broken
config in place. Restore from openclaw.json.bak (or an earlier
numbered bak) and diff to see what you broke.
Per-agent model verification
When an agent seems slow or falls back unexpectedly:
journalctl --user -u openclaw-gateway --since "5 min ago" --no-pager | \
grep -iE "<agent-id>.*(model|fallback|timeout|requested=)"
Look for fallback_model reason=timeout and requested=... — the
requested model is the agent's configured primary, and the fallback
chain shows what the gateway tried next.
9. Common pitfalls
- Gateway silently disabled itself → config validation failed; check
journalctl immediately after every edit.
- New skill added but agent can't see it → forgot to add its name to
the agent's
skills: [...] allowlist.
- Skill disappears intermittently → a
$workspace/skills/<name>
symlink is tripping symlink-escape. Delete the symlink; keep the
skill only in ~/.openclaw/skills/.
- Bot answers but takes forever → LLM provider timeout hitting
fallback chain. Check the requested primary is still up (provider
status page) before changing config.
- Two bots run but a third is offline → usually a single bad DM
policy edit (e.g.
allowBots misplaced inside a guild). Validate
the config diff, fix the misplaced field, restart.
- Container setup changes don't stick → you forgot
docker rm -f openclaw-sbx-shared after editing setupCommand.