clawthority
Authorization policy engine plugin for Clawthority
Audits
PassInstall
openclaw plugins install clawhub:@clawthority/clawthority
Clawthority
A semantic authorization runtime for AI agents. Define what your agent can do, enforce it at the boundary, and keep a human in the loop for what matters.
Clawthority is a policy engine plugin for OpenClaw. It sits between your AI agent and every tool it calls, evaluates rules before execution, and — if policy says no — the call is never placed.
Agent tool call ─► Clawthority ─► Allow │ Deny │ Ask human ─► Audit log
[!NOTE] v1.0 — stable API. The plugin API and policy bundle schema follow semantic versioning; breaking changes ship in a future major release. The version badge above reflects the current release.
Contents
- Why Clawthority
- What it is — and isn't
- Quickstart
- How it works
- Action registry
- Human-in-the-loop
- Configuration
- Documentation
- Development
- License
Why Clawthority
AI agents call tools. When a tool can delete files, send money, or talk to strangers, who decides whether that call goes through — the model, or you?
Skill-based safety (instruct the model to "please check first") fails the moment the model is wrong, distracted, or prompt-injected. Clawthority moves the decision outside the model's loop — into the code path between the agent and the tool.
| The Skill (model-enforced) | The Plugin (code-enforced) | |
|---|---|---|
| Lives in | Context window — model sees it | Execution path — between agent and tools |
| Enforces via | Model reasoning; asks it to comply | Code boundary; intercepts the call |
| Can be bypassed? | Yes — prompt injection, loop misfire | No — runs outside the model's loop |
| Gives you | Observability + soft stop | Hard enforcement + append-only audit log |
A skill asks the model to enforce. A plugin enforces regardless of what the model decides.
What it is — and isn't
Clawthority is:
- A policy decision + enforcement layer for tool calls, installed as an OpenClaw plugin.
- A semantic authorizer — rules are written against canonical action classes (
filesystem.delete,payment.initiate), not brittle tool-name regexes. - A cryptographic capability system — HITL approvals are SHA-256-bound to
(action_class, target, payload_hash)at approval time. Param tampering after approval = auto-deny. - Two install modes —
open(default, implicit permit + critical forbids) for zero-friction installs, andclosed(implicit deny, explicit permits required) for locked-down production. Stage 1 capability/HITL gates and pipeline-level error handling fail closed in both modes.
Clawthority isn't:
- A model-safety or alignment layer. It does not enforce semantic constraints on prompt content, and it does not inspect tool outputs for sensitive data.
- A runtime for agents. OpenClaw still decides which tools the agent sees; Clawthority decides whether those calls run.
- A substitute for good action-class registration. Misregistered tools fall through to
unknown_sensitive_action, which is a critical forbid in both modes — a signal you need to register the alias.
Quickstart
Install as an OpenClaw plugin:
git clone https://github.com/OpenAuthority/clawthority ~/.openclaw/plugins/clawthority
cd ~/.openclaw/plugins/clawthority
npm install && npm run build
Register in ~/.openclaw/config.json:
{ "plugins": ["clawthority"] }
By default Clawthority runs in open mode — implicit permit, with a critical-forbid safety net (shell.exec, code.execute, payment.initiate, credential.read, credential.write, unknown_sensitive_action). To run in closed mode (implicit deny, explicit permits required) set the env var before launching your agent:
export CLAWTHORITY_MODE=closed
Mode is read once at activation — restart the agent to change it.
Drop a policy bundle at data/bundles/active/bundle.json:
{
"version": 1,
"rules": [
{ "effect": "permit", "action_class": "filesystem.read", "priority": 10 },
{ "effect": "forbid", "action_class": "shell.exec", "priority": 100 },
{ "effect": "forbid", "action_class": "payment.initiate", "priority": 90 }
],
"checksum": "<SHA-256 of JSON.stringify(rules)>"
}
Run your agent. A shell.exec call now terminates at the boundary:
[clawthority] │ DECISION: ✕ BLOCKED (cedar/forbid) — shell.exec
Every decision — allow or deny — is streamed to data/audit.jsonl.
[!TIP] Bundles are hot-reloaded with monotonic version + checksum enforcement, so you can iterate on rules without restarting your agent.
How it works
Agent picks a tool → Clawthority intercepts
│
│ normalize_action(toolName, params) → { action_class, target, payload_hash }
│ buildEnvelope(...) → ExecutionEnvelope
▼
┌──────────────────────── Pipeline ────────────────────────┐
│ Stage 1: Capability Gate │
│ • low-risk bypass │
│ • approval_required / TTL / payload binding │
│ • one-time consumption, session scope │
│ • untrusted source + high risk → deny │
│ │
│ Stage 2: Constraint Enforcement Engine │
│ • protected path check (~/.ssh, /etc/, .env, …) │
│ • trusted domain check (communication.external.send) │
│ • PolicyEngine.evaluateByActionClass(...) │
│ │
│ HITL: if required and no valid capability │
│ → issue approval via Telegram / Slack │
│ → deny 'pending_hitl_approval' │
└──────────────────────────────────────────────────────────┘
│
├── allow → tool executes
└── deny → tool call never placed; ExecutionEvent logged
Every decision emits an ExecutionEvent to the append-only JSONL audit log with action_class, target, decision, deny_reason, latency_ms, and context_hash.
Full walk-through: docs/architecture.md.
Action registry
Tool calls are normalized to a canonical action class before policy evaluation. You write rules against the class, not the tool name — so aliases, renames, and misspelled parameters all route to the same decision.
| action_class | risk | default HITL | sample aliases |
|---|---|---|---|
filesystem.read | low | none | read_file, cat_file, view_file |
filesystem.write | medium | per_request | write_file, edit_file, patch_file |
filesystem.delete | high | per_request | rm, delete_file, unlink, shred |
shell.exec | high | per_request | exec, bash, run_command |
communication.email | high | per_request | send_email, gmail, mail |
web.post | medium | per_request | http_post, axios.post, fetch |
payment.initiate | critical | per_request | wire_transfer, stripe_charge |
credential.write | critical | per_request | set_secret, keychain_set |
unknown_sensitive_action | critical | per_request | fallback for unknown tools |
Parameter reclassification — a filesystem.write with a URL target is reclassified to web.post; shell metacharacters in shell.exec / filesystem.write parameters escalate risk to critical.
Full table: docs/action-registry.md.
Human-in-the-loop
High-risk action classes route to a human for approval via Telegram or Slack. Approvals are:
- Payload-bound — SHA-256 of
(action_class | target | payload_hash)is stored with the approval and re-verified at consumption. Any parameter change invalidates the token. - One-time (
approve_once) or session-scoped (session) — session approvals keyed on${session_id}:${action_class}. - TTL-limited — default 120 seconds, configurable.
- UUID v7 IDs — time-sortable, safe to log.
Example approval message:
Action: communication.external.send
Target: user@partner.com
Summary: communication.external.send → user@partner.com
Expires: 2026-04-14T12:34:56Z
Token: 01f2e4b8-...
Channel setup, retry/backoff, and fallback behaviour: docs/human-in-the-loop.md.
Configuration
All config lives in openclaw.plugin.json — every field is optional:
{
"bundlePath": "data/bundles/active",
"proposalPath": "data/bundles/proposals",
"auditLogFile": "data/audit.jsonl",
"cee": {
"trustedDomains": ["company.com"],
"protectedPaths": ["~/.ssh/", "~/.gnupg/", "/etc/", "~/.env", ".env", "~/.aws/"]
},
"hitl": {
"telegram": { "botToken": "...", "chatId": "..." },
"slack": { "botToken": "xoxb-...", "channelId": "C0...", "signingSecret": "...", "interactionPort": 3201 }
}
}
Full schema and environment-variable overrides: docs/configuration.md.
[!IMPORTANT] In production, set
data/bundles/active/read-only for the Clawthority process user. Only your deployment pipeline should have write access.
Documentation
Getting started
- Installation — prerequisites, plugin registration, first run
- Configuration — full schema, env-var overrides, secrets handling
- Usage — common rule patterns and day-to-day operation
Architecture & reference
- Architecture —
ExecutionEnvelope, two-stage pipeline, adapter layer - Action Registry — all canonical action classes, risk tiers, HITL modes
- Human-in-the-Loop — payload binding, session vs approve-once, channel adapters
- API Reference — target spec for the dashboard / control-plane REST + SSE surface
Operations
- Rule Deletion — safe rule removal via the impact-preview modal
- Troubleshooting — common errors, log prefixes, fail-closed diagnostics
- Roadmap — what's shipped, what's next
Contributing
- Contributing guide — dev setup, test layout, commit conventions
Development
npm install
npm run dev # watch mode
npm run build # production build
npm test # unit tests
npm run test:e2e # end-to-end tests
