shroud-privacy 2.0.20 → 2.2.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
@@ -5,7 +5,7 @@
5
5
  <h1 align="center">Shroud — Community Edition</h1>
6
6
 
7
7
  <p align="center">
8
- Privacy obfuscation plugin for <a href="https://openclaw.ai">OpenClaw</a>. Detects sensitive data (PII, network infrastructure, credentials) and replaces it with deterministic fake values before anything reaches the LLM. Tool calls still work because Shroud deobfuscates on the way back.
8
+ Privacy obfuscation for AI agents. Detects sensitive data (PII, network infrastructure, credentials) and replaces it with deterministic fake values before anything reaches the LLM. Tool calls still work because Shroud deobfuscates on the way back. Works with <a href="https://openclaw.ai">OpenClaw</a> (plugin) or any agent via the Agent Privacy Protocol (APP).
9
9
  </p>
10
10
 
11
11
  > **Open-source Community Edition** — free to use under Apache 2.0 license. [Enterprise Edition](#enterprise-edition) available with additional features for teams.
@@ -26,57 +26,94 @@
26
26
  | `before_tool_call` | LLM → Tool | Deobfuscate tool parameters + track tool chain depth |
27
27
  | `tool_result_persist` | Tool → History | Obfuscate tool results before storing |
28
28
  | `message_sending` | Agent → User | Deobfuscate outbound messages (all channels) |
29
+ | `globalThis.__shroudDeobfuscate` | Agent → Channel | Global deobfuscation hook — called by OpenClaw before ANY channel send |
29
30
 
30
- > **Streaming deobfuscation:** On first load, Shroud patches pi-ai's EventStream to deobfuscate ALL LLM responses at the stream level every provider (Anthropic, OpenAI, Google), every channel (Slack, WhatsApp, Telegram, etc.). A single gateway restart activates the patch.
31
+ > **Privacy guarantee:** Shroud intercepts ALL outbound LLM API calls (Anthropic, OpenAI, Google, any provider) at the `fetch` level and obfuscates PII in every message including assistant history and Slack `<mailto:>` markup — before it leaves the process. No PII reaches the LLM. On the channel delivery side, Shroud registers `globalThis.__shroudDeobfuscate` — a single function that OpenClaw calls before sending to ANY channel (Slack, WhatsApp, Signal, web, etc.). One hook, all channels, transparent no-op if Shroud isn't loaded.
32
+
33
+ > **Requires OpenClaw 2026.3.24 or later** with the channel delivery patch (see [OpenClaw patch](#openclaw-channel-delivery-patch) below).
31
34
 
32
35
  ## Install
33
36
 
34
- ### OpenClaw
37
+ ### OpenClaw (2026.3.24+)
35
38
 
36
39
  ```bash
40
+ # Ensure you're on OpenClaw 2026.3.24 or later
41
+ openclaw --version
42
+
43
+ # Install Shroud
37
44
  openclaw plugins install shroud-privacy
38
45
  ```
39
46
 
40
- That's it. Configure in `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`.
47
+ Configure in `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`. No OpenClaw file modifications needed — Shroud uses runtime prototype patches only.
41
48
 
42
- ### From source (development)
49
+ ### Any agent (via APP)
50
+
51
+ The **Agent Privacy Protocol** (APP) lets any AI agent add privacy obfuscation — no OpenClaw required. Shroud ships with an APP server and a Python client.
43
52
 
44
53
  ```bash
45
- git clone https://github.com/walterkeating-stack/shroud.git
46
- cd shroud
47
- npm install && npm run build
48
- bash deploy-local.sh # → OpenClaw (~/.openclaw/extensions/)
54
+ npm install shroud-privacy
49
55
  ```
50
56
 
51
- ## Updating
57
+ **Python:**
52
58
 
53
- OpenClaw doesn't have a `plugins update` command yet, so updating requires removing the old install first. A helper script is included:
59
+ ```python
60
+ from shroud_client import ShroudClient
54
61
 
55
- ```bash
56
- # Update to latest version (preserves your config)
57
- bash scripts/update-openclaw-plugin.sh
62
+ with ShroudClient() as shroud:
63
+ # Before sending to LLM
64
+ result = shroud.obfuscate("Contact admin@acme.com about 10.1.0.1")
65
+ send_to_llm(result.text) # "Contact user@example.net about 100.64.0.12"
58
66
 
59
- # Update to a specific version
60
- bash scripts/update-openclaw-plugin.sh <version>
67
+ # After receiving from LLM
68
+ restored = shroud.deobfuscate(llm_response)
69
+ show_to_user(restored.text) # original values restored
61
70
  ```
62
71
 
63
- The script saves your plugin config from `openclaw.json`, removes the old extension, reinstalls from npm, restores your config, and restarts the gateway.
72
+ Copy `clients/python/shroud_client.py` into your project, or import it directly from the npm install path. Requires Node.js on the PATH.
64
73
 
65
- ### Manual update
74
+ **Any language:**
66
75
 
67
- If you prefer to do it manually:
76
+ Spawn the APP server and talk JSON-RPC over stdin/stdout:
68
77
 
69
78
  ```bash
70
- # 1. Remove old plugin files
71
- rm -rf ~/.openclaw/extensions/shroud-privacy
79
+ node node_modules/shroud-privacy/app-server.mjs node_modules/shroud-privacy/dist
80
+ ```
72
81
 
73
- # 2. Reinstall (this resets your plugin config to defaults)
74
- openclaw plugins install shroud-privacy
82
+ Handshake (server writes on startup):
83
+ ```json
84
+ {"app":"1.0","engine":"shroud","version":"2.1.0","capabilities":["obfuscate","deobfuscate","batch","stats","health","configure","audit","partitions"]}
85
+ ```
75
86
 
76
- # 3. Re-apply your config in ~/.openclaw/openclaw.json
77
- # (under plugins.entries."shroud-privacy".config)
87
+ Obfuscate:
88
+ ```json
89
+ → {"id":1,"method":"obfuscate","params":{"text":"Contact admin@acme.com"}}
90
+ ← {"id":1,"result":{"text":"Contact user@example.net","entityCount":1,"categories":{"email":1},"modified":true}}
91
+ ```
92
+
93
+ Deobfuscate:
94
+ ```json
95
+ → {"id":2,"method":"deobfuscate","params":{"text":"Contact user@example.net"}}
96
+ ← {"id":2,"result":{"text":"Contact admin@acme.com","replacementCount":1,"modified":true}}
97
+ ```
78
98
 
79
- # 4. Restart
99
+ Other methods: `reset`, `stats`, `health`, `configure`, `shutdown`.
100
+
101
+ ### From source (development)
102
+
103
+ ```bash
104
+ git clone https://github.com/walterkeating-stack/shroud.git
105
+ cd shroud
106
+ npm install && npm run build
107
+ openclaw plugins install --path .
108
+ openclaw gateway restart
109
+ ```
110
+
111
+ ## Updating
112
+
113
+ ```bash
114
+ # Remove old plugin, reinstall from npm, restart
115
+ openclaw plugins remove shroud-privacy
116
+ openclaw plugins install shroud-privacy
80
117
  openclaw gateway restart
81
118
  ```
82
119
 
@@ -195,16 +232,35 @@ alias shroud-stats="node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-st
195
232
 
196
233
  The CLI reads live stats from `/tmp/shroud-stats.json` (override with `SHROUD_STATS_FILE` env var). The stats file is updated by the running gateway on every obfuscation event.
197
234
 
198
- ### Auto-patching on first install
235
+ ### How privacy works
236
+
237
+ Shroud uses runtime prototype patches — **no OpenClaw files are modified**:
238
+
239
+ **Outbound (PII → LLM):** Patches `globalThis.fetch` to intercept all POST requests to LLM API endpoints (`/messages`, `/chat/completions`). Obfuscates every message in the request body — user, assistant, system, tool results — before the request leaves the process. Strips Slack `<mailto:>` markup to prevent PII leaking through chat formatting. Re-obfuscates deobfuscated assistant messages in conversation history to prevent multi-turn PII leaks. Works for every LLM provider.
240
+
241
+ **Inbound (LLM → User):** Patches `EventStream.prototype.push()` to deobfuscate streaming responses in real-time. Fake values are replaced with real values as they stream.
242
+
243
+ **Channel delivery:** Shroud registers `globalThis.__shroudDeobfuscate(text)` — a single global function that converts fake values back to real values. OpenClaw calls this once in its generic message delivery function, before sending to any channel. If Shroud isn't loaded, the function doesn't exist — transparent no-op. The `message_sending` hook provides a backup deobfuscation path.
199
244
 
200
- On first load, Shroud automatically patches pi-ai's `EventStream.push()` to enable streaming deobfuscation across all LLM providers and delivery channels. The patch:
245
+ All patches are applied once at plugin load and are idempotent subsequent loads detect and skip them.
246
+
247
+ ### OpenClaw channel delivery patch
248
+
249
+ Shroud requires a one-line addition to OpenClaw's message delivery function — the single point where all outbound channel messages pass through. This covers ALL channels (Slack, WhatsApp, Signal, Telegram, web, etc.) with one change:
250
+
251
+ ```js
252
+ // In OpenClaw's generic message delivery function:
253
+ const deob = globalThis.__shroudDeobfuscate;
254
+ if (deob && typeof text === 'string') text = deob(text);
255
+ ```
201
256
 
202
- 1. Backs up the original file (`.shroud-backup`)
203
- 2. Injects a 4-line hook that calls `globalThis.__shroudStreamDeobfuscate`
204
- 3. Clears the Node.js V8 compile cache
205
- 4. Triggers a gateway restart via SIGUSR1
257
+ **Why this works:**
258
+ - **One change, all channels** no per-channel hooks needed
259
+ - **Transparent** — if Shroud isn't loaded, `globalThis.__shroudDeobfuscate` is `undefined`, the `if` is false, zero overhead
260
+ - **All releases** — works on any OpenClaw version that sends channel messages through a common delivery path
261
+ - **Last-moment deobfuscation** — fakes are replaced with real values at the latest possible point, just before the HTTP API call
206
262
 
207
- On subsequent loads, the patch is detected and skipped. To revert: restore the `.shroud-backup` file and restart.
263
+ The `deploy-local.sh` script includes a post-install verification step that tests the full chain.
208
264
 
209
265
  ### Rule hit counters
210
266
 
@@ -303,6 +359,109 @@ With proof hashes enabled:
303
359
 
304
360
  OpenClaw logs each plugin message twice (once under the plugin subsystem logger, once under the parent `openclaw` logger). This is normal OpenClaw behavior. Filter to `"name":"openclaw"` to get one line per event, as shown in the verify command above.
305
361
 
362
+ ## Agent Privacy Protocol (APP)
363
+
364
+ APP is an open protocol for adding privacy obfuscation to any AI agent. Shroud is the reference implementation.
365
+
366
+ ### Overview
367
+
368
+ ```
369
+ ┌─────────────────┐ stdin/stdout ┌──────────────────┐
370
+ │ Your Agent │ ◄──── JSON-RPC ────► │ APP Server │
371
+ │ (any language) │ │ (app-server.mjs)│
372
+ └─────────────────┘ └──────────────────┘
373
+ │ │
374
+ │ 1. obfuscate(user_input) │ detects PII,
375
+ │ 2. send to LLM ──────────────► │ returns fakes
376
+ │ 3. deobfuscate(llm_response) │ restores reals
377
+ │ 4. show to user │
378
+ ```
379
+
380
+ ### Protocol specification
381
+
382
+ - **Transport**: Newline-delimited JSON-RPC 2.0 over stdin/stdout
383
+ - **Encoding**: UTF-8
384
+ - **Process model**: Agent spawns APP server as subprocess, one per agent instance
385
+
386
+ ### Handshake
387
+
388
+ On startup, the server writes a single JSON line to stdout:
389
+
390
+ ```json
391
+ {"app":"1.0","engine":"shroud","version":"2.1.0","capabilities":["obfuscate","deobfuscate","batch","stats","health","configure","audit","partitions"]}
392
+ ```
393
+
394
+ The agent must read this line before sending requests. Fields:
395
+ - `app` — protocol version (always `"1.0"`)
396
+ - `engine` — implementation name
397
+ - `version` — implementation version
398
+ - `capabilities` — supported methods
399
+
400
+ ### Methods
401
+
402
+ | Method | Params | Returns | Description |
403
+ |--------|--------|---------|-------------|
404
+ | `obfuscate` | `{text}` | `{text, entityCount, categories, modified, audit}` | Replace real values with fakes |
405
+ | `deobfuscate` | `{text}` | `{text, replacementCount, modified, audit}` | Restore fakes to real values |
406
+ | `reset` | `{}` | `{ok, summary}` | Clear all mappings |
407
+ | `stats` | `{}` | `{storeMappings, ruleHits, ...}` | Engine statistics |
408
+ | `health` | `{}` | `{uptime, requests, avgLatencyMs}` | Liveness check |
409
+ | `configure` | `{config}` | `{ok}` | Hot-reload configuration |
410
+ | `batch` | `{operations: [{direction, text}]}` | `{results: [...]}` | Batch obfuscate/deobfuscate |
411
+ | `shutdown` | `{}` | `{ok}` | Graceful shutdown (flushes stats) |
412
+
413
+ ### Request/response format
414
+
415
+ ```
416
+ → {"id":1,"method":"obfuscate","params":{"text":"Server 10.1.0.1 is down"}}
417
+ ← {"id":1,"result":{"text":"Server 100.64.0.12 is down","entityCount":1,"categories":{"ip_address":1},"modified":true,"audit":{"requestId":"a1b2c3","proofIn":"8a3c1f","proofOut":"f7d2a1"}}}
418
+ ```
419
+
420
+ Errors:
421
+ ```
422
+ ← {"id":1,"error":{"code":-32602,"message":"Missing required param: text"}}
423
+ ```
424
+
425
+ ### Heartbeat
426
+
427
+ The server writes JSON heartbeats to stderr every 30 seconds:
428
+ ```json
429
+ {"heartbeat":true,"pid":12345,"uptime":120,"requests":42,"avgLatencyMs":1.2,"storeSize":15,"memoryMB":28}
430
+ ```
431
+
432
+ ### Integration checklist
433
+
434
+ 1. `npm install shroud-privacy`
435
+ 2. Spawn: `node node_modules/shroud-privacy/app-server.mjs node_modules/shroud-privacy/dist`
436
+ 3. Read handshake line from stdout
437
+ 4. Before LLM: send `obfuscate`, use returned `text`
438
+ 5. After LLM: send `deobfuscate`, show returned `text` to user
439
+ 6. On agent shutdown: send `shutdown`
440
+
441
+ ### Python client
442
+
443
+ A ready-made Python client is included at `clients/python/shroud_client.py`:
444
+
445
+ ```python
446
+ from shroud_client import ShroudClient
447
+
448
+ client = ShroudClient()
449
+ client.start()
450
+
451
+ safe = client.obfuscate("Contact admin@acme.com about 10.1.0.1")
452
+ print(safe.text) # fakes
453
+ print(safe.entity_count) # 2
454
+ print(safe.categories) # {"email": 1, "ip_address": 1}
455
+
456
+ real = client.deobfuscate(llm_response)
457
+ print(real.text) # originals restored
458
+ print(real.residual_fakes) # any CGNAT/ULA IPs that survived
459
+
460
+ client.stop()
461
+ ```
462
+
463
+ Supports context manager, auto-restart on crash, residual fake detection, and hot-reload via `configure()`.
464
+
306
465
  ## Development
307
466
 
308
467
  ```bash
@@ -316,7 +475,7 @@ npm run lint # type-check without emitting
316
475
 
317
476
  ```bash
318
477
  npm run build
319
- bash deploy-local.sh # OpenClaw (~/.openclaw/extensions/shroud-privacy/)
478
+ openclaw plugins install --path .
320
479
  openclaw gateway restart
321
480
  ```
322
481
 
@@ -329,8 +488,8 @@ openclaw gateway restart
329
488
  # 2. Update CHANGELOG.md
330
489
  # 3. Commit and tag
331
490
  git add -A
332
- git commit -m "Release v1.x.y"
333
- git tag v1.x.y
491
+ git commit -m "release: vX.Y.Z"
492
+ git tag vX.Y.Z
334
493
  git push && git push --tags
335
494
  ```
336
495