openclaw-safeclaw-plugin 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,74 +1,247 @@
1
1
  # openclaw-safeclaw-plugin
2
2
 
3
- Neurosymbolic governance plugin for OpenClaw AI agents. Validates every tool call, message, and action against safety constraints before execution.
3
+ Neurosymbolic governance plugin for OpenClaw AI agents. Validates every tool call, message, and action against OWL ontologies and SHACL constraints before execution.
4
4
 
5
- ## Install
5
+ ## Installation
6
+
7
+ ### Via ClawHub (recommended)
8
+
9
+ ```bash
10
+ openclaw plugins install safeclaw
11
+ ```
12
+
13
+ ### Manual install
6
14
 
7
15
  ```bash
8
16
  npm install -g openclaw-safeclaw-plugin
17
+ safeclaw-plugin setup
18
+ safeclaw-plugin restart-openclaw
9
19
  ```
10
20
 
21
+ The `setup` command copies the plugin manifest to `~/.openclaw/extensions/safeclaw/` and enables it in `~/.openclaw/openclaw.json`. After install, restart OpenClaw to activate the plugin.
22
+
11
23
  ## Quick Start
12
24
 
13
- 1. Sign up at [safeclaw.eu](https://safeclaw.eu) and create an API key
14
- 2. Install and connect:
25
+ 1. Install the plugin (see above)
26
+ 2. Connect to SafeClaw (cloud or self-hosted):
15
27
 
16
28
  ```bash
17
- npm install -g openclaw-safeclaw-plugin
29
+ # Cloud
18
30
  safeclaw-plugin connect <your-api-key>
31
+
32
+ # Self-hosted
33
+ safeclaw-plugin config set serviceUrl http://localhost:8420/api/v1
34
+ ```
35
+
36
+ 3. Restart OpenClaw:
37
+
38
+ ```bash
19
39
  safeclaw-plugin restart-openclaw
20
40
  ```
21
41
 
22
- That's it. Every tool call your AI agent makes is now governed by SafeClaw.
42
+ Every tool call your AI agent makes is now governed by SafeClaw.
23
43
 
24
- ## Commands
44
+ ## Configuration
25
45
 
46
+ Configuration is resolved in this order (later sources override earlier ones):
47
+
48
+ 1. **Defaults** -- hardcoded in the plugin
49
+ 2. **Config file** -- `~/.safeclaw/config.json`
50
+ 3. **Environment variables** -- `SAFECLAW_*` prefixed vars
51
+ 4. **OpenClaw plugin config** -- values from `api.pluginConfig` (set via OpenClaw settings UI or `openclaw.json`)
52
+
53
+ ### Config file
54
+
55
+ Created automatically by `safeclaw-plugin connect`. Structure:
56
+
57
+ ```json
58
+ {
59
+ "enabled": true,
60
+ "remote": {
61
+ "serviceUrl": "http://localhost:8420/api/v1",
62
+ "apiKey": "sc_live_..."
63
+ },
64
+ "enforcement": {
65
+ "mode": "enforce",
66
+ "failMode": "open"
67
+ }
68
+ }
26
69
  ```
27
- safeclaw-plugin connect <api-key> Connect to SafeClaw and register with OpenClaw
28
- safeclaw-plugin setup Register plugin with OpenClaw (no key needed)
29
- safeclaw-plugin config show Show current plugin configuration
30
- safeclaw-plugin config set <k> <v> Set a plugin configuration value
31
- safeclaw-plugin tui Open the interactive settings TUI
32
- safeclaw-plugin restart-openclaw Restart the OpenClaw daemon
70
+
71
+ ### Environment variables
72
+
73
+ | Variable | Default | Description |
74
+ |----------|---------|-------------|
75
+ | `SAFECLAW_URL` | `http://localhost:8420/api/v1` | SafeClaw service URL |
76
+ | `SAFECLAW_API_KEY` | *(empty)* | API key for authentication |
77
+ | `SAFECLAW_TIMEOUT_MS` | `5000` | HTTP request timeout in milliseconds |
78
+ | `SAFECLAW_ENABLED` | `true` | Set `false` to disable the plugin entirely |
79
+ | `SAFECLAW_ENFORCEMENT` | `enforce` | Enforcement mode (see below) |
80
+ | `SAFECLAW_FAIL_MODE` | `open` | Fail mode (see below) |
81
+ | `SAFECLAW_AGENT_ID` | *(empty)* | Agent identifier for multi-agent governance |
82
+ | `SAFECLAW_AGENT_TOKEN` | *(empty)* | Agent authentication token |
83
+
84
+ ### OpenClaw plugin config
85
+
86
+ When running inside OpenClaw, the plugin reads `api.pluginConfig` which maps to the `configSchema` in `openclaw.plugin.json`. These values take priority over the config file. Set them via the OpenClaw settings UI or directly in `~/.openclaw/openclaw.json`:
87
+
88
+ ```json
89
+ {
90
+ "plugins": {
91
+ "entries": {
92
+ "safeclaw": {
93
+ "enabled": true,
94
+ "config": {
95
+ "enforcement": "enforce",
96
+ "failMode": "open",
97
+ "serviceUrl": "http://localhost:8420/api/v1"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
33
103
  ```
34
104
 
35
- ## What It Does
105
+ ## Enforcement Modes
36
106
 
37
- - **Blocks dangerous actions** — force push, deleting root, exposing secrets
38
- - **Enforces dependencies** — tests must pass before git push
39
- - **Checks user preferences** confirmation for irreversible actions
40
- - **Governs messages** blocks sensitive data leaks
41
- - **Full audit trail** every decision logged with ontological justification
107
+ | Mode | Behavior |
108
+ |------|----------|
109
+ | `enforce` | Block tool calls and messages that violate constraints. Recommended for production. |
110
+ | `warn-only` | Log warnings but allow all actions through. Useful during initial rollout. |
111
+ | `audit-only` | Server-side logging only, no client-side warnings or blocks. |
112
+ | `disabled` | Plugin is completely inactive. No HTTP calls to the service. |
42
113
 
43
- ## How It Works
114
+ ## Fail Modes
44
115
 
45
- The plugin registers hooks on OpenClaw events:
116
+ Controls what happens when the SafeClaw service is unreachable:
46
117
 
47
- 1. **before_tool_call** validates against SHACL shapes, policies, preferences, dependencies
48
- 2. **before_agent_start** — injects governance context into the agent's system prompt
49
- 3. **message_sending** checks outbound messages for sensitive data
50
- 4. **after_tool_call** records action outcomes for dependency tracking
51
- 5. **llm_input/output** — logs LLM interactions for audit
118
+ | Mode | Behavior |
119
+ |------|----------|
120
+ | `open` | Allow all actions when the service is unavailable. Default. |
121
+ | `closed` | Block all actions when the service is unavailable. Use when safety is critical. |
52
122
 
53
- ## Configuration
123
+ ## Hooks
54
124
 
55
- Set via environment variables or `~/.safeclaw/config.json`:
125
+ The plugin registers 11 hooks on OpenClaw events. Each hook communicates with the SafeClaw service via HTTP.
56
126
 
57
- | Variable | Default | Description |
58
- |----------|---------|-------------|
59
- | `SAFECLAW_URL` | `https://api.safeclaw.eu/api/v1` | SafeClaw service URL |
60
- | `SAFECLAW_API_KEY` | *(empty)* | API key (set automatically by `safeclaw-plugin connect`) |
61
- | `SAFECLAW_TIMEOUT_MS` | `5000` | Request timeout in ms |
62
- | `SAFECLAW_ENABLED` | `true` | Set `false` to disable |
63
- | `SAFECLAW_ENFORCEMENT` | `enforce` | `enforce`, `warn-only`, `audit-only`, or `disabled` |
64
- | `SAFECLAW_FAIL_MODE` | `open` | `open` (allow on failure) or `closed` (block on failure) |
127
+ ### Blocking hooks (can prevent actions)
65
128
 
66
- ## Enforcement Modes
129
+ | Hook | Priority | Description |
130
+ |------|----------|-------------|
131
+ | `before_tool_call` | 100 | The main gate. Evaluates every tool call against SHACL shapes, policies, preferences, and dependencies. Returns `{ block: true }` if the action violates constraints. |
132
+ | `message_sending` | 100 | Checks outbound messages for sensitive data leaks, contact rule violations, and content policy. Returns `{ cancel: true }` to block. |
133
+ | `subagent_spawning` | 100 | Evaluates child agent spawn requests. Detects delegation bypass attempts where a blocked parent tries to spawn an unrestricted child. |
134
+
135
+ ### Context hooks (modify agent behavior)
136
+
137
+ | Hook | Priority | Description |
138
+ |------|----------|-------------|
139
+ | `before_prompt_build` | 100 | Injects governance context into the agent system prompt via `prependSystemContext`. Tells the agent what constraints are active. |
140
+
141
+ ### Recording hooks (fire-and-forget)
142
+
143
+ | Hook | Description |
144
+ |------|-------------|
145
+ | `after_tool_call` | Records tool execution results (success/failure, duration, errors) for dependency tracking and audit. |
146
+ | `llm_input` | Logs the prompt sent to the LLM, including provider and model name. |
147
+ | `llm_output` | Logs the LLM response, including token usage. |
148
+ | `subagent_ended` | Records child agent lifecycle completion. |
149
+ | `session_start` | Notifies the service when a new session begins. |
150
+ | `session_end` | Notifies the service when a session ends. |
151
+ | `message_received` | Evaluates inbound messages for governance (sender, channel, content). |
152
+
153
+ ## Agent Tools
154
+
155
+ The plugin registers two tools that agents can call to introspect governance state.
156
+
157
+ ### `safeclaw_status`
158
+
159
+ Returns the current governance status. No parameters.
160
+
161
+ ```json
162
+ {
163
+ "status": "ok",
164
+ "enforcement": "enforce",
165
+ "failMode": "open",
166
+ "serviceUrl": "http://localhost:8420/api/v1",
167
+ "handshakeCompleted": true
168
+ }
169
+ ```
170
+
171
+ ### `safeclaw_check_action`
172
+
173
+ Dry-run check of whether a tool call would be allowed. No side effects.
174
+
175
+ **Parameters:**
176
+ - `toolName` (string, required) -- tool name to check
177
+ - `params` (object, optional) -- tool parameters to validate
178
+
179
+ ```json
180
+ {
181
+ "block": false,
182
+ "reason": null,
183
+ "constraints": ["shacl:ActionShape", "policy:NoForceOnMain"]
184
+ }
185
+ ```
186
+
187
+ ## CLI Commands
188
+
189
+ The plugin ships a standalone CLI (`safeclaw-plugin`) and registers a `safeclaw` subcommand in the OpenClaw CLI via `api.registerCli`.
190
+
191
+ ### Standalone CLI
192
+
193
+ ```
194
+ safeclaw-plugin connect <api-key> Save API key, validate via handshake, register with OpenClaw
195
+ safeclaw-plugin setup Register plugin with OpenClaw (no key needed)
196
+ safeclaw-plugin restart-openclaw Restart the OpenClaw daemon
197
+ safeclaw-plugin status Run diagnostics (config, service, handshake, OpenClaw, NemoClaw)
198
+ safeclaw-plugin config show Show current configuration
199
+ safeclaw-plugin config set <k> <v> Set a config value (enforcement, failMode, enabled, serviceUrl)
200
+ safeclaw-plugin tui Open interactive settings TUI
201
+ ```
202
+
203
+ ### OpenClaw CLI extension
204
+
205
+ When loaded by OpenClaw, the plugin adds:
206
+
207
+ ```
208
+ openclaw safeclaw status Show SafeClaw service status and enforcement mode
209
+ ```
210
+
211
+ ## NemoClaw Sandbox
212
+
213
+ When running inside a NemoClaw sandbox (detected via the `OPENSHELL_SANDBOX` environment variable), the plugin automatically adjusts:
214
+
215
+ - **Service URL**: `localhost` is rewritten to `host.containers.internal` since the sandbox runs in a container and cannot reach the host's loopback interface directly.
216
+ - **Egress policy**: The bundled `policies/safeclaw.yaml` defines the network rules NemoClaw needs to allow SafeClaw traffic.
217
+
218
+ ### Setup
219
+
220
+ 1. Copy the egress policy into your NemoClaw configuration:
221
+
222
+ ```bash
223
+ nemoclaw policy-add safeclaw
224
+ ```
225
+
226
+ Or manually copy `policies/safeclaw.yaml` to your NemoClaw policy directory.
227
+
228
+ 2. The policy allows two destinations:
229
+ - `api.safeclaw.eu:443` (HTTPS) -- cloud service
230
+ - `host.containers.internal:8420` (HTTP) -- self-hosted service on the host machine
231
+
232
+ 3. No additional configuration is needed. The plugin detects the sandbox automatically and adjusts the service URL.
233
+
234
+ ## Architecture
235
+
236
+ This plugin is a thin HTTP bridge (~450 lines). All governance logic lives in the SafeClaw Python service. The plugin:
237
+
238
+ 1. Registers hooks on OpenClaw events
239
+ 2. Forwards event data to the SafeClaw service via HTTP POST
240
+ 3. Acts on the service response (block, warn, or allow)
241
+ 4. Sends a heartbeat every 30 seconds with config hash
242
+ 5. Registers as an OpenClaw service for clean lifecycle management (no `process.exit()`)
67
243
 
68
- - **`enforce`** block actions that violate constraints (recommended)
69
- - **`warn-only`** — log warnings but allow all actions
70
- - **`audit-only`** — server-side logging only, no client-side action
71
- - **`disabled`** — plugin is completely inactive
244
+ The plugin performs a handshake with the service on startup to validate the API key and confirm the engine is ready. If the handshake fails and `failMode` is `closed`, all tool calls are blocked until the service becomes reachable.
72
245
 
73
246
  ## License
74
247
 
package/SKILL.md CHANGED
@@ -1,48 +1,49 @@
1
- # SafeClaw Neurosymbolic Governance for OpenClaw
1
+ # SafeClaw -- Neurosymbolic Governance for OpenClaw
2
2
 
3
- SafeClaw adds ontology-based constraint checking to your OpenClaw agent. Every tool call, message, and action is validated against OWL ontologies and SHACL shapes before execution.
3
+ SafeClaw validates every tool call, message, and agent action against OWL ontologies and SHACL constraints before execution. It acts as a governance gate between your AI agent and the tools it uses.
4
4
 
5
5
  ## What it does
6
6
 
7
- - **Blocks dangerous actions** force push, deleting root, exposing secrets
8
- - **Enforces dependencies** tests must pass before git push
9
- - **Checks user preferences** confirmation for irreversible actions based on autonomy level
10
- - **Governs messages** blocks sensitive data leaks, enforces never-contact lists
11
- - **Full audit trail** every decision logged with ontological justification
7
+ - **Blocks dangerous actions** -- force push, deleting root, exposing secrets
8
+ - **Enforces dependencies** -- tests must pass before git push
9
+ - **Checks user preferences** -- confirmation for irreversible actions based on autonomy level
10
+ - **Governs messages** -- blocks sensitive data leaks, enforces contact rules
11
+ - **Controls subagent delegation** -- prevents blocked parents from spawning unrestricted children
12
+ - **Full audit trail** -- every decision logged with ontological justification
12
13
 
13
- ## Setup
14
+ ## Hooks
14
15
 
15
- The plugin connects to `https://api.safeclaw.eu/api/v1` by default — no configuration needed.
16
+ 11 hooks covering the full agent lifecycle:
16
17
 
17
- ### Self-hosted mode
18
+ - `before_tool_call` -- constraint gate for every tool invocation
19
+ - `before_prompt_build` -- injects governance context into system prompt
20
+ - `message_sending` -- outbound message governance
21
+ - `message_received` -- inbound message evaluation
22
+ - `llm_input` / `llm_output` -- LLM interaction audit logging
23
+ - `after_tool_call` -- records outcomes for dependency tracking
24
+ - `subagent_spawning` / `subagent_ended` -- multi-agent governance
25
+ - `session_start` / `session_end` -- session lifecycle tracking
18
26
 
19
- To run your own SafeClaw service, override the URL:
27
+ ## Agent tools
20
28
 
21
- ```bash
22
- export SAFECLAW_URL="http://localhost:8420/api/v1"
23
- export SAFECLAW_API_KEY="sc_live_your_key_here" # optional
24
- ```
29
+ - `safeclaw_status` -- check governance service status and active enforcement mode
30
+ - `safeclaw_check_action` -- dry-run check if a specific tool call would be allowed
25
31
 
26
32
  ## Configuration
27
33
 
28
- Set via environment variables or `~/.safeclaw/config.json`:
34
+ Set via OpenClaw plugin settings, `~/.safeclaw/config.json`, or `SAFECLAW_*` environment variables. Supports four enforcement modes (`enforce`, `warn-only`, `audit-only`, `disabled`) and two fail modes (`open`, `closed`).
35
+
36
+ ### NemoClaw sandbox
29
37
 
30
- | Variable | Default | Description |
31
- |----------|---------|-------------|
32
- | `SAFECLAW_URL` | `https://api.safeclaw.eu/api/v1` | SafeClaw service URL |
33
- | `SAFECLAW_API_KEY` | (empty) | API key for remote/cloud mode |
34
- | `SAFECLAW_TIMEOUT_MS` | `500` | Request timeout in milliseconds |
35
- | `SAFECLAW_ENABLED` | `true` | Set to `false` to disable |
36
- | `SAFECLAW_ENFORCEMENT` | `enforce` | `enforce`, `warn-only`, `audit-only`, or `disabled` |
38
+ Automatically detects NemoClaw sandboxes and rewrites `localhost` to `host.containers.internal`. Includes a bundled egress policy at `policies/safeclaw.yaml`.
37
39
 
38
- ## How it works
40
+ ### Self-hosted
39
41
 
40
- This plugin registers hooks on every OpenClaw event:
42
+ Run the SafeClaw service locally:
41
43
 
42
- 1. **before_tool_call** — validates against SHACL shapes, policies, preferences, dependencies
43
- 2. **before_agent_start** — injects governance context into the agent's system prompt
44
- 3. **message_sending** — checks outbound messages for sensitive data and contact rules
45
- 4. **after_tool_call** — records action outcomes for dependency tracking
46
- 5. **llm_input/output** — logs LLM interactions for audit
44
+ ```bash
45
+ pip install safeclaw
46
+ safeclaw serve
47
+ ```
47
48
 
48
- If the SafeClaw service is unavailable, the plugin degrades gracefully no blocks, no crashes.
49
+ The plugin connects to `http://localhost:8420/api/v1` by default.
package/cli.tsx CHANGED
@@ -7,7 +7,7 @@ import { join, dirname } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import { fileURLToPath } from 'url';
9
9
  import App from './tui/App.js';
10
- import { loadConfig, saveConfig, type SafeClawConfig } from './tui/config.js';
10
+ import { loadConfig, saveConfig, isNemoClawSandbox, getSandboxName, type SafeClawConfig } from './tui/config.js';
11
11
 
12
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
13
  const PKG_VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')).version as string;
@@ -464,6 +464,13 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
464
464
  allOk = false;
465
465
  }
