← Back to Plugins
Tools

Redacter

SBTopZZZ-LG By SBTopZZZ-LG 👁 96 views ▲ 0 votes

Defense-in-depth secret redaction OpenClaw plugin. Intercepts and irreversibly redacts secrets before any content reaches the LLM and sanitizes OpenClaw-owned transcripts/tool-result persistence.

Homepage GitHub

Install

npm install
npm

Configuration Example

{
  "extensions": ["/absolute/path/to/openclaw-redacter/dist/index.js"]
}

README

# Redacter — OpenClaw Secret Redaction Plugin

<!-- Banner -->
<picture>
  <img src="./docs/images/banner.webp" alt="openclaw-redacter banner" />
</picture>

<!-- Badges -->
<p>
  <a href="https://www.npmjs.com/package/openclaw-redacter">
    <img src="https://img.shields.io/npm/v/openclaw-redacter/latest" alt="npm version" />
  </a>
  <a href="https://github.com/SBTopZZZ-LG/openclaw-redacter/blob/main/LICENSE">
    <img src="https://img.shields.io/github/license/SBTopZZZ-LG/openclaw-redacter" alt="License" />
  </a>
  <a href="https://github.com/SBTopZZZ-LG/openclaw-redacter/actions">
    <img src="https://github.com/SBTopZZZ-LG/openclaw-redacter/actions/workflows/cd.yaml/badge.svg" alt="Release workflow" />
  </a>
</p>

Defense-in-depth secret and sensitive-content redaction for OpenClaw. Intercepts
and irreversibly redacts secrets before any content reaches the LLM, and
sanitizes OpenClaw-owned transcript and tool-result persistence.

The plugin is written in TypeScript, depends on no runtime libraries, and is
intentionally narrow: v1 supports **irreversible** redaction only. No
deterministic placeholders or reversible tokenization.

## What it does

- Scans every model-bound payload (system prompt, user prompt, history
  messages, tool params, tool results) for known secret formats and
  structured key/value pairs.
- Replaces detected secrets with irreversible markers such as
  `[REDACTED:API_KEY]`, `[REDACTED:JWT]`, `[REDACTED:AWS_ACCESS_KEY_ID]`, etc.
- Treats `before_agent_run` as the **primary final barrier**. When configured
  in `strict` mode, a detected secret in that hook blocks the agent run
  with a sanitized, classified reason that OpenClaw's `InputGateDecision`
  contract handles.
- Sanitizes OpenClaw-owned transcript writes via `tool_result_persist` and
  `before_message_write` (synchronous rewrite of the persisted message).
- Wires additional defense-in-depth hooks: `before_prompt_build`,
  `llm_input`, `before_tool_call`, `after_tool_call`, `message_received`,
  `message_sending`, and `message_sent`.
- Logs only **sanitized summaries** of detections. The audit pipeline is
  structurally incapable of emitting a raw secret value.

## Threat model

Defend against:

- API keys, OAuth tokens, JWTs, session tokens, basic/bearer credentials.
- Cloud credentials: AWS access keys, AWS secret access keys (length-40
  base64-ish), GCP API keys, Stripe keys, etc.
- VCS tokens: GitHub classic + fine-grained PATs, GitLab PATs, Slack tokens.
- Private key blocks (PEM) and SSH public-key strings.
- `.env` style secrets, JSON/YAML key-value pairs with secret-shaped keys,
  connection strings / DSNs, webhook URLs with `?secret=` / `?token=`
  query params.

Out of scope for v1:

- Reversible tokenization / vault-based re-injection.
- Provider-side encryption of transcripts.
- Anti-prompt-injection for instructions (this plugin's scope is secret
  redaction only).

## Install

### From npm

```bash
openclaw plugins install npm:openclaw-redacter
```

To install a specific version:

```bash
openclaw plugins install npm:[email protected]
```

### As a local extension

```bash
git clone <repo>
cd openclaw-redacter
npm install
npm run build
```

Then point OpenClaw at the built entry:

```json
{
  "extensions": ["/absolute/path/to/openclaw-redacter/dist/index.js"]
}
```

### As a workspace / npm package

Add `@openclaw/redacter` (or your chosen name) to your OpenClaw workspace's
`package.json` and list the package in the OpenClaw extension manifest.

## Recommended starter config

Add this to `~/.openclaw/openclaw.json`:

```jsonc
{
  "plugins": {
    "redacter": {
      "enabled": true,
      "mode": "permissive",

      // Exact string match — the literal value is redacted wherever it appears.
      "exactSecrets": [
        "MY_SECRET_TOKEN",
        // Environment variable — resolved from process.env at load time.
        "${MY_API_KEY}",
        // Prefix + secret + suffix pattern.
        "example_SECRET_TOKEN_12345",
      ],

      // SHA-256 hash — matches the pre-hashed value, avoids storing raw secrets in config.
      "exactSecretHashes": [
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      ],

      // Built-in detectors (AWS keys, GitHub tokens, JWTs, PEM keys, etc.)
      "builtInDetectors": {
        "masterEnable": true,
      },

      // All hooks enabled.
      "phases": {
        "promptBuild": true,
        "beforeAgentRun": true,
        "messageFlow": true,
        "toolCalls": true,
        "persistence": true,
      },

      // In permissive mode the plugin never blocks — only redacts and warns.
      // Switch to "strict" when you're ready to enforce.
      "blockOnDetection": {
        "criticalPhasesOnly": true,
        "allDetections": false,
        "detectorClasses": [],
      },

      "audit": {
        "enabled": true,
        "includeCountsOnly": false,
        "neverLogRawMatches": true,
      },
    },
  },
}
```

