shroud-privacy 2.0.19 → 2.1.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,6 +1,12 @@
1
- # Shroud — Community Edition
1
+ <p align="center">
2
+ <img src="logo.png" alt="Shroud" width="160" height="160">
3
+ </p>
2
4
 
3
- Privacy obfuscation plugin for [OpenClaw](https://openclaw.ai). 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.
5
+ <h1 align="center">Shroud Community Edition</h1>
6
+
7
+ <p align="center">
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
+ </p>
4
10
 
5
11
  > **Open-source Community Edition** — free to use under Apache 2.0 license. [Enterprise Edition](#enterprise-edition) available with additional features for teams.
6
12
 
@@ -19,19 +25,77 @@ Privacy obfuscation plugin for [OpenClaw](https://openclaw.ai). Detects sensitiv
19
25
  | `before_message_write` | Any → History | Obfuscate non-assistant messages; deobfuscate assistant messages |
20
26
  | `before_tool_call` | LLM → Tool | Deobfuscate tool parameters + track tool chain depth |
21
27
  | `tool_result_persist` | Tool → History | Obfuscate tool results before storing |
22
- | `message_sending` | Agent → User | Deobfuscate outbound messages (Telegram only) |
28
+ | `message_sending` | Agent → User | Deobfuscate outbound messages (all channels) |
29
+
30
+ > **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 inbound side, streaming responses are deobfuscated in real-time via `EventStream.prototype.push()` — no OpenClaw file modifications needed.
23
31
 
24
- > **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.
32
+ > **Requires OpenClaw 2026.3.24 or later.** Older versions do not call `message_sending` for Slack/WhatsApp channels, causing duplicate messages with fake tokens. Shroud 2.1+ is tested exclusively against OpenClaw 2026.3.24.
25
33
 
26
34
  ## Install
27
35
 
28
- ### OpenClaw
36
+ ### OpenClaw (2026.3.24+)
29
37
 
30
38
  ```bash
39
+ # Ensure you're on OpenClaw 2026.3.24 or later
40
+ openclaw --version
41
+
42
+ # Install Shroud
31
43
  openclaw plugins install shroud-privacy
32
44
  ```
33
45
 
34
- That's it. Configure in `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`.
46
+ Configure in `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`. No OpenClaw file modifications needed — Shroud uses runtime prototype patches only.
47
+
48
+ ### Any agent (via APP)
49
+
50
+ 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.
51
+
52
+ ```bash
53
+ npm install shroud-privacy
54
+ ```
55
+
56
+ **Python:**
57
+
58
+ ```python
59
+ from shroud_client import ShroudClient
60
+
61
+ with ShroudClient() as shroud:
62
+ # Before sending to LLM
63
+ result = shroud.obfuscate("Contact admin@acme.com about 10.1.0.1")
64
+ send_to_llm(result.text) # "Contact user@example.net about 100.64.0.12"
65
+
66
+ # After receiving from LLM
67
+ restored = shroud.deobfuscate(llm_response)
68
+ show_to_user(restored.text) # original values restored
69
+ ```
70
+
71
+ Copy `clients/python/shroud_client.py` into your project, or import it directly from the npm install path. Requires Node.js on the PATH.
72
+
73
+ **Any language:**
74
+
75
+ Spawn the APP server and talk JSON-RPC over stdin/stdout:
76
+
77
+ ```bash
78
+ node node_modules/shroud-privacy/app-server.mjs node_modules/shroud-privacy/dist
79
+ ```
80
+
81
+ Handshake (server writes on startup):
82
+ ```json
83
+ {"app":"1.0","engine":"shroud","version":"2.1.0","capabilities":["obfuscate","deobfuscate","batch","stats","health","configure","audit","partitions"]}
84
+ ```
85
+
86
+ Obfuscate:
87
+ ```json
88
+ → {"id":1,"method":"obfuscate","params":{"text":"Contact admin@acme.com"}}
89
+ ← {"id":1,"result":{"text":"Contact user@example.net","entityCount":1,"categories":{"email":1},"modified":true}}
90
+ ```
91
+
92
+ Deobfuscate:
93
+ ```json
94
+ → {"id":2,"method":"deobfuscate","params":{"text":"Contact user@example.net"}}
95
+ ← {"id":2,"result":{"text":"Contact admin@acme.com","replacementCount":1,"modified":true}}
96
+ ```
97
+
98
+ Other methods: `reset`, `stats`, `health`, `configure`, `shutdown`.
35
99
 
36
100
  ### From source (development)
37
101
 
@@ -189,16 +253,17 @@ alias shroud-stats="node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-st
189
253
 
190
254
  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.
191
255
 
192
- ### Auto-patching on first install
256
+ ### How privacy works
257
+
258
+ Shroud uses runtime prototype patches — **no OpenClaw files are modified**:
259
+
260
+ **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.
193
261
 
194
- On first load, Shroud automatically patches pi-ai's `EventStream.push()` to enable streaming deobfuscation across all LLM providers and delivery channels. The patch:
262
+ **Inbound (LLM User):** Patches `EventStream.prototype.push()` to deobfuscate streaming responses in real-time. Fake values are replaced with real values as they stream.
195
263
 
196
- 1. Backs up the original file (`.shroud-backup`)
197
- 2. Injects a 4-line hook that calls `globalThis.__shroudStreamDeobfuscate`
198
- 3. Clears the Node.js V8 compile cache
199
- 4. Triggers a gateway restart via SIGUSR1
264
+ **Channel delivery:** On OpenClaw 2026.3.24+, the `message_sending` hook fires for ALL channels (Slack, WhatsApp, Telegram, etc.) and deobfuscates outbound messages. Older OpenClaw versions skip this hook for some channels, causing fake tokens in channel output.
200
265
 
201
- On subsequent loads, the patch is detected and skipped. To revert: restore the `.shroud-backup` file and restart.
266
+ All patches are applied once at plugin load and are idempotent subsequent loads detect and skip them.
202
267
 
203
268
  ### Rule hit counters
204
269
 
@@ -297,11 +362,114 @@ With proof hashes enabled:
297
362
 
298
363
  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.
299
364
 
365
+ ## Agent Privacy Protocol (APP)
366
+
367
+ APP is an open protocol for adding privacy obfuscation to any AI agent. Shroud is the reference implementation.
368
+
369
+ ### Overview
370
+
371
+ ```
372
+ ┌─────────────────┐ stdin/stdout ┌──────────────────┐
373
+ │ Your Agent │ ◄──── JSON-RPC ────► │ APP Server │
374
+ │ (any language) │ │ (app-server.mjs)│
375
+ └─────────────────┘ └──────────────────┘
376
+ │ │
377
+ │ 1. obfuscate(user_input) │ detects PII,
378
+ │ 2. send to LLM ──────────────► │ returns fakes
379
+ │ 3. deobfuscate(llm_response) │ restores reals
380
+ │ 4. show to user │
381
+ ```
382
+
383
+ ### Protocol specification
384
+
385
+ - **Transport**: Newline-delimited JSON-RPC 2.0 over stdin/stdout
386
+ - **Encoding**: UTF-8
387
+ - **Process model**: Agent spawns APP server as subprocess, one per agent instance
388
+
389
+ ### Handshake
390
+
391
+ On startup, the server writes a single JSON line to stdout:
392
+
393
+ ```json
394
+ {"app":"1.0","engine":"shroud","version":"2.1.0","capabilities":["obfuscate","deobfuscate","batch","stats","health","configure","audit","partitions"]}
395
+ ```
396
+
397
+ The agent must read this line before sending requests. Fields:
398
+ - `app` — protocol version (always `"1.0"`)
399
+ - `engine` — implementation name
400
+ - `version` — implementation version
401
+ - `capabilities` — supported methods
402
+
403
+ ### Methods
404
+
405
+ | Method | Params | Returns | Description |
406
+ |--------|--------|---------|-------------|
407
+ | `obfuscate` | `{text}` | `{text, entityCount, categories, modified, audit}` | Replace real values with fakes |
408
+ | `deobfuscate` | `{text}` | `{text, replacementCount, modified, audit}` | Restore fakes to real values |
409
+ | `reset` | `{}` | `{ok, summary}` | Clear all mappings |
410
+ | `stats` | `{}` | `{storeMappings, ruleHits, ...}` | Engine statistics |
411
+ | `health` | `{}` | `{uptime, requests, avgLatencyMs}` | Liveness check |
412
+ | `configure` | `{config}` | `{ok}` | Hot-reload configuration |
413
+ | `batch` | `{operations: [{direction, text}]}` | `{results: [...]}` | Batch obfuscate/deobfuscate |
414
+ | `shutdown` | `{}` | `{ok}` | Graceful shutdown (flushes stats) |
415
+
416
+ ### Request/response format
417
+
418
+ ```
419
+ → {"id":1,"method":"obfuscate","params":{"text":"Server 10.1.0.1 is down"}}
420
+ ← {"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"}}}
421
+ ```
422
+
423
+ Errors:
424
+ ```
425
+ ← {"id":1,"error":{"code":-32602,"message":"Missing required param: text"}}
426
+ ```
427
+
428
+ ### Heartbeat
429
+
430
+ The server writes JSON heartbeats to stderr every 30 seconds:
431
+ ```json
432
+ {"heartbeat":true,"pid":12345,"uptime":120,"requests":42,"avgLatencyMs":1.2,"storeSize":15,"memoryMB":28}
433
+ ```
434
+
435
+ ### Integration checklist
436
+
437
+ 1. `npm install shroud-privacy`
438
+ 2. Spawn: `node node_modules/shroud-privacy/app-server.mjs node_modules/shroud-privacy/dist`
439
+ 3. Read handshake line from stdout
440
+ 4. Before LLM: send `obfuscate`, use returned `text`
441
+ 5. After LLM: send `deobfuscate`, show returned `text` to user
442
+ 6. On agent shutdown: send `shutdown`
443
+
444
+ ### Python client
445
+
446
+ A ready-made Python client is included at `clients/python/shroud_client.py`:
447
+
448
+ ```python
449
+ from shroud_client import ShroudClient
450
+
451
+ client = ShroudClient()
452
+ client.start()
453
+
454
+ safe = client.obfuscate("Contact admin@acme.com about 10.1.0.1")
455
+ print(safe.text) # fakes
456
+ print(safe.entity_count) # 2
457
+ print(safe.categories) # {"email": 1, "ip_address": 1}
458
+
459
+ real = client.deobfuscate(llm_response)
460
+ print(real.text) # originals restored
461
+ print(real.residual_fakes) # any CGNAT/ULA IPs that survived
462
+
463
+ client.stop()
464
+ ```
465
+
466
+ Supports context manager, auto-restart on crash, residual fake detection, and hot-reload via `configure()`.
467
+
300
468
  ## Development
301
469
 
302
470
  ```bash
303
471
  npm install
304
- npm test # run vitest (210 tests)
472
+ npm test # run vitest (718 tests)
305
473
  npm run build # compile TypeScript
306
474
  npm run lint # type-check without emitting
307
475
  ```