Tools
Shroud
Privacy obfuscation plugin for OpenClaw — detects PII and replaces with deterministic fake values before anything reaches the LLM
Install
npm install &&
Configuration Example
"shroud-privacy": {
"enabled": true,
"config": {
// Recommended: safe defaults for community use
"auditEnabled": true // audit log on — see what Shroud is doing
// "auditIncludeProofHashes": false // off by default (opt-in)
// "auditMaxFakesSample": 0 // off by default (opt-in)
// "auditLogFormat": "human" // human-readable single lines
// "minConfidence": 0.0 // catch everything (default)
// "secretKey": "" // auto-generated if empty
// "persistentSalt": "" // set for cross-session consistency
// "canaryEnabled": false // data leakage tracking (opt-in)
}
}
README
# Shroud — Community Edition
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.
> **Open-source Community Edition** — free to use under Apache 2.0 license. [Enterprise Edition](#enterprise-edition) available with additional features for teams.
## What it does
1. **Detects** 100+ entity types: emails, IPs, phones, API keys, hostnames, SNMP communities, BGP ASNs, credit cards, SSNs, file paths, URLs, person/org/location names, VLANs, route-maps, ACLs, OSPF IDs, IBANs, JWTs, PEM certs, GPS coordinates, ICS/SCADA identifiers, Palo Alto/Check Point/Juniper/Fortinet/F5 config secrets, and custom regex patterns.
2. **Replaces** each value with a deterministic fake (same input + key = same fake every time). Fakes are format-preserving: IPv4 stays in CGNAT range (`100.64.0.0/10`), IPv6 uses ULA range (`fd00::/8`), emails keep `@domain` structure, credit cards pass Luhn, etc.
3. **Deobfuscates** LLM responses and tool parameters so the user sees real values and tools receive real arguments.
4. **Audit logs** every obfuscation/deobfuscation event with counts, categories, char deltas, and optional proof hashes — never logging raw sensitive values.
### Hook lifecycle
| Hook | Direction | What happens |
|------|-----------|-------------|
| `before_prompt_build` | User → LLM | Obfuscate user prompt, prepend privacy context |
| `before_message_write` | Any → History | Obfuscate non-assistant messages; deobfuscate assistant messages |
| `before_tool_call` | LLM → Tool | Deobfuscate tool parameters + track tool chain depth |
| `tool_result_persist` | Tool → History | Obfuscate tool results before storing |
| `message_sending` | Agent → User | Deobfuscate outbound messages (Telegram only) |
> **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.
## Install
### OpenClaw
```bash
openclaw plugins install shroud-privacy
```
That's it. Configure in `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`.
### From source (development)
```bash
git clone https://github.com/walterkeating-stack/shroud.git
cd shroud
npm install && npm run build
bash deploy-local.sh # → OpenClaw (~/.openclaw/extensions/)
```
## Updating
OpenClaw doesn't have a `plugins update` command yet, so updating requires removing the old install first. A helper script is included:
```bash
# Update to latest version (preserves your config)
bash scripts/update-openclaw-plugin.sh
# Update to a specific version
bash scripts/update-openclaw-plugin.sh <version>
```
The script saves your plugin config from `openclaw.json`, removes the old extension, reinstalls from npm, restores your config, and restarts the gateway.
### Manual update
If you prefer to do it manually:
```bash
# 1. Remove old plugin files
rm -rf ~/.openclaw/extensions/shroud-privacy
# 2. Reinstall (this resets your plugin config to defaults)
openclaw plugins install shroud-privacy
# 3. Re-apply your config in ~/.openclaw/openclaw.json
# (under plugins.entries."shroud-privacy".config)
# 4. Restart
openclaw gateway restart
```
## Configure
Edit `~/.openclaw/openclaw.json` under `plugins.entries."shroud-privacy".config`:
```jsonc
"shroud-privacy": {
"enabled": true,
"config": {
// Recommended: safe defaults for community use
"auditEnabled": true // audit log on — see what Shroud is doing
// "auditIncludeProofHashes": false // off by default (opt-in)
// "auditMaxFakesSample": 0 // off by default (opt-in)
// "auditLogFormat": "human" // human-readable single lines
// "minConfidence": 0.0 // catch everything (default)
// "secretKey": "" // auto-generated if empty
// "persistentSalt": "" // set for cross-session consistency
// "canaryEnabled": false // data leakage tracking (opt-in)
}
}
```
Restart the gateway after config changes:
```bash
openclaw gateway restart
```
### Safe defaults
Out of the box, Shroud:
- Auto-generates a secret key (per-session unless you set `secretKey`)
- Detects all entity categories at confidence >= 0.0
- Logs audit lines (counts + categories) but **not** proof hashes or fake samples
- Never logs raw values, real→fake mappings, or original text
To enable proof hashes and fake samples for deeper audit:
```jsonc
"config": {
"auditEnabled": true,
"auditIncludeProofHashes": true,
"auditHashTruncate": 12,
"auditMaxFakesSample": 3
}
```
## Config reference
### Core settings
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `secretKey` | string | auto | HMAC secret for deterministic mapping |
| `persistentSalt` | string | `""` | Fixed salt for cross-session consistency |
| `minConfidence` | number | `0.0` | Minimum detector confidence (0.0–1.0) |
| `allowlist` | string[] | `[]` | Values to never obfuscate |
| `denylist` | string[] | `[]` | Values to always obfuscate |
| `canaryEnabled` | boolean | `false` | Inject tracking tokens for leak detection |
| `canaryPrefix` | string | `"SHROUD-CANARY"` | Prefix for canary tokens |
| `auditEnabled` | boolean | `false` | Enable audit logging |
| `verboseLogging` | boolean | `false` | Alias for `auditEnabled` |
| `auditLogFormat` | `"human"` \| `"json"` | `"human"` | Audit output format |
| `auditIncludeProofHashes` | boolean | `false` | Include salted SHA-256 proof hashes |
| `auditHashSalt` | string | `""` | Salt for proof hashes |
| `auditHashTruncate` | number | `12` | Truncate proof hashes to N hex chars |
| `auditMaxFakesSample` | number | `0` | Include up to N fake values in audit (0 = off) |
| `logMappings` | boolean | `false` | Log mapping table (debug only) |
| `customPatterns` | array | `[]` | User-defined regex detection patterns |
| `detectorOverrides` | object | `{}` | Override built-in rules: disable or change confidence per rule name |
| `maxToolDepth` | number | `10` | Max nested tool call depth before warning |
| `redactionLevel` | `"full"` \| `"masked"` \| `"stats"` | `"full"` | Output mode: fake values, partial masking, or category placeholders |
| `dryRun` | boolean | `false` | Detect entities but don't replace (testing mode) |
| `maxStoreMappings` | number | `0` | Max mapping store size with LRU eviction (0 = unlimited) |
> **Env var overrides:** `SHROUD_SECRET_KEY` and `SHROUD_PERSISTENT_SALT` override their respective config keys (priority: env var > plugin config > default).
### Detector overrides
Disable or tune individual detection rules by name. Rule names match the built-in pattern names (e.g. `email`, `ipv4`, `phone_intl`, `cisco_enable_secret`). See `src/detectors/regex.ts` for the full list.
```jsonc
"detectorOverrides": {
"phone_intl": { "enabled": false }, // disable international phone detection
"file_path_unix": { "confidence": 0.5 }, // lower confidence (filtered by minConfidence)
"snmp_community": { "confidence": 1.0 } // boost to always match
}
```
Rules not listed keep their defaults. Overrides apply to both direct regex detection and code-aware detection.
### Conversational tools
Shroud registers tools that the LLM can call during conversations:
| Tool | What it does |
|------|-------------|
| `shroud-stats` | Show all detection rules with status, confidence, hit counts, store size, and config summary |
| `shroud_status` | Quick stats: entity counts, session info, audit status (JSON) |
| `shroud_reset` | Clear all mappings and start a fresh privacy session |
You can also run the stats CLI from the terminal:
```bash
node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-stats.mjs # live rule table
node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-stats.mjs --json # JSON output
node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-stats.mjs --test "Contact [email protected]"
```
Tip: create an alias for convenience:
```bash
alias shroud-stats="node ~/.openclaw/extensions/shroud-privacy/scripts/shroud-stats.mjs"
```
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.
### Auto-patching on first install
On first load, Shroud automatically patches pi-ai's `EventStream.push()` to enable streaming deobfuscation across all LLM providers and delivery channels. The patch:
1. Backs up the original file (`.shroud-backup`)
2. Injects a 4-line hook that calls `globalThis.__shroudStreamDeobfuscate`
3. Clears the Node.js V8 compile cache
4. Triggers a gateway restart via SIGUSR1
On subsequent loads, the patch is detected and skipped. To revert: restore the `.shroud-backup` file and restart.
### Rule hit counters
Shroud tracks per-rule match counts for the lifetime of the process. Counters appear in three places:
- **`shroud-stats` CLI** — see [Conversational tools](#conversational-tools) above for usage. Shows all rules with status, confidence, and hit counts from the running gateway.
- **Audit log lines** — `byRule=regex:email:3,regex:ipv4:2,...` alongside the existing `byCat` field.
- **`getStats()`** — the `ruleHits` object in the stats response, useful for programmatic access.
Counters reset on `reset()` or gateway restart.
## Redaction levels
Three output modes for different audiences:
- **`full`** (default): Replace with realistic fake values. Best for LLM interaction.
- **`masked`**: Partial masking (`j***@***.com`, `***-**-1234`). Best for human review.
- **`stats`**: Category placeholders (`[EMAIL-1]`, `[HOSTNAME-3]`). Best for dashboards.
```jsonc
"redactionLevel": "masked"
... (truncated)
tools
Comments
Sign in to leave a comment