Once the config is saved, validate it and restart the gateway:

```bash
openclaw config validate
openclaw gateway restart
```

## Configuration

The plugin reads its config from the OpenClaw plugin config object. The
schema is exported at `dist/config.schema.json`. Defaults favor safety.

```jsonc
{
  "plugins": {
    "redacter": {
      "enabled": true,
      "mode": "strict", // "strict" or "permissive"
      "exactSecrets": ["MY_INTERNAL_TOKEN"],
      // OR reference an environment variable (resolved at load time):
      // "exactSecrets": ["${MY_INTERNAL_TOKEN}"],
      // OR store hashes to avoid putting raw secrets in config:
      "exactSecretHashes": ["<sha256 hex>"],
      "regexRules": [
        {
          "id": "internal_token",
          "pattern": "INT_[A-Z0-9]{12}",
          "label": "[REDACTED:INTERNAL]",
        },
      ],
      "phases": {
        "promptBuild": true,
        "beforeAgentRun": true,
        "messageFlow": true,
        "toolCalls": true,
        "persistence": true,
      },
      "blockOnDetection": {
        "criticalPhasesOnly": true, // only block in promptBuild / beforeAgentRun
        "allDetections": false,
        "detectorClasses": [], // empty = all
      },
      "audit": {
        "enabled": true,
        "includeCountsOnly": false,
        "neverLogRawMatches": true,
      },
    },
  },
}
```

See `src/config.ts` for the full schema.

## Detector management

### Custom regex rules

```jsonc
{
  "plugins": {
    "redacter": {
      "customRegexRules": [
        {
          "id": "internal_token",
          "pattern": "INT_[A-Z0-9]{12}",
          "label": "[REDACTED:INTERNAL_TOKEN]",
          "flags": "g",
        },
      ],
    },
  },
}
```

The legacy `regexRules` field is still accepted and is merged with
`customRegexRules` by id (the new field takes precedence on conflict).

### Built-in detector management

```jsonc
{
  "plugins": {
    "redacter": {
      "builtInDetectors": {
        "masterEnable": true,
        "overrides": {
          "anthropicApiKey": {
            "enabled": true,
            "labelOverride": "[REDACTED:ANT_KEY]",
          },
          "awsAccessKey": {
            "enabled": false,
          },
          "jwt": {
            "patternOverride": "\\beyJ[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+\\b",
          },
        },
      },
    },
  },
}
```

When `masterEnable` is `false`, all built-in detectors are disabled
unless explicitly re-enabled via a per-detector `enabled: true`. Pattern
overrides are validated at engine construction time; invalid patterns
fall back to the default and surface a warning. Labels follow a layered
resolution: `labelOverride` → `redactLabelsByDetector[id]` → built-in
default.

## Strict vs permissive mode

| Mode         | Detections | Blocking                                                                                                 |
| ------------ | ---------- | -------------------------------------------------------------------------------------------------------- |
| `strict`     | Replace    | Block when policy says so (default: only in `promptBuild` / `beforeAgentRun`, or per `detectorClasses`). |
| `permissive` | Replace    | Never block; emit warnings, continue.                                                                    |

In strict mode, blocking behavior is governed by `blockOnDetection`:

- `criticalPhasesOnly: true` (default) — block only in `promptBuild` /
  `beforeAgentRun`. Other phases sanitize and warn.
- `allDetections: true` — block on any detection in any phase.
- `detectorClasses: ["exactSecret", "privateKeyPem"]` — block only on
  the listed detector classes. If empty, all classes are eligible.

## Hook coverage

| Hook                   | Purpose                                                                                                                     | Mutation?                 | Phase            |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------------- |
| `before_prompt_build`  | Early sanitization of prompt-build inputs.                                                                                  | observation only in v1    | `promptBuild`    |
| `before_agent_run`     | **Primary final barrier.** Sanitizes prompt/system/messages; returns `InputGateDecision.block` in strict mode on detection. | mutation (block)          | `beforeAgentRun` |
| `llm_input`            | Post-barrier diagnostic. Emits audit event if a secret still appears in the final LLM payload.                              | observation               | `beforeAgentRun` |
| `before_tool_call`     | Sanitizes tool parameters. Optionally blocks in strict mode.                                                                | mutation (params / block) | `toolCalls`      |
| `after_tool_call`      | Observes tool result content; cannot mutate at this hoo

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...