Prompt-based rules don't work. You tell your AI agent "don't run docker stop" in a system prompt and it ignores it when it thinks it knows better. Guardian fixes this by intercepting tool calls before they execute and blocking the ones you don't allow.
It's an OpenClaw plugin. One file, no dependencies, no external services.
Protect your workspace from destructive commands
{
"id": "rm-workspace",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*(sudo\\s+)?rm\\s+-(r|rf|fr)\\s+.*/clawd",
"field": "command",
"blockMessage": "π‘οΈ rm -rf blocked on workspace. Use trash instead."
}Make tools read-only β let the agent read emails but block write, forward, and delete:
{
"id": "email-readonly",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*himalaya\\s+(message\\s+)?(write|forward|delete|copy|save)",
"field": "command",
"blockMessage": "π‘οΈ Email is read-only. Ask the human to send/forward/delete."
}Protect files from edits β prevent the agent from rewriting its own identity:
{
"id": "soul-protect",
"tool": "Write",
"pattern": "SOUL\\.md$",
"field": "file_path",
"blockMessage": "π‘οΈ SOUL.md is protected. Changes require human approval."
}{
"id": "soul-protect-edit",
"tool": "Edit",
"pattern": "SOUL\\.md$",
"field": "file_path",
"blockMessage": "π‘οΈ SOUL.md is protected. Changes require human approval."
}Force managed paths β block raw docker commands so the agent uses your stack manager:
{
"id": "docker-mutate",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*(sudo\\s+)?docker\\s+(stop|start|rm|kill|restart|pull|build|compose|run)",
"field": "command",
"managedPath": "Use Komodo for container management",
"blockMessage": "π‘οΈ Docker mutations blocked. Use your stack manager."
}The Voxyz article on running 5 AI agents in production found that making memory_search a mandatory process step (not a prompt reminder) improved retrieval from 30% to 73%. Their conclusion: "stronger models don't help as much as harder constraints."
System prompts are suggestions. Guardian is enforcement.
Agent calls exec("docker stop nginx")
β before_tool_call hook fires
β Guardian matches docker-mutate rule
β Tool call BLOCKED
β Agent sees: "π‘οΈ Docker mutations blocked. Use your stack manager."
β Agent uses the managed path instead
- Copy the plugin:
mkdir -p ~/.openclaw/extensions/guardian
cp index.js openclaw.plugin.json guardian-rules.json ~/.openclaw/extensions/guardian/- Enable in
~/.openclaw/openclaw.json:
{
"plugins": {
"allow": ["guardian"],
"load": {
"paths": ["~/.openclaw/extensions/guardian"]
},
"entries": {
"guardian": { "enabled": true }
}
}
}- Restart the gateway. Look for:
[guardian] Loaded N rules (N enabled)
[guardian] Registered before_tool_call enforcement hook
Rules live in guardian-rules.json:
[
{
"id": "unique-id",
"description": "What this rule does",
"enabled": true,
"tool": "exec",
"pattern": "regex-to-match",
"field": "command",
"exclude": "optional-allow-pattern",
"managedPath": "What the agent should use instead",
"blockMessage": "π‘οΈ Message shown when blocked"
}
]| Field | Required | Description |
|---|---|---|
id |
β | Unique rule identifier |
description |
β | Human-readable explanation |
enabled |
β | Toggle without deleting |
tool |
β | Tool to intercept: exec, Write, Edit, etc. |
pattern |
β | Regex matched against the target field (case-insensitive) |
field |
β | Parameter to check: command, file_path, path, etc. |
fallbackField |
Try this field if the primary is empty (e.g., path when file_path is null) |
|
exclude |
Regex β if this matches, the rule is skipped (allow list) | |
managedPath |
Tells the agent what to use instead | |
blockMessage |
β | What the agent sees on block |
- Patterns use the
iflag (case-insensitive) - For exec commands, prefix with
(^|[;&|\\n])\\s*to match command position β avoids false positives from docker/himalaya inside echo strings or comments - Use
excludeto carve out read-only operations from a broad block - Rules are cached for 60 seconds β edit the JSON and changes take effect automatically
{
"id": "no-sudo",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*sudo\\s+",
"field": "command",
"blockMessage": "π‘οΈ sudo blocked. Stage files and ask the human to run privileged commands."
}{
"id": "no-git-workspace",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*git\\s+(clone|init|checkout|push|pull|merge|rebase|reset)",
"field": "command",
"blockMessage": "π‘οΈ Git blocked in workspace. Clone to /tmp/ first, copy what you need."
}{
"id": "protect-config",
"tool": "Write",
"pattern": "\\.openclaw/openclaw\\.json$",
"field": "file_path",
"blockMessage": "π‘οΈ Config is protected. Use the config validation workflow."
}{
"id": "db-readonly",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*(sqlite3|psql|mysql).*\\b(INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE)\\b",
"field": "command",
"blockMessage": "π‘οΈ Database writes blocked. Read-only access only."
}Prevent agents from approving their own approval gates β forces human-in-the-loop:
{
"id": "self-approve-lobster",
"tool": "lobster",
"pattern": "^resume$",
"field": "action",
"blockMessage": "π‘οΈ Self-approval blocked. Route approval requests to your human."
}{
"id": "gateway-restart",
"tool": "exec",
"pattern": "(^|[;&|\\n])\\s*(sudo\\s+)?openclaw\\s+gateway\\s+restart",
"field": "command",
"blockMessage": "π‘οΈ Direct restart blocked. Use your restart workflow."
}Write test cases alongside your rules:
node test-rules.cjs
# Guardian Rule Tests: 97 pass / 0 fail (of 97)The test harness validates regex patterns against known inputs without needing the OpenClaw runtime. Add test cases every time you add or modify a rule.
Every block is logged to ~/.openclaw/guardian/guardian.jsonl:
{"ts":"2026-03-21T22:54:28.123Z","session":"main","rule":"docker-mutate","tool":"exec","blocked":"docker stop nginx"}Cumulative stats in ~/.openclaw/guardian/stats.json. Review periodically β frequent blocks on the same rule might mean your agent needs a better managed path, not more blocks.
{
"plugins": {
"entries": {
"guardian": {
"enabled": true,
"config": {
"verbose": false
}
}
}
}
}Set verbose: true to log all evaluated tool calls, not just blocks.
- Block, don't warn. Warning tiers don't work β the agent treats warnings the same way it treats system prompts. Either block or allow.
- Rules in data, not code. JSON rules can be edited without touching the plugin. They can even be auto-optimized by external harnesses.
- Every block needs an alternative. A block without a
managedPathis just frustration. Tell the agent what to do instead. - False positives are bugs. If a legitimate operation gets blocked, fix the pattern. Don't add a "sometimes it's ok" tier.
- Audit everything. You should know exactly what Guardian is doing.
guardian-rules.json β index.js (before_tool_call hook) β block or allow
β
guardian.jsonl (audit log)
stats.json (block counts)
Single-file plugin, ~180 lines. Registers one before_tool_call hook. Evaluates all enabled rules against every tool call. First match wins.
Guardian can intercept any OpenClaw tool β not just exec. Block Write, Edit, gateway, lobster, message, or any other tool by name. If the tool has parameters, you can match against any parameter field.
MIT