Code Pluginsource linked

Agent Permissionsv0.2.0

OpenClaw plugin — permission and approval engine. Gates built-in tool calls (bash, file edit, etc.) and plugin tools via a three-bucket policy (allow/deny/ask) with wildcard rules, rule sources (session/workspace/user/config), and in-chat approval. No network calls.

@clawnify/agent-permissions·runtime agent-permissions·by @clawnify
Community code plugin. Review compatibility and verification before install.
openclaw plugins install clawhub:@clawnify/agent-permissions
Latest release: v0.2.0Download zip

Capabilities

configSchema
Yes
Executes code
Yes
HTTP routes
0
Runtime ID
agent-permissions

Compatibility

Built With Open Claw Version
2026.5.12
Min Gateway Version
>=2026.5.12
Plugin Api Range
>=2026.5.2
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
The package consistently describes and implements a permission/approval engine that hooks OpenClaw tool calls, evaluates allow/deny/ask rules, and uses native in-chat approval.
Instruction Scope
Rules, default modes, strict mode, bypass-style modes, wildcard matching, and dangerous-pattern handling are documented and reflected in the source; no hidden prompt manipulation or unrelated instructions were found.
Install Mechanism
Installation is a normal OpenClaw/npm plugin flow with no postinstall or setup entry; published runtime code is TypeScript/JavaScript for the declared plugin.
Credentials
It reads local and user permission JSON files and can write learned allow rules, which is sensitive but proportionate for a permissions engine; source review found no runtime network calls.
Persistence & Privilege
The allow-always path persists approved rules to the user permissions file and exposes a same-process resolver API on globalThis; both are disclosed and purpose-aligned, but broad rules can meaningfully change future agent authority.
Assessment
Before installing, review the configured allow/deny/ask rules carefully. Avoid broad allow rules or bypass-style modes unless you intend the agent to proceed without prompts; use strict mode or explicit ask rules for higher-control environments.

Verification

Tier
source linked
Scope
artifact only
Summary
Validated package structure and linked the release to source metadata.
Commit
38aa3fb9eb09
Tag
38aa3fb9eb0993f83022d0b88ed85567f5480853
Provenance
No
Scan status
clean

Tags

latest
0.2.0

@clawnify/agent-permissions

OpenClaw plugin — permission and approval engine for any OpenClaw agent.

Gates built-in tool calls (bash, file edit, web fetch, etc.) and any plugin- registered tool through a three-bucket policy (allow / deny / ask), with rule sources walked in priority order, in-chat approval surfaced via OpenClaw's native requireApproval, and learning into allow-always rules.

MIT licensed. Maintained by Clawnify, designed for the wider OpenClaw ecosystem — works in any gateway with any agent setup.

Installation

openclaw plugins install @clawnify/agent-permissions --pin

Or via npm:

npm install @clawnify/agent-permissions

Then enable in openclaw.json:

{
  "plugins": {
    "allow": ["agent-permissions", "your-consumer-plugin"],
    "entries": {
      "agent-permissions": {
        "enabled": true,
        "config": {
          "defaultMode": "default",
          "ask": ["Bash(*)"],
          "deny": ["Bash(rm -rf /:*)"]
        }
      }
    },
    "load": {
      "paths": [
        "/path/to/agent-permissions",
        "/path/to/your-consumer-plugin"
      ]
    }
  }
}

agent-permissions must load before any consumer plugin (list it first in plugins.load.paths) so the registration API is available when consumer register() runs.

Why this exists

OpenClaw's built-in permission infrastructure today is:

  • Gateway-level exec-approval for bash — coarse, command-shape rules
  • registerTrustedToolPolicy — bundled-only; external plugins can't use it

