# @axonflow/openclaw
**Governance for OpenClaw agents: block dangerous tool calls, require human approval on high-risk actions, redact PII from outbound messages, and keep a compliance-grade audit trail — without changing a single line of your agent code.**
[](https://www.npmjs.com/package/@axonflow/openclaw)
[](https://clawhub.ai/plugins/%40axonflow%2Fopenclaw)
[](./LICENSE)
> **→ Full integration walkthrough:** **[docs.getaxonflow.com/docs/integration/openclaw](https://docs.getaxonflow.com/docs/integration/openclaw/)** — architecture, hook coverage, policy examples, and troubleshooting.
> **Upgrade strongly recommended.** AxonFlow ships substantial monthly security and quality hardening; staying on the latest major is the security-supported release line. [Latest release](https://github.com/getaxonflow/axonflow-openclaw-plugin/releases/latest) · [Security advisories](https://github.com/getaxonflow/axonflow-openclaw-plugin/security/advisories)
> **Rolling this plugin out for a real team or production workflow?**
>
> Choose the path that fits:
> - **Hosted Starter:** $9.99/month when enrollment opens; [check current availability](https://getaxonflow.com/saas-pricing/) before starting email verification or Portal Checkout
> - **Free self-hosted:** Community Edition on infrastructure you operate
> - **Paid production program:** [Design Partner or Confidential Pilot](https://getaxonflow.com/design-partner?utm_source=readme_plugin_openclaw) - one scoped workflow over 60 or 75 days, founder-led rollout support, upfront conversion pricing, and a fixed decision date; public track from $2,000 or confidential track from $4,000
>
> The paid program requires a dated forcing event, written controls, an executive sponsor, and a technical owner. Prices are subject to eligibility and a signed agreement.
---
## Why this plugin exists
OpenClaw is a strong agent runtime. It is also a serious production security problem the moment you take it past a prototype:
- **[135,000+ publicly exposed instances](https://www.bitsight.com/blog/openclaw-ai-security-risks-exposed-instances)** deployed without central policy enforcement
- **[13+ CVEs disclosed in 2026](https://github.com/jgamblin/OpenClawCVEs/)**, several at CVSS 9.8+
- **[1,184 malicious skills](https://cyberpress.org/clawhavoc-poisons-openclaws-clawhub-with-1184-malicious-skills/)** poisoned in ClawHub via the ClawHavoc supply-chain attack
- **No native PII/secrets scanning**, no SQL-injection defense, no compliance-grade audit trail, no org-wide tool policy, no approval workflow
OpenClaw handles agent runtime, MCP connectivity, channels, and tool execution. It was never intended to be the place you enforce governance. This plugin adds the governance layer on top, so OpenClaw keeps doing what it does well and AxonFlow takes over the "is this allowed, should this redact, who approved, where is the audit record" questions.
**AxonFlow governs. OpenClaw orchestrates.** OpenClaw still makes every LLM call; AxonFlow only evaluates policies and records audit trails. LLM provider keys never leave your machine. Where the rest of your data goes — tool inputs, message bodies — depends on which deployment mode you pick. See **[Where your data goes](#where-your-data-goes)** below.
---
## Where your data goes
The plugin governs tool calls and outbound messages by sending each one to an AxonFlow endpoint for policy enforcement and audit. Pick the deployment mode that fits your workload:
> **Privacy notice — read before installing.** AxonFlow [Community SaaS](https://docs.getaxonflow.com/docs/deployment/community-saas/) at `try.getaxonflow.com` is the default hosted endpoint when no other endpoint is configured. A fresh install must first link to a verified Hosted account; until then, governed calls are blocked locally. Once linked, governed tool inputs (tool name + arguments) and outbound message bodies are checked by AxonFlow's policy enforcement endpoint. **Community SaaS is for early exploration only** — not for production workloads, regulated environments, real user data, personal data, or any other sensitive information. It is offered "as is" on a best-effort basis with no SLA, no warranties, and no commitment to retention, deletion, or incident-response timelines.
>
> For any serious use, choose one of the following instead:
>
> 1. **[Self-host AxonFlow Community Edition](https://docs.getaxonflow.com/docs/deployment/self-hosted/)** — runs entirely on your infrastructure and keeps data within your boundary. Recommended for any real workload.
> 2. **Community Edition with an [Evaluation License](https://docs.getaxonflow.com/docs/deployment/evaluation-rollout-guide/)** — for production use with real users or clients on the open core; adds production-fit limits and license-gated features. Free 90-day [evaluation license](https://getaxonflow.com/plugins/evaluation-license).
> 3. **[AxonFlow Enterprise](https://docs.getaxonflow.com/docs/deployment/community-to-enterprise-migration/)** — production-grade governance, regulatory-grade controls, SLOs, and contractual commitments suitable for regulated industries. Contact [hello@getaxonflow.com](mailto:hello@getaxonflow.com).
>
> To skip Community SaaS entirely: set `pluginConfig.endpoint` to a self-hosted AxonFlow URL. That alone flips the plugin into self-hosted mode — hosted linking is not attempted, and no env var is required. Get the AxonFlow platform from [getaxonflow/axonflow](https://github.com/getaxonflow/axonflow) and follow the [Getting Started](https://docs.getaxonflow.com/docs/getting-started/) guide for the Docker Compose setup. For air-gapped environments where AxonFlow is not yet reachable but you want to suppress hosted setup, set `AXONFLOW_COMMUNITY_SAAS=0`; set `AXONFLOW_TELEMETRY=off` to also disable the 7-day usage heartbeat.
### Self-hosted (recommended for any real use)
Point the plugin at an AxonFlow instance you run. Nothing leaves your network except the usage heartbeat (which can also be disabled). Configure three values in `pluginConfig`:
- `endpoint` — the URL of your AxonFlow agent gateway (for example `https://axonflow.your-corp.example.com`).
- `clientId` — the AxonFlow tenant identifier issued to your deployment.
- `clientSecret` — the matching secret. **Never commit this to source control or paste it into a config file checked into a repository.** Resolve it from a secret manager (Vault, AWS Secrets Manager, GCP Secret Manager, or your CI provider's secret store) and inject the value via your OpenClaw config templating, an environment variable consumed by your config loader, or your platform's secret-injection sidecar.
For production with real users or clients: run Community Edition with a free 90-day [Evaluation License](https://docs.getaxonflow.com/docs/deployment/evaluation-rollout-guide/) for production-fit limits and license-gated features, or [AxonFlow Enterprise](https://docs.getaxonflow.com/docs/deployment/community-to-enterprise-migration/) for regulated industries with SLOs and contractual commitments.
See the [Self-Hosted Deployment Guide](https://docs.getaxonflow.com/docs/deployment/self-hosted/) for prerequisites and production options, or the [OpenClaw Integration Guide](https://docs.getaxonflow.com/docs/integration/openclaw/) for architecture and the full pluginConfig schema.
### Community SaaS hosted starter
Start at [Hosted Starter pricing](https://getaxonflow.com/saas-pricing/) to check availability. Paid enrollment and Checkout are not yet open. When enrollment opens, verify your email, complete Checkout in the secure Portal, then [run the link command from the installed plugin](#link-your-hosted-account) and enter its 10-character code in the Portal. The client generates its own secret and stores it at `$AXONFLOW_CONFIG_DIR/try-registration.json` (mode `0600`); the browser never shows it. Fresh Hosted installs never create an anonymous tenant, even with `AXONFLOW_LEGACY_AUTO_REGISTER=1`. Existing saved credentials remain usable until expiry; users of older anonymous Hosted credentials should sign in on the website and device-link this client before expiry because the legacy flag cannot renew them.
Until a fresh Hosted install loads a linked credential, governed tool calls are blocked and outbound messages are cancelled, even with `onError: "allow"`. Explicit self-hosted Community configurations keep their existing behavior.
**Use only for early exploration of the plugin's behaviour. Not for production workloads, regulated environments, real user data, personal data, or any other sensitive information.**
| What goes to `try.getaxonflow.com` | What does NOT |
|---|---|
| Tool name + arguments before each governed call | LLM provider API keys |
| Outbound message bodies before delivery (PII/secret scan) | — |
| After linking: LLM prompt excerpts (first 500 chars) and response excerpts (first 200 chars) for audit | — |
| After linking: governed tool audit records include tool name, tool input (top-level string values are truncated after 500 characters; nested values are not truncated), up to 500 characters of the serialized result, error text when present, and duration | — |
| Usage heartbeat (plugin version, OS, runtime, instance ID) | Files outside the OpenClaw runtime |
Hosted Starter includes the governance foundation and a Hosted case Portal. The verified account shows its exact governed-event, case, seat, integration, retention, and burst limits before purchase and through billing status. Higher volume, additional integrations, longer retention, production financial actions, and contractual controls follow the paid Enterprise PoC or design-partner path. Read the [Community SaaS documentation](https://docs.getaxonflow.com/docs/deployment/community-saas/) for the data boundary and current limits.
### Air-gapped: zero outbound
If you need the plugin to make *no* outbound calls at all — air-gapped lab, regulated network, etc. — set both:
```bash
export AXONFLOW_COMMUNITY_SAAS=0 # disable auto-bootstrap
export AXONFLOW_TELEMETRY=off # disable 7-day heartbeat
```
…and configure `pluginConfig.endpoint` to a self-hosted AxonFlow on the same network. With these set, no traffic leaves your environment.
## Environment variables
The plugin recognizes the following environment variables. All are optional with safe defaults; the plugin runs unmodified if none are set.
| Variable | Effect |
|---|---|
| `AXONFLOW_ENDPOINT` | Override the AxonFlow agent gateway endpoint. Wins over `pluginConfig.endpoint` when both are set. Setting this selects self-hosted mode: all governed traffic targets this endpoint. For self-hosted deployments, set this (or `pluginConfig.endpoint`) to your AxonFlow URL. |
| `AXONFLOW_TELEMETRY=off` | Disables the 7-day usage heartbeat to `checkpoint.getaxonflow.com`. Accepted off-values: `off`, `0`, `false`, `no`. |
| `AXONFLOW_COMMUNITY_SAAS=0` | Disables Hosted bootstrap entirely. With no explicit self-hosted endpoint, governed tools and outbound messages stay blocked until the client is linked, even under `onError: "allow"`. Set `pluginConfig.endpoint` (or `AXONFLOW_ENDPOINT`) for self-hosted enforcement. Accepted off-values: `0`, `false`, `off`, `no`. |
| `AXONFLOW_LEGACY_AUTO_REGISTER` | Obsolete Hosted flag. Setting it cannot create or renew an anonymous Hosted credential; link the client through the Portal instead. |
| `AXONFLOW_CACHE_DIR` | Overrides the per-user cache dir (telemetry stamp, rate-limit backoff, free-tier throttle + upgrade-prompt stamps). Defaults to `$XDG_CACHE_HOME/axonflow` (Linux), `~/Library/Caches/axonflow` (macOS), `%LOCALAPPDATA%\axonflow` (Windows). |
| `AXONFLOW_CONFIG_DIR` | Overrides the per-user config dir (Community-SaaS registration file, disclosure stamp, plugin runtime-state record read by `axonflow-openclaw-status`). Defaults to OS conventions: `$XDG_CONFIG_HOME/axonflow` (Linux), `~/Library/Application Support/axonflow` (macOS), `%APPDATA%\axonflow` (Windows). |
| `AXONFLOW_LICENSE_TOKEN` | Legacy prepaid plugin-claim token. Existing 90-day purchases remain valid through their paid term. New monthly hosted subscriptions are resolved from the registered account and require no token. |
| `AXONFLOW_USER_TOKEN` | Per-user authorization token forwarded on every governed request via the `X-User-Token` header, giving this developer a **validated** `{identity, role}` on the AxonFlow fleet plane: role-scoped reads return the developer's own rows, and audit attribution keys on the token's canonical email — beating the forgeable `userEmail` label — on the planes that consume the header (the MCP tools at `/api/v1/mcp-server` and the audit/decisions/overrides REST surface; the `check-input`/`check-output` hook plane keeps client-scoped attribution on current platforms). Minted by an org admin via the AxonFlow user-token mint API; delivered via managed settings/MDM. Resolution order: `pluginConfig.userToken` wins over this env var, which wins over the `0600` provisioning file `~/.config/axonflow/user-token.json` (a cross-plugin path shared with the other AxonFlow plugins). A malformed value is dropped with a warning — never sent — and resolution falls through to the next source. Unset (default): no header is sent; requests are byte-identical to v2.6.x. |
| `AXONFLOW_PEP_AUDIENCE` | The audience your AxonFlow decision proofs are bound to. Set it and this plugin declares what it can enforce on every governed call (ADR-065 PEP capability handshake, sent as `X-Axonflow-PEP-Handshake`): a platform running v10.4.0 or later then **refuses** to hand this plugin a mandatory obligation it has declared it cannot discharge, rather than assuming it will. The plugin presents **two** declarations under separate names, because its request and response paths discharge different obligations. A malformed value throws at construction rather than silently disabling the handshake. Unset (default): no header is sent and requests are byte-identical to v2.8.x. |
| `AXONFLOW_RECOVERY_TIMEOUT_MS` | Per-HTTP-request timeout in milliseconds for the bundled `axonflow-openclaw-recover` CLI's calls to `/api/v1/recover` and `/api/v1/recover/verify`. Default `10000` (10s). Increase for high-latency networks; decrease for fail-fast CI environments. |
| `AXONFLOW_UPGRADE_URL` | Overrides the authenticated hosted checkout endpoint surfaced by `axonflow-openclaw-status`. Defaults to `https://try.getaxonflow.com/api/v1/billing/checkout`. |
| `AXONFLOW_TRY=1` | Forces the v1 telemetry `deployment_mode` classifier to report `community_saas` even when the configured endpoint is a custom hostname proxying `try.getaxonflow.com`. No effect outside the heartbeat payload. |
This table is the published declaration for user-facing environment variables. The [`manifest-envvars-coverage`](.github/workflows/manifest-envvars-coverage.yml) gate keeps it aligned with shipped code. The code-plugin manifest intentionally omits unsupported top-level `envVars` and `runtimeBehavior` fields.
<a id="link-your-hosted-account"></a>
## Activate Hosted Starter
Hosted Starter is planned at **$9.99 USD per month**. Paid enrollment and Checkout are currently closed. When they open, the verified account will show the current governed-event, case, seat, integration, retention, and burst allowances before purchase.
To activate an installed plugin:
1. **Check availability.** Visit the [pricing page](https://getaxonflow.com/saas-pricing/). Once paid enrollment opens, verify your email and complete Checkout in the secure Portal.
2. **Link OpenClaw.** After [installing the plugin](#step-2-install-the-plugin), run the link script from OpenClaw's installed extension directory:
```bash
node "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/extensions/axonflow-governance/bin/axonflow-openclaw-link.mjs"
```
Approve its code in the Portal. Each linked client receives its own owner-revocable credential. Up to 10 OpenClaw installations share the included OpenClaw integration. Use `--replace` only when you intend to replace an existing local credential file. OpenClaw's plugin installer does not add the package's `bin` commands to your shell `PATH`; this command also works when `OPENCLAW_STATE_DIR` points to a custom state directory.
3. **Restart and check status.** Run:
```bash
node "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/extensions/axonflow-governance/bin/axonflow-openclaw-status.mjs"
```
The status output confirms the linked client identity and endpoint.
4. **Make a governed call.** Unpaid calls remain blocked. A paid account uses the linked tenant and needs no license token.
Existing 90-day prepaid tokens continue working until their recorded expiry. Renewals, failed payments, cancellation, invoices, and recovery are available through the billing status and secure billing portal endpoints.
### Check status
Use the status command to confirm the registered identity, endpoint, deployment mode, legacy prepaid token state, and authenticated checkout endpoint:
```bash
# Installed via OpenClaw plugin install:
node "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/extensions/axonflow-governance/bin/axonflow-openclaw-status.mjs"
# Or directly from a clone:
node bin/axonflow-openclaw-status.mjs
# Machine-readable JSON, suitable for piping into jq:
node "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/extensions/axonflow-governance/bin/axonflow-openclaw-status.mjs" --json
```
Sample output (hosted account registered):
```
AxonFlow OpenClaw plugin status
client_id: cs_demo_tenant_abc123 (formerly tenant_id)
(tenant identity used for authenticated Hosted calls)
endpoint: https://try.getaxonflow.com (mode=community-saas)
tier: Hosted Starter (payment status checked by AxonFlow)
checkout: POST https://try.getaxonflow.com/api/v1/billing/checkout
Manage payment at https://portal.try.getaxonflow.com/hosted/start.
```
Sample output (self-hosted):
```
AxonFlow OpenClaw plugin status
client_id: acme-prod (formerly tenant_id)
(from pluginConfig.clientId; the identity governed requests authenticate with)
endpoint: https://axonflow.acme.internal (mode=self-hosted)
(from pluginConfig, as recorded by the plugin load at
2026-07-28T11:02:14.881Z; reload OpenClaw after changing it)
tier: Self-hosted Community
```
Sample output (legacy prepaid term active):
```
AxonFlow OpenClaw plugin status
client_id: cs_demo_tenant_abc123 (formerly tenant_id)
(tenant identity used for authenticated Hosted calls)
endpoint: https://try.getaxonflow.com (mode=community-saas)
tier: Prepaid Pro (expires 2026-10-14, 78 days remaining)
license: ...XYZ9 (redacted; last 4 chars only)
```
The license token is **never** printed in full — only the last four characters are shown so you can confirm the token is the one you expect without exposing it via screen-share, copy-paste, or shell history. If `client_id` is missing, the CLI points you at the [installed plugin's link command](#link-your-hosted-account) to pair with your Hosted account. Legacy auto-registered clients can use `axonflow-openclaw-recover` to re-issue credentials against their registered email. JSON output (`--json`) populates BOTH `client_id` and the legacy `tenant_id` key with the same value so v2.4.x consumers keep working unchanged.
**Self-hosted installs.** The CLI runs as its own process. It can see neither OpenClaw's `pluginConfig` nor the environment the OpenClaw runtime was started with, so through v2.8.4 it reported the Community-SaaS default endpoint and the cached Community-SaaS `client_id` even when governed traffic was going to a self-hosted stack ([#167](https://github.com/getaxonflow/axonflow-openclaw-plugin/issues/167)).
It now resolves the endpoint, mode and identity through the same helper the governance runtime uses. To give that helper the inputs it otherwise could not see, every plugin load records them at `$AXONFLOW_CONFIG_DIR/openclaw-plugin-runtime-state.json` (mode `0600`): the endpoint override the runtime resolved — from **either** `AXONFLOW_ENDPOINT` or `pluginConfig.endpoint` — plus the configured `clientId`. No credentials are written: `clientSecret`, `licenseToken` and `userToken` are excluded.
What is recorded is your input, not the resolved answer. `AXONFLOW_ENDPOINT` is still read live from whatever shell you run the CLI in, and still wins, so a recorded value only ever fills a gap and can never override your current environment. Three consequences worth knowing:
- The `mode=` suffix on the `endpoint` line, and the `mode` / `identity_source` keys in `--json`, tell you which deployment the answer describes.
- When a recorded value contributed, the CLI names the channel it came from and the timestamp of the load that recorded it. When nothing was recorded, or nothing contributed, that line is absent rather than decorating an environment-only answer with a timestamp that would suggest otherwise.
- If your shell exports a *different* `AXONFLOW_ENDPOINT` than the runtime was started with, the CLI reports what a fresh load would resolve **and** adds a `NOTE:` naming the endpoint the running plugin is still governing against until it reloads.
Change `pluginConfig` or the runtime's environment and reload OpenClaw, and the record is rewritten to match.
One record exists per config directory. If you run two OpenClaw hosts with different plugin configurations against the same `AXONFLOW_CONFIG_DIR`, the last one to load wins the record, and the CLI will report that configuration to both. Give them separate `AXONFLOW_CONFIG_DIR`s if you need their status surfaces to stay independent — the runtimes themselves are unaffected, since each governs from its own in-process configuration.
The same values are available to the agent through the `axonflow_get_tenant_id` tool, which runs inside the runtime and reads the live config directly.
### Recover lost Community-SaaS credentials
If you registered through the older Community SaaS auto-bootstrap flow, lost its credential file (`$AXONFLOW_CONFIG_DIR/try-registration.json`), and want your tenant + audit history back, use the legacy recovery command:
```bash
# Installed via OpenClaw plugin install:
node "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/extensions/axonflow-governance/bin/axonflow-openclaw-recover.mjs" you@example.com
# Or directly from a clone:
node bin/axonflow-openclaw-recover.mjs you@example.com
```
The CLI:
1. Posts your email to `/api/v1/recover` (the platform always returns 202 — anti-enumeration, no signal whether the email is bound or not).
2. Prompts you to paste the magic-link token (or the full magic-link URL) from the email you receive.
3. Posts the token to `/api/v1/recover/verify`, which returns a freshly-issued tenant_id + secret bound to the original email.
4. Persists the new credentials at `$AXONFLOW_CONFIG_DIR/try-registration.json` (mode `0o600`).
For a new Hosted Starter account, use the [installed plugin's link command](#link-your-hosted-account) and approve its code in the Portal. Legacy recovery does not create or restore a new website-linked client credential.
Reload OpenClaw and the plugin picks up the recovered registration on the next init — no other config change required. Magic-link tokens are one-shot and short-lived; replays return 401.
---
## What you get
| Capability | What it means in practice |
|---|---|
| **Pre-execution policy check** | Every tool call is scored against 80+ built-in policies (reverse shells, SSRF, credential access, SQLi, prompt injection, path traversal, PII in arguments) before it runs |
| **Approval gates** | Any tool in `highRiskTools` pauses execution and posts a native OpenClaw approval request with policy severity surfaced as approval priority |
| **Outbound message scanning** | Every message to Telegram/Discord/Slack/webhook is scanned for PII and secrets before delivery — redacted, blocked, or passed through per policy |
| **Compliance-grade audit trail** | Every tool call and LLM interaction records the input, output summary, matched policies, decision, and duration |
| **Decision explainability** | Blocked calls return a `decision_id` the agent can pass to `explainDecision()` to see exactly which policy family triggered and why |
| **Session overrides** | Operators can request a time-bounded, audit-logged exception when policy allows it — without leaving the agent |
| **Per-user identity** | `config.userEmail` threads the actual human operator through to every explain/override call, so shared chat agents still produce attributable audits |
---
## How it plugs in
```
┌──────────────────────────────────────────────────────────────┐
│ OpenClaw Agent │
│ │
│ User Message → LLM Call → Tool Execution → Response → User │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ @axonflow/openclaw │ │
│ │ │ │
│ │ GOVERNANCE (can block / modify): │ │
│ │ before_tool_call (priority 10) → check_input │ │
│ │ message_sending (priority 10) → check_output │ │
│ │ │ │
│ │ AUDIT (observe-only, non-blocking): │ │
│ │ after_tool_call (priority 90) → audit_tool_call │ │
│ │ llm_input (priority 90) → record prompt │ │
│ │ llm_output (priority 90) → record response │ │
│ └────────────────────────┬───────────────────────────────┘ │
└───────────────────────────┼──────────────────────────────────┘
│
▼
┌───────────────────┐
│ AxonFlow │
│ ┌─────┐ ┌─────┐ │
│ │Policy│ │Audit│ │
│ │Engine│ │Trail│ │
│ └─────┘ └─────┘ │
│ ┌─────┐ │
│ │ PII │ │
│ │Scan │ │
│ └─────┘ │
└───────────────────┘
```
**What stays the same:** your OpenClaw agent config, ClawHub skills, MCP connectors, and channel integrations are unchanged. The plugin only adds lifecycle hooks.
---
## The production problems this solves
These are the three questions that reliably surface the moment an OpenClaw agent hits real users or regulators.
### 1. "The tool that phones home"
A `web_fetch` skill is installed from ClawHub. An agent uses it to look up product docs. Then a user asks, *"Summarize my customer list"* — the agent calls `web_fetch` with customer emails in the URL. The data leaves your infrastructure. OpenClaw executed the tool correctly; nobody checked what it was sending.
**What the plugin does:** `check_input` fires before `web_fetch` runs, scans the URL arguments against PII and exfiltration policies, and blocks the call with a decision ID.
### 2. "The MCP response full of PII"
An MCP connector queries your CRM for "recent support tickets." The MCP server returns 50 rows with names, emails, phone numbers. All of it flows into the LLM context. OpenClaw managed the connection; SecretRef protected the credentials; the *data itself* was never inspected.
**What the plugin does:** `check_output` fires on `message_sending` before anything reaches the user channel, and scans every outbound message for SSN, credit card, API key, and other 80+ policy matches — redacting or blocking per policy.
### 3. "The compliance question nobody can answer"
Six months later, a regulator asks: *"For this interaction on March 14, which tools were called, what data did they access, which policies were in effect, and why was the response allowed?"* OpenClaw's execution logs show a tool was called and succeeded. The *decision context* does not exist.
**What the plugin does:** every governed call emits a structured audit record with tool, input, output summary, matched policies, decision, and duration. Search via `searchAuditEvents()` or the Customer Portal.
---
## Take a governed plugin rollout into production
Solo developers and self-serve teams can use the free 90-day [Plugin Evaluation License](https://getaxonflow.com/plugins/evaluation-license?utm_source=readme_plugin_openclaw_eval) to validate hook behavior, policy packs, and override workflows.
Organizations with a dated production requirement, written controls, an executive sponsor, and a technical owner can use AxonFlow's paid [Production Program](https://getaxonflow.com/design-partner?utm_source=readme_plugin_openclaw). It takes one scoped workflow into production over 60 or 75 days with Enterprise access, founder-led rollout support, upfront conversion pricing, and a fixed decision date.
Public Design Partner pricing starts at $2,000; the Confidential Paid Pilot starts at $4,000. Prices are subject to eligibility and a signed agreement.
### See AxonFlow in Action
Videos covering different angles of the platform:
- **[Product demos: Platform + Fraud & Risk](https://getaxonflow.com/demo/?utm_source=github&utm_medium=readme&utm_campaign=product_demo&utm_content=axonflow-openclaw-plugin)** - runtime enforcement, HITL approvals, audit evidence, cost visibility, and agentic payment controls
- **[Community Quickstart walkthrough (2 min)](https://youtu.be/BSqU1z0xxCo)** - governed calls, PII blocking, Gateway Mode with LangChain/CrewAI, and MAP from YAML
- **[Architecture deep dive (12 min)](https://youtu.be/Q2CZ1qnquhg)** - how the control plane works, policy enforcement flow, and multi-agent planning
### Plugin Evaluation Tier (Free 90-day License)
Outgrown Community on a real plugin install? Evaluation unlocks the capacity and features that matter for plugin users — without moving to Enterprise yet:
| Capability | Community | Evaluation (Free) | Enterprise |
|---|---|---|---|
| Tenant policies | 20 | 50 | Unlimited |
| Org-wide policies | 0 | 5 | Unlimited |
| Audit retention | 3 days | 14 days | Up to 10 years |
| HITL approval gates | — | 25 pending, 24h expiry | Unlimited, 24h |
| Evidence export (CSV/JSON) | — | 5,000 records · 14d window · 3/day | Unlimited |
| Policy simulation | — | 300/day | Unlimited |
| Session overrides (self-service unblock) | — | — | Enterprise-only |
Org-wide policies and session overrides are **Enterprise-only** — those are the actual upgrade triggers for plugin users.
[Get a free Plugin Evaluation license](https://getaxonflow.com/plugins/evaluation-license?utm_source=readme_plugin_openclaw_eval)
---
## Install
This is a **three-step** install: stand up the AxonFlow platform, add the plugin to OpenClaw, then point the plugin at the platform. The plugin alone does not enforce policy — it is a thin client that talks to an AxonFlow agent gateway. If the platform is not installed and reachable, governed tool calls have nothing to evaluate against. **Skipping Step 3 is the most common mistake**: the platform is running locally but the plugin still falls back to Community SaaS because no endpoint is configured.
### Step 1: install the AxonFlow platform
For any real workload, run AxonFlow on your own infrastructure via Docker Compose:
```bash
git clone https://github.com/getaxonflow/axonflow.git
cd axonflow && docker compose up -d
# verify
curl -s http://localhost:8080/health | jq .
```
Follow the [Getting Started](https://docs.getaxonflow.com/docs/getting-started/) guide for prerequisites (Docker Engine or Desktop, Docker Compose v2, 4 GB RAM, 10 GB disk) and the [Self-Hosted Deployment Guide](https://docs.getaxonflow.com/docs/deployment/self-hosted/) for production options. For production with real users or clients, run Community Edition with a free 90-day [Evaluation License](https://docs.getaxonflow.com/docs/deployment/evaluation-rollout-guide/) or [AxonFlow Enterprise](https://docs.getaxonflow.com/docs/deployment/community-to-enterprise-migration/).
> Skipping Step 1 makes the plugin fall back to the [Community SaaS](https://docs.getaxonflow.com/docs/deployment/community-saas/) endpoint at `try.getaxonflow.com` for early exploration only. **Do not skip Step 1 for any real workload** — see the [Privacy notice](#where-your-data-goes) above.
### Step 2: install the plugin
Requires OpenClaw **2026.4.15 or later** (CVE floor). **Strongly recommended: 2026.5.7 or later** — see [hash-mismatch workaround](#openclaw-cli-hash-mismatch) below. Upgrade with `npm install -g openclaw@latest`.
```bash
openclaw plugins install @axonflow/openclaw@latest
```
Current OpenClaw releases review plugin capabilities during installation and no longer accept `--accept-capabilities`. If an older supported CLI reports that capability consent is required, check `openclaw plugins install --help` and use that CLI version's consent option.
We recommend the npm-spec form as our primary install path. npm is our source-of-truth registry — every release is published there first, with the long track record and stability you'd expect from a mature package registry. The CLI resolves the npm spec and downloads the latest tarball directly.
To pin a specific version (recommended in production / CI):
```bash
openclaw plugins install @axonflow/openclaw@2.10.2
```
The `clawhub:@axonflow/openclaw` form is also supported and pulls from the [ClawHub](https://clawhub.ai/plugins/%40axonflow%2Fopenclaw) mirror, which we publish to alongside [npm](https://www.npmjs.com/package/@axonflow/openclaw) on each release.
<a id="openclaw-cli-hash-mismatch"></a>
<details>
<summary>Hit <code>ClawHub archive integrity mismatch</code>? Upgrade the CLI.</summary>
OpenClaw CLI versions before **2026.5.7** can hit a regression in the ClawHub install path's integrity check (confirmed on 2026.4.27). `openclaw plugins install @axonflow/openclaw` fails with:
```
ClawHub archive integrity mismatch for "@axonflow/openclaw@2.X.Y":
expected sha256-..., got sha256-...
```
The published artifact is byte-correct on both [npm](https://www.npmjs.com/package/@axonflow/openclaw) and [ClawHub](https://clawhub.ai/p/axonflow) (they serve identical bytes that match the manifest's pinned hash). The CLI computes a different hash internally and fails the comparison. Fixed in OpenClaw **2026.5.7**.
```bash
npm install -g openclaw@latest
openclaw plugins install @axonflow/openclaw@latest
```
If you cannot upgrade the CLI, fall back to a local-tgz install — the same path used for the older ENOENT bug below — which bypasses the broken integrity check:
```bash
TGZ=$(npm pack @axonflow/openclaw 2>/dev/null | tail -1)
openclaw plugins install "./$TGZ"
```
</details>
<details>
<summary>On a pre-2026.4.14 CLI? The ENOENT workaround still applies.</summary>
OpenClaw versions before 2026.4.14 had a bug ([openclaw/openclaw#66618](https://github.com/openclaw/openclaw/issues/66618)) that made scoped packages fail with `ENOENT .../openclaw-clawhub-package-XXXXXX/@axonflow/openclaw.zip` — both forms of the install command hit it. The fix shipped in 2026.4.14. If you cannot upgrade, install from npm directly:
```bash
# Captures the exact tgz filename so a stale tgz in CWD doesn't get picked up
TGZ=$(npm pack @axonflow/openclaw 2>/dev/null | tail -1)
openclaw plugins install "./$TGZ"
```
</details>
### Step 3: point the plugin at the platform
Without this step the plugin uses the Hosted endpoint; a fresh install directs you to check enrollment availability before verifying your email, completing Checkout, and linking the client through the Portal. Paid enrollment and Checkout are currently closed. It does not auto-detect a locally-running AxonFlow. Set `pluginConfig.endpoint` (and `clientId` / `clientSecret` if you have them) for a self-hosted stack:
```yaml
# openclaw.config.yaml
plugins:
"@axonflow/openclaw":
endpoint: http://localhost:8080 # or your remote AxonFlow URL
# clientId + clientSecret are required for Evaluation License or Enterprise tenants
```
Every plugin init logs a one-line canary on stderr confirming the active mode:
```
[AxonFlow] Connected to AxonFlow at http://localhost:8080 (mode=self-hosted)
```
If the canary says `mode=community-saas` after you ran Step 1, the plugin is still hitting `try.getaxonflow.com` because Step 3 was skipped or `pluginConfig.endpoint` is unset. Fix Step 3 and reload.
See [Configure](#configure) below for the full pluginConfig schema (`highRiskTools`, `governedTools`, `onError`, `userEmail`, etc.).
---
## Configure
[Step 3](#step-3-point-the-plugin-at-the-platform) above covers the minimum config (`endpoint` + optional `clientId` / `clientSecret`). The full pluginConfig schema is below.
### Full configuration reference
| Option | Required | Default | Description |
|--------|----------|---------|-------------|
| `endpoint` | No | `https://try.getaxonflow.com` (Community SaaS) when unset; `http://localhost:8080` when self-hosted with no endpoint specified | AxonFlow agent gateway URL |
| `clientId` | No | `"community"` (self-hosted) or the ID from a saved Hosted link | Tenant identity for data isolation. Override for Evaluation License or Enterprise tenants. |
| `clientSecret` | No | `""` (self-hosted) or the secret from a saved Hosted link | Basic-auth secret paired with `clientId`. Required for self-hosted Community Edition with an Evaluation License or AxonFlow Enterprise; supplied by device linking for a new Hosted account; can be left unset for self-hosted Community Edition without a license. |
| `licenseToken` | No | `process.env.AXONFLOW_LICENSE_TOKEN` if set | Legacy prepaid plugin-claim token. Existing 90-day purchases remain valid through expiry. New monthly hosted subscriptions need no token. Env var wins over `pluginConfig.licenseToken`. |
| `userEmail` | No | — | Per-user identity forwarded on explain/override calls. Shared agents should set this from session context. |
| `userToken` | No | `process.env.AXONFLOW_USER_TOKEN`, else `~/.config/axonflow/user-token.json` (mode `0600`) | Per-user authorization token, sent as `X-User-Token` on every governed request. Yields a **validated** `{identity, role}` on the platform's fleet plane — role-scoped reads return this developer's own rows (a token-less caller gets zero rows on those read tools against current platforms), and audit attribution keys on the token's canonical email rather than the forgeable `userEmail` label on the header-consuming planes (MCP tools + audit/decisions/overrides REST; the `check-input`/`check-output` hook plane keeps client-scoped attribution on current platforms). Minted by an org admin via the platform's user-token mint API. This config value wins over the env var, which wins over the provisioning file; a malformed candidate at any source is dropped with a warning (never sent) and resolution falls through to the next source. Unset: header omitted, wire behavior unchanged. |
| `highRiskTools` | No | `["web_fetch"]` | Tools that require human approval even when policy allows |
| `governedTools` | No | `[]` (all) | Tools to govern. Empty = all tools. |
| `excludedTools` | No | `[]` | Tools to exclude from governance. Takes precedence over `governedTools`. |
| `defaultOperation` | No | `"execute"` | Operation type for `check_input` (`"execute"` or `"query"`) |
| `onError` | No | `"block"` | Governs behavior on **auth/config errors only** (401/403). `"block"` denies the tool call with a message telling the operator to fix configuration; `"allow"` lets the call through ungoverned. Does not apply to network/transient errors — see Fail behavior below. |
| `requestTimeoutMs` | No | `8000` | Timeout for policy checks, output scans, audit writes, and health checks |
### Fail behavior
The plugin classifies errors from the AxonFlow client into two buckets and applies different rules per hook.
| Hook | Transient network error (timeout, DNS, connection refused, 5xx) | Auth/config error (401 / 403) | Payment required (402) |
|---|---|---|
| `before_tool_call` | Tools listed in `highRiskTools` require native OpenClaw approval. Other governed tools proceed regardless of `onError`, with a one-time warning. | Respects `onError`. The default blocks; with `"allow"`, tools in `highRiskTools` still require approval. | **Always blocks** and surfaces the Portal or available Checkout URL. |
| `message_sending` | Respects `onError`. The default cancels the message. | Same as network error. | **Always cancels**, regardless of `onError`. |
| `after_tool_call`, `llm_input`, `llm_output` (audit) | Always silently caught. Governance was already enforced on the pre-execution hook. | Always silently caught. |
The `before_tool_call` network fail-open is not silent. The first time a governed tool call proceeds without approval because the governance check failed for a non-auth reason — the endpoint was unreachable, timed out, or answered 5xx — the plugin emits one warning on the same channel as the auth-failure notice (`console.warn`, so it lands in the OpenClaw plugin log and in the terminal running the session), naming the endpoint, the underlying error, and the fact that the call ran with no policy evaluated. Tools in `highRiskTools` require native approval instead:
```
[AxonFlow] Governance check against http://localhost:8080 failed (fetch failed). This tool
call ran UNGOVERNED — no policy was evaluated, nothing was blocked, and no decision was
recorded. Tool calls continue to run ungoverned until the endpoint answers again; restore
it to resume enforcement. Shown once per process.
```
It appears once per process rather than once per tool call, mirroring the auth-failure notice, so a long outage does not flood the transcript.
Auth errors take the `onError` path above. With the default `onError: "block"`, governed calls are blocked; a 401 also trips the client's one-time authentication warning. With `onError: "allow"`, a 403 on a tool outside `highRiskTools` proceeds ungoverned and emits the one-time warning; a 401 uses the client's authentication warning. Tools in `highRiskTools` require native approval even in this path.
If you need tool-execution itself to fail-closed during an AxonFlow outage (for example on a production infrastructure agent), pair the plugin with an OpenClaw-side health check or a front-door liveness gate — the plugin alone will not achieve that for `before_tool_call`.
---
## Use-case recipes
### DevOps / coding agent — heavy exec usage
```yaml
plugins:
@axonflow/openclaw:
endpoint: http://localhost:8080
highRiskTools: [exec, process]
excludedTools: [get_current_time, list_models]
onError: block
```
### Customer support agent — Slack/Discord/Telegram
```yaml
plugins:
@axonflow/openclaw:
endpoint: http://localhost:8080
highRiskTools: [message, execute_sql, send_email]
onError: block
```
### Social automation agent — reviewed live actions
Use this pattern when an OpenClaw social plugin separates local catalog or discovery tools from live account actions. Keep free/local lookup tools outside approval gates, but require review for tools that can publish content, send direct messages, spend credits, export audience data, create monitors or webhooks, or run recurring workflows.
```yaml
plugins:
@axonflow/openclaw:
endpoint: http://localhost:8080
highRiskTools: [social_post, social_dm, social_export, social_monitor]
excludedTools: [social_catalog]
onError: block
```
### Self-healing infrastructure agent — highest risk
```yaml
plugins:
@axonflow/openclaw:
endpoint: http://localhost:8080
highRiskTools: [exec, process, web_fetch]
onError: block # auth-error path and message_sending fail-closed; see Fail behavior above
```
More examples — content/social agents, data analysts, RAG pipelines, and named ecosystem plugin recipes — in the [integration guide](https://docs.getaxonflow.com/docs/integration/openclaw/#use-case-configuration-examples).
---
## MCP tools available to your agent
Beyond the lifecycle hooks, OpenClaw agents can call **15 MCP tools** via the agent's MCP server at `/api/v1/mcp-server`. These are served by the platform (not the plugin), so new tools become available to every plugin without a code change.
**Governance (6):** `check_policy`, `check_output`, `audit_tool_call`, `list_policies`, `get_policy_stats`, `search_audit_events`
**Explainability & overrides (4):** `explain_decision`, `create_override`, `delete_override`, `list_overrides`
**Tenant identity and hosted capability (5):**
| Tool | Hosted Starter |
|------|----------------|
| `axonflow_get_tenant_id` | Returns tenant identity, endpoint, tier state, and authenticated checkout endpoint |
| `axonflow_list_pro_features` | Lists the capabilities reported by the current platform |
| `axonflow_request_approval` | Available within the published hosted limits |
| `axonflow_create_tenant_policy` | Available within the published hosted limits |
| `axonflow_get_cost_estimate` | Availability follows the server plan response |
OpenClaw also registers `axonflow_get_tenant_id` locally as a plugin agent tool. It returns `tenant_id`, `tier`, `checkout_url`, `checkout_method`, and legacy prepaid expiry fields.
When a tool call is blocked, the agent can surface the `decision_id` to the operator, call `explain_decision` to reveal the triggering policy family, and — if the decision is overridable — call `create_override` with mandatory justification for a short-lived, audit-logged exception. Operators never leave the OpenClaw session.
See [Decision Explainability](https://docs.getaxonflow.com/docs/governance/explainability/) and [Session Overrides](https://docs.getaxonflow.com/docs/governance/overrides/).
### Hosted payment and limit prompts
An unpaid hosted call receives HTTP 402. The plugin blocks the governed action even when `onError` is `allow` and surfaces a Portal link or an authenticated Checkout URL when one is available. Checkout is currently closed. Once paid enrollment opens, a paid account that reaches its monthly governed-event allowance receives HTTP 429; the plugin blocks that governed action even when `onError` is `allow`.
When Checkout is available, a blocked call may show:
```
[AxonFlow] Hosted governance needs an active plan. Complete checkout: https://checkout.stripe.com/...
```
Separate legacy Plugin Pro limits can stamp a local back-off file from `Retry-After`; this does not apply to the Hosted Starter monthly refusal. The legacy upgrade nudge is shown at most once per UTC day.
---
## What's covered today, and what's not
**Protected today:**
- Tool inputs before execution
- Outbound messages before delivery
- Tool and LLM audit trails (including search & explainability)
- Decision-level overrides with per-user attribution
**Not protected yet:**
- Tool results written into the session transcript (OpenClaw's `tool_result_persist` hook is synchronous and cannot call AxonFlow's HTTP APIs)
PII in tool results is still caught by `message_sending` before it reaches the end user, but it is visible to the LLM. When OpenClaw adds async support for `tool_result_persist`, this plugin will add transcript scanning immediately. Upstream issue: [openclaw/openclaw#58558](https://github.com/openclaw/openclaw/issues/58558).
---
## Latency
| Operation | Typical overhead |
|-----------|-----------------|
| Policy pre-check | 2–5 ms |
| PII / secrets detection | 1–3 ms |
| SQL-injection scan | 1–2 ms |
| Audit write (async) | 0 ms (non-blocking) |
| **Total per-tool overhead** | **3–10 ms** |
Imperceptible for interactive agents.
---
## Starter policies
The [policies directory](./policies) ships research-backed starter policies addressing the top 10 OpenClaw security risks — reverse shells, SSRF, credential exfiltration, path traversal, agent config poisoning, prompt injection, and more. Ready-to-use SQL INSERT statements and setup instructions included.
---
## Telemetry
The plugin sends a usage heartbeat once every 7 days so AxonFlow can understand adoption and environment shape. Includes plugin version, OS/arch, Node.js version, AxonFlow platform version, the licence tier, edition and deployment mode that platform reports about itself, this plugin's own deployment-mode and endpoint-type classification, org_id, and hook configuration summary. Also includes a persistent per-machine instance ID (UUID v4). **Never** includes message contents, tool arguments, or policy data.
The licence tier sent is whatever the platform reported about itself, relayed verbatim. The plugin does not normalise, map, or restrict the value, so a transient state such as `starting`, or a tier name introduced after this plugin shipped, reaches the wire unchanged rather than being flattened into a fixed list. What is never read or sent: **no licence key, no expiry date, no seat count, and no customer or organisation name**. It is read from the `tier` field of the same `/health` response the heartbeat already fetches to detect the platform version, so it costs no additional request, and it is omitted entirely whenever that probe does not answer with one.
Two further values are relayed on the same terms, from the same response: the platform's **edition** and the deployment mode the **platform reports about itself**. The second is deliberately separate from the `deployment_mode` above, which is this plugin's own classification of the endpoint it was pointed at — they answer different questions and routinely differ, so neither is written over the other. Both are omitted entirely whenever the platform does not report them — whether the member is missing or present as `null`, both of which mean not-learned — which is the case for every platform released before they existed. Any relayed value longer than 64 bytes is dropped whole rather than truncated, since a truncated value would be something the platform never said.
The heartbeat refuses HTTP redirects on both legs. A redirected `/health` teaches the plugin nothing rather than reading values from a host you did not configure, and a redirected checkpoint POST is not treated as a delivery — `fetch` would otherwise re-issue it as a bodyless GET whose success advanced the 7-day stamp on a ping that was never received, silencing this machine for a week.
Opt out: set `AXONFLOW_TELEMETRY=off` in the environment OpenClaw runs in.
### Scope of `AXONFLOW_TELEMETRY=off`
`AXONFLOW_TELEMETRY=off` disables the usage heartbeat described above. On **self-hosted** and **in-VPC** deployments, that heartbeat is the only data the plugin sends to AxonFlow, so setting `=off` means we receive nothing. On **Community SaaS** (`try.getaxonflow.com`) the hosted service also processes operational data — registrations, audit logs, policy enforcement records, workflow state, plan data, and request-header metadata aggregated for usage analytics — as part of running the platform; that operational data flow is governed by the [Privacy Policy](https://getaxonflow.com/privacy/), not by `AXONFLOW_TELEMETRY`.
### Network calls this plugin makes
| Call | When | What is sent | Opt out |
|---|---|---|---|
| Governance check | Every governed tool call / outbound message | Tool name + arguments, message body | Core functionality |
| Audit log | After each tool execution / LLM call | Tool name, result summary (truncated), prompt summary (first 500 chars), token usage, latency | Core functionality |
| Health check | On plugin init | Nothing (GET request) | Fire-and-forget |
| Usage heartbeat | Once per 7 days | Plugin version, OS, arch, Node version, platform version, the platform's licence tier, edition and own deployment mode, this plugin's deployment-mode and endpoint-type classification, org_id, hook config summary, persistent instance ID | `AXONFLOW_TELEMETRY=off` (also accepts `0`, `false`, `no`) |
| Platform version, licence tier, edition + platform deployment mode probe | During heartbeat (if not opted out) | Nothing (one GET to /health; all four fields are read from the same response, and redirects are refused) | Disabled when `AXONFLOW_TELEMETRY=off` |
| Version compatibility check | On plugin init | Plugin version | Fire-and-forget |
| Hosted account linking | When you run the [installed plugin's link command](#link-your-hosted-account) | Locally generated client secret to Portal device start, then an approval code to enter in the Portal | Don't run the CLI; use an explicit self-hosted endpoint for Community Edition instead |
| Credential recovery | Only when you run `axonflow-openclaw-recover` | The email address you pass on the command line, then the magic-link token you paste back | Don't run the CLI — it never fires from the plugin runtime |
---
## Testing
Unit tests (jest, mock fetch — no live stack needed):
```bash
npm test
```
Smoke E2E (requires a live AxonFlow stack at `localhost:8080`):
```bash
npm ci && npm run build
# Start a local AxonFlow stack first — `docker compose up -d` in
# the axonflow repo, or point AXONFLOW_ENDPOINT at an existing one.
node tests/e2e/smoke-block-context.mjs
```
The smoke scenario uses `AxonFlowClient.mcpCheckInput` to fire a SQLi-bearing statement against a running platform and asserts the response carries richer-context fields (`decision_id`, `risk_level`, `policy_matches`). Exits 0 with a `SKIP:` message if no stack is reachable.
For the broader validation story — explain-decision, override lifecycle, audit-filter parity, cache invalidation — see the [OpenClaw integration guide](https://docs.getaxonflow.com/docs/integration/openclaw/).
---
## Links
- **[OpenClaw Integration Guide](https://docs.getaxonflow.com/docs/integration/openclaw/)** — the full walkthrough (recommended starting point)
- [AxonFlow Documentation](https://docs.getaxonflow.com)
- [Policy Enforcement](https://docs.getaxonflow.com/docs/mcp/policy-enforcement/)
- [Decision Explainability](https://docs.getaxonflow.com/docs/governance/explainability/)
- [Session Overrides](https://docs.getaxonflow.com/docs/governance/overrides/)
- [PII Detection](https://docs.getaxonflow.com/docs/security/pii-detection/)
- [Audit Logging](https://docs.getaxonflow.com/docs/governance/audit-logging/)
- Sister plugins: [Claude Code](https://github.com/getaxonflow/axonflow-claude-plugin) · [Cursor](https://github.com/getaxonflow/axonflow-cursor-plugin) · [Codex](https://github.com/getaxonflow/axonflow-codex-plugin)
## License
MIT