opencode-redactor is an OpenCode plugin designed to sanitize user text by masking secrets (API keys, PII) before they are sent to LLM providers. It uses a combination of Regex patterns and Shannon Entropy analysis.
- Runtime: Bun (TypeScript)
- Framework: OpenCode plugin hooks (via default export).
- Dependency Policy: Zero external npm dependencies at runtime. Uses native JS/TS features and Bun built-ins only.
- Key Files:
opencode-redactor.tscontains both the plugin hooks and masking logic (single-file plugin). This file is intended to be copied/symlinked into a project's.opencode/plugins/directory.index.jsis the npm package root entrypoint and forwards todist/opencode-redactor.jsfor directory import compatibility.index.d.tsprovides package-level types for TypeScript consumers.package.jsonprovides npm publish metadata and build scripts.
experimental.chat.messages.transform: primary protection; redacts provider-bound message history immediately before the LLM request is constructed.- Important: this hook must mutate
messages[*].parts[*]in-place. OpenCode does not reliably use a replacedoutput.messagesarray.
- Important: this hook must mutate
chat.message: optional local redaction and auditing of freshly received user parts.- Local persistence masking is controlled by
OPENCODE_REDACTOR_MASK_LOCAL(default: fail-open; do not mutate stored parts).
- Local persistence masking is controlled by
tool.execute.after: masks secrets in tool outputs after execution (file reads, command output, etc.). Also masksoutput.metadatafor edit tools.tool.execute.before: restores<REDACTED:hex14>tokens to original values in write/edit tool args before execution. Throws a safety error if tokens cannot be resolved (prevents corrupted file writes).experimental.text.complete: masks secrets in assistant completion text before it is rendered or persisted.event: intercepts multiple event types:message.part.updated: masks completed tool-part output/metadata before it enters conversation history and TUI rendering.session.diff: masksbefore/afterstrings in file diffs shown in the diff viewer.tui.prompt.append,tui.command.execute: observed for early detection/auditing only (no mutation).
doc: https://github.com/anomalyco/opencode/blob/dev/packages/web/src/content/docs/plugins.mdx
Since this is a single-file plugin, there is no framework build step beyond bun build.
-
Run Tests:
bun test -
Build (for npm publish):
bun run build
- Imports: avoid runtime imports; keep the plugin self-contained.
- Regex: Keep
DETECTORSconservative and prefer high-confidence patterns. - Formatting: Standard TypeScript formatting.
- Safety:
- Do not log the original secrets in debug output if possible.
- When modifying entropy logic, test against "normal" English text to ensure low false-positive rates.
Secrets are replaced with deterministic <REDACTED:hex14> tokens. The token ID is derived from a dual FNV-1a-like 32-bit hash of the original value, producing 14 hex characters (56 bits). The in-memory tokenRegistry maps both directions: value → token and token → value. Same secret always produces the same token within a session.
tool.execute.before calls unmaskArgs() to restore tokens in write/edit tool arguments before the file operation runs. If an unresolvable token is found, it throws to prevent corrupted output.
- Regex Pass: Scans for high-confidence patterns (AWS, Stripe, GitHub, etc.) and replaces matches with
<REDACTED:hex14>tokens. - KeywordDetector Pass: Matches
key = "value"/key: value/password is valuepatterns around known secret keywords; masks the value portion only. - JWT Pass: Validates JWT-like tokens by base64url-decoding and JSON-parsing the header and payload; masks only formally valid JWTs.
- Entropy Pass (quoted): Any quoted string ≥ 20 chars is tested against hex and base64 entropy thresholds.
- Entropy Pass (context-aware): Unquoted tokens within 60 characters of a secret keyword are tested against entropy thresholds.
The tool appends a single JSON line when masking changes output text (no secret values; metadata only).
- Default:
~/.config/opencode/opencode-redactor.audit.jsonl - Disable:
OPENCODE_REDACTOR_AUDIT_ENABLED=0 - Override:
OPENCODE_REDACTOR_AUDIT_DIR/OPENCODE_REDACTOR_AUDIT_PATH
Audit records include:
hook,durationMs,bytesIn,bytesOutparts,partsChangedforchat.messageandexperimental.chat.messages.transformfields,fieldsChangedfor TUI event scanstoolNamefortool.execute.after/tool.execute.beforewhen availabletokensFound,tokensRestoredfortool.execute.before
Always test test1.sh and test2.sh and analyze it's output