clawthority

Authorization policy engine plugin for Clawthority

Audits

Pass

Install

openclaw plugins install clawhub:@clawthority/clawthority

Clawthority

Clawthority

OpenClaw Plugin CI Version License: Apache 2.0 Node.js >= 18 TypeScript PRs Welcome

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

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 inContext window — model sees itExecution path — between agent and tools
Enforces viaModel reasoning; asks it to complyCode boundary; intercepts the call
Can be bypassed?Yes — prompt injection, loop misfireNo — runs outside the model's loop
Gives youObservability + soft stopHard 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 modesopen (default, implicit permit + critical forbids) for zero-friction installs, and closed (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_classriskdefault HITLsample aliases
filesystem.readlownoneread_file, cat_file, view_file
filesystem.writemediumper_requestwrite_file, edit_file, patch_file
filesystem.deletehighper_requestrm, delete_file, unlink, shred
shell.exechighper_requestexec, bash, run_command
communication.emailhighper_requestsend_email, gmail, mail
web.postmediumper_requesthttp_post, axios.post, fetch
payment.initiatecriticalper_requestwire_transfer, stripe_charge
credential.writecriticalper_requestset_secret, keychain_set
unknown_sensitive_actioncriticalper_requestfallback 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

  • ArchitectureExecutionEnvelope, 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


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

License

Apache-2.0.