466
466
 
467
+ // 9. NemoClaw sandbox
468
+ if (isNemoClawSandbox()) {
469
+ console.log(`[ok] NemoClaw sandbox: ${getSandboxName()}`);
470
+ } else {
471
+ console.log('[--] NemoClaw: not in sandbox (standalone mode)');
472
+ }
473
+
467
474
  // Summary
468
475
  console.log('');
469
476
  if (allOk) {
@@ -483,8 +490,8 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
483
490
  console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
484
491
  console.log('');
485
492
  console.log('Diagnostics:');
486
- console.log(' status Run 8 checks: config, API key, service health, evaluate endpoint,');
487
- console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config');
493
+ console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
494
+ console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
488
495
  console.log('');
489
496
  console.log('Configuration:');
490
497
  console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { join, dirname } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import { fileURLToPath } from 'url';
9
9
  import App from './tui/App.js';
10
- import { loadConfig, saveConfig } from './tui/config.js';
10
+ import { loadConfig, saveConfig, isNemoClawSandbox, getSandboxName } from './tui/config.js';
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  const PKG_VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')).version;
13
13
  function readJson(path) {
@@ -473,6 +473,13 @@ else if (command === 'status') {
473
473
  console.log('[!!] OpenClaw config: not found');
474
474
  allOk = false;
475
475
  }
476
+ // 9. NemoClaw sandbox
477
+ if (isNemoClawSandbox()) {
478
+ console.log(`[ok] NemoClaw sandbox: ${getSandboxName()}`);
479
+ }
480
+ else {
481
+ console.log('[--] NemoClaw: not in sandbox (standalone mode)');
482
+ }
476
483
  // Summary
477
484
  console.log('');
478
485
  if (allOk) {
@@ -494,8 +501,8 @@ else {
494
501
  console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
495
502
  console.log('');
496
503
  console.log('Diagnostics:');
497
- console.log(' status Run 8 checks: config, API key, service health, evaluate endpoint,');
498
- console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config');
504
+ console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
505
+ console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
499
506
  console.log('');
500
507
  console.log('Configuration:');
501
508
  console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
package/dist/index.d.ts CHANGED
@@ -6,25 +6,11 @@
6
6
  * This plugin is a thin HTTP bridge that forwards OpenClaw events
7
7
  * to the SafeClaw service and acts on the responses.
8
8
  */
9
- interface PluginEvent {
10
- sessionId?: string;
11
- userId?: string;
12
- [key: string]: unknown;
13
- }
14
- interface PluginContext {
15
- sessionId?: string;
16
- userId?: string;
17
- [key: string]: unknown;
18
- }
19
- interface PluginApi {
20
- on(event: string, handler: (event: PluginEvent, ctx: PluginContext) => Promise<Record<string, unknown> | void> | void, options?: {
21
- priority?: number;
22
- }): void;
23
- }
9
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/core';
24
10
  declare const _default: {
25
11
  id: string;
26
12
  name: string;
27
13
  version: string;
28
- register(api: PluginApi): void;
14
+ register(api: OpenClawPluginApi): void;
29
15
  };
30
16
  export default _default;