Pi extension that redacts sensitive strings from user input, tool results, and context before the LLM sees them.
pi install npm:pi-redactor
Or from a local path:
pi install /path/to/pi-redactor
The extension intercepts messages at three points:
input — rewrites your message before it reaches the LLM.tool_result — redacts tool output (file reads, command output, etc.) before it enters the conversation.context — scans the full message history immediately before each LLM call, catching secrets in historical context, assistant echoes, or custom messages.All configured patterns are applied using a single-pass regex with longest-match-first semantics. The transformed text is sent instead of the original.
The LLM never sees the original sensitive strings.
If redaction fails at runtime (e.g., a malformed pattern), the extension fails closed: user messages are blocked, tool results are replaced with an error, and context is emptied. An error notification is displayed in each case. Disable the redactor with /redact off to bypass.
| Command | Description |
|---|---|
/redact |
Show help and usage |
/redact add <string> |
Add pattern; replaces with [REDACTED] |
/redact add <string> as <label> |
Add pattern with custom replacement label |
/redact remove <string> |
Remove pattern by its original string |
/redact list |
Show all active patterns |
/redact clear |
Remove all patterns (prompts for confirmation) |
/redact on |
Enable redaction |
/redact off |
Disable redaction |
/redact limit <n> |
Set max pattern count (default 100, max 1000) |
The delimiter ` as ` splits original from label. The last occurrence of ` as ` is used, so:
/redact add John Smith as CEO as [PERSON]
Redacts John Smith as CEO → [PERSON].
/redact add John Smith as [CLIENT]
/redact add acct_12345
Input: "Contact John Smith about acct_12345."
LLM receives: "Contact [CLIENT] about [REDACTED]."
Notification: 🔒 Redacted 2 occurrence(s) from your message
john smith matches John Smith.secret and secretkey are patterns, the input secretkey matches the longer pattern. Patterns do not apply sequentially.handled, tool results are replaced with an error, and context is emptied./redact list to review, then /redact on to re-enable./redact limit <n>. Adding a pattern beyond the current limit produces an error.| State | Display |
|---|---|
| Enabled, N patterns (N > 0) | 🔒 Redactor: N pattern(s) |
| Enabled, 0 patterns | 🔒 Redactor: no patterns |
| Disabled | 🔓 Redactor: off |
Patterns persist to a platform-specific state directory that is outside of typical version-controlled dotfile paths:
| Platform | Path |
|---|---|
| Linux | ~/.local/state/pi-redactor/config.json |
| macOS | ~/.local/state/pi-redactor/config.json |
| Windows | %LOCALAPPDATA%\pi-redactor\config.json |
The XDG_STATE_HOME environment variable is respected on Linux/macOS. On Windows, LOCALAPPDATA is used.
The file uses an envelope format with integrity checking:
{
"version": 1,
"configVersion": 3,
"checksum": "<sha256-hex-of-serialized-data>",
"data": {
"enabled": true,
"maxPatterns": 100,
"patterns": [
{
"original": "John Smith",
"replacement": "[CLIENT]",
"createdAt": 1710000000000
}
]
}
}
| Field | Description |
|---|---|
version |
Envelope schema version. Currently 1. |
configVersion |
Monotonically incrementing counter for concurrency control. |
checksum |
SHA-256 hex digest of JSON.stringify(data). Used for tamper detection. |
data.enabled |
Whether redaction is active. |
data.maxPatterns |
Maximum number of patterns allowed. Default 100, max 1000. |
data.patterns[].original |
The original string to redact. |
data.patterns[].replacement |
The replacement label. |
data.patterns[].createdAt |
Unix timestamp in milliseconds when the pattern was added. |
configVersion. If another pi session modified the file since this session last read it, the save fails with an error instructing the user to reload./redact list displays the raw original strings in the local UI. These are the sensitive values being redacted. Do not use this command where the terminal output may be captured or shared.toLowerCase() without Unicode normalization. Visually identical strings in different normal forms (NFC vs NFD) are treated as distinct patterns (e.g., é as U+00E9 vs U+0065 U+0301).context handler scans unsigned thinking blocks, but blocks with a thinkingSignature are passed through unchanged. Modifying the thinking text would invalidate the cryptographic signature used by LLM providers for multi-turn thought chain continuity. Secrets that appeared in signed thinking blocks (from turns before a redaction pattern was configured) will persist in the conversation context.redacted: true are encrypted payloads with no meaningful text to scan.context handler does not scan type: "toolCall" block arguments. The corresponding ToolResultMessage content is redacted.This package was developed predominantly using an AI coding agent. All code, tests, and documentation were reviewed, validated, and approved by me.
MIT