Non-bundled plugins that want to gate their own tools (or gate other plugins' tools) have no host-level seam. They either reinvent approval per-plugin or ship without it. This engine fills that gap.

Single global before_tool_call hook + an extension point so any plugin can participate without each one rebuilding the policy / approval / learning loop.

What it does

CapabilityHow
Gates any tool call in the gatewaySingle global before_tool_call hook at priority 100 (verified upstream sort direction in src/plugins/hooks.ts:266)
Built-in tools (bash, file edit, web fetch, …) supported out of the boxGeneric resolver: shell tools (bash/exec) match against the actual command; everything else matches by tool name. No registration required.
Other plugins' tools supportedSame generic path — operators add rules in openclaw.json targeting the tool name. Plugins don't need to know about us.
Optional rich promptsConsumer plugins MAY call registerResolver({ toolName, resolve }) for tool-specific prompt titles/descriptions. Opt-in.
Three-bucket policyallow / deny / ask evaluated against rule sources in priority order
In-chat approvalUses OpenClaw's native requireApproval — same UI as exec approvals
Learningallow-always resolutions persist to user/local/session as configured
Wildcard rulesTool(foo) exact, Tool(foo:*) legacy prefix, Tool(foo *) new wildcard
Dangerous-pattern denylistPatterns like python:*, node:*, eval cannot be allow-always-persisted
Fail-closedOpenClaw's hook runner catches exceptions and fails open — this plugin wraps every code path in try/catch and returns { block: true } instead

Default modes

  • default (out of the box) — operator-opt-in: tools pass through unless an ask or deny rule explicitly matches. No surprises; you add rules for what you want gated.
  • strict — Claude-Code style: ask on anything not explicitly allowed. Opt-in for hard-gate setups.
  • bypassPermissions / dontAsk — allow everything except matching deny rules.
  • acceptEdits — currently behaves like default. Reserved for future tool-category-aware behavior (auto-allow edits within CWD).

What it does NOT do

  • Network calls. Storage is local files. Consumers that want cloud sync can hook onAllowAlwaysPersisted and mirror to their own backend.
  • Tool-specific logic. Each tool's rule-content + prompt text comes from a resolver, not from this engine. The engine is tool-agnostic.

Architecture

┌─── OpenClaw gateway process ────────────────────────────────────────┐
│                                                                     │
│  Consumer plugin (e.g. agent-tools, clawflow, third-party)          │
│   └─ on register():                                                 │
│        getAgentPermissionsApi().registerResolver({                  │
│          toolName: "some_tool",                                     │
│          resolve(params) {                                          │
│            return { ruleContent: "delete", title, description };    │
│          },                                                         │
│        })                                                           │
│                                                                     │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │ agent-permissions (this plugin)                              │  │
│  │                                                              │  │
│  │  before_tool_call hook (priority: 100)                       │  │
│  │   ├─ resolver = resolvers.get(event.toolName)                │  │
│  │   ├─ req = resolver(event.params)                            │  │
│  │   ├─ decision = ruleEngine.evaluate(toolName, ruleContent)   │  │
│  │   ├─ bucket "deny"  → { block: true, blockReason }           │  │
│  │   ├─ bucket "ask"   → { requireApproval: {...} }             │  │
│  │   ├─ bucket "allow" → undefined (proceed)                    │  │
│  │   └─ try/catch wrapper → { block: true } on any error        │  │
│  │                                                              │  │
│  │  rule sources walked in priority order:                      │  │
│  │   1. session (in-memory, allow-always with scope:session)    │  │
│  │   2. local   (.openclaw/permissions.json in CWD)             │  │
│  │   3. user    (~/.openclaw/permissions.json)                  │  │
│  │   4. config  (pluginConfig.allow/deny/ask from openclaw.json)│  │
│  │                                                              │  │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

Rule format

Same shape as Anthropic's Claude Code permission system (studied as prior art).

RuleMeaning
ToolNameTool-wide rule (any params match).
ToolName(*)Equivalent to tool-wide (empty / * content).
Bash(npm install)Exact match on (content).
Bash(npm:*)Legacy prefix syntax — matches npm, npm install, etc.
Bash(git *)Wildcard — * matches any chars. Trailing * makes trailing args optional, so git * matches both git add and bare git.
Bash(python -c "print\\(1\\)")Escape (, ) in content with \. Escape * with \*. Escape \ with \\.

Dangerous patterns

dangerousPatterns config (defaults built in) lists prefixes that may match ask rules but can never be allow-always-persisted, even if the user clicks "allow always." Reason: granting Bash(python:*) = arbitrary code execution, which defeats the gate entirely.

Default list (conservative):

python python3 python2 node deno tsx ruby perl php lua
npx bunx npm run yarn run pnpm run bun run
bash sh zsh fish
eval exec env xargs sudo ssh
curl wget

Override dangerousPatterns in openclaw.json to extend or replace.

Inter-plugin API (runtime)

import { getAgentPermissionsApi } from "@clawnify/agent-permissions";

// In your consumer plugin's register():
const perms = getAgentPermissionsApi(); // throws if agent-permissions not loaded

perms.registerResolver({
  toolName: "my_dangerous_tool",
  resolve(params) {
    const p = params as { target?: string };
    return {
      ruleContent: "delete",
      title: `Delete ${p.target ?? "?"}?`,
      description: "This is irreversible.",
    };
  },
});

perms.onAllowAlwaysPersisted(async (event) => {
  // Optional: mirror to your own backend, audit, etc.
});

The plugin publishes its API on globalThis[Symbol.for("clawnify.agent-permissions.api.v1")], so consumers find it at runtime even when each plugin ships as an independent tarball with no shared node_modules. The getAgentPermissionsApi() helper wraps the Symbol lookup with a descriptive error if the plugin isn't loaded (typically a plugins.load.paths ordering issue).

Development

git clone https://github.com/clawnify/agent-permissions.git
cd agent-permissions
npm install
npm run build
npm test

Tests use Node's built-in test runner via tsx. No vitest/jest setup.

Releases

Tag a release on GitHub → .github/workflows/publish.yml runs npm publish --provenance.

License

MIT.


Initiated and maintained by the Clawnify team — AI agent hosting and orchestration.