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 +182 -14
- package/app-server.mjs +534 -0
- package/clients/python/shroud_client.py +369 -0
- package/dist/detectors/code.d.ts +1 -0
- package/dist/detectors/code.js +33 -12
- package/dist/detectors/patterns.js +35 -12
- package/dist/detectors/regex.js +257 -26
- package/dist/generators/network.d.ts +83 -14
- package/dist/generators/network.js +772 -45
- package/dist/hooks.js +344 -41
- package/dist/index.js +93 -78
- package/dist/obfuscator.d.ts +4 -0
- package/dist/obfuscator.js +51 -6
- package/dist/redaction.js +3 -2
- package/dist/store.js +11 -0
- package/openclaw.plugin.json +20 -2
- package/package.json +15 -7
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.png" alt="Shroud" width="160" height="160">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
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 (
|
|
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
|
-
> **
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
```
|