npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-redactor

v0.1.1

Published

OpenCode redaction plugin to mask secrets and high-entropy strings before sending to LLM providers

Readme

OpenCode Redactor

A lightweight, native TypeScript plugin for OpenCode. It masks sensitive information (API keys, PII, credentials) and high-entropy strings before text is sent to an LLM provider.

Features

  • Zero Dependencies: Built using native TypeScript and Bun, ensuring fast startup and minimal footprint.
  • Hybrid Detection Engine:
    • Regex Matching: Instantly identifies known patterns for AWS, GitHub, GitLab, Slack, Stripe, private keys, and emails.
    • Entropy Analysis: Uses Shannon entropy calculation to detect potential secrets that don't match specific regex patterns (e.g., random hex strings, passwords).
  • Smart Masking: Replaces secrets with descriptive placeholders like <SECRET: AWSKeyDetector> or <SECRET: Base64HighEntropyString>, preserving context while hiding the original value.
  • Pre-Send Hook Coverage: Masks both user chat text and tool output text via OpenCode hooks.
  • Metadata-Only Audit Logs: Writes JSONL records without storing raw secret values.

Installation

This plugin is designed for the OpenCode environment.

1. Local Project Installation

Copy the plugin into your project's .opencode/plugins/ directory:

mkdir -p .opencode/plugins
cp opencode-redactor.ts .opencode/plugins/opencode-redactor.ts

Tip: during development, symlink instead of copying to avoid drift:

mkdir -p .opencode/plugins
ln -sf "$PWD/opencode-redactor.ts" .opencode/plugins/opencode-redactor.ts

2. Global Installation

To make the plugin available across all your OpenCode projects:

mkdir -p ~/.config/opencode/plugins
cp opencode-redactor.ts ~/.config/opencode/plugins/opencode-redactor.ts

3. Install via npm (recommended)

If you publish/install this plugin from npm, add it to your OpenCode config:

opencode.json

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["[email protected]"]
}

OpenCode will install/load the package and run the plugin's default export.

Note: OpenCode currently imports npm plugins via a directory path. This package includes a root index.js entrypoint for compatibility.

Usage

Once installed, OpenCode loads the plugin automatically at startup.

This avoids “opportunistic tool calling” (where the model decides whether to call a tool after seeing your raw text).

Auto-Mask Before LLM (Plugin)

This repo is an OpenCode plugin. The source file is:

  • opencode-redactor.ts (copy/symlink into .opencode/plugins/opencode-redactor.ts)

If you paste a secret into chat, it should be replaced with <SECRET: ...> placeholders before any upstream request is made.

To also protect secrets introduced by tools (file reads, command output, etc.), the plugin hooks:

  • tool.execute.after

This masks tool outputs at the source so they are never printed/stored/sent unmasked in subsequent model turns.

Testing (Local)

1) Unit tests (masking behavior)

Run the bun tests in this repo:

bun test

2) Manual end-to-end test (OpenCode plugin)

  1. Ensure the plugin files are in either:

    • Project: .opencode/plugins/
    • Global: ~/.config/opencode/plugins/
  2. Restart OpenCode so it reloads plugins.

  3. Test it out by sending a message with a fake secret:

export OPENCODE_REDACTOR_AUDIT_ENABLED=1
export OPENCODE_REDACTOR_AUDIT_PATH="$PWD/opencode-redactor.audit.jsonl"
rm -f "$OPENCODE_REDACTOR_AUDIT_PATH"

# In another terminal, watch the audit log:
# tail -f "$OPENCODE_REDACTOR_AUDIT_PATH"

echo "AKIAIOSFODNN7EXAMPLE" > secret-test.txt
opencode run "Read secret-test.txt and tell me what it contains"

# Then verify audit output:
cat "$OPENCODE_REDACTOR_AUDIT_PATH"

Configuration

You can tune entropy sensitivity by editing the constants near the top of opencode-redactor.ts:

const BASE64_ENTROPY_LIMIT = 4.5;
const HEX_ENTROPY_LIMIT = 3.0;
const MIN_LENGTH_FOR_ENTROPY = 16;

Local Audit Log

By default, when masking changes output text, the plugin appends a single JSON line (JSONL) to a local audit file so you can track usage and which detectors fired.

  • Default path: ~/.config/opencode/opencode-redactor.audit.jsonl
  • Disable logging: set OPENCODE_REDACTOR_AUDIT_ENABLED=0
  • Override paths: OPENCODE_REDACTOR_AUDIT_DIR or OPENCODE_REDACTOR_AUDIT_PATH

Each audit line includes additional performance metadata:

  • hook: chat.message or tool.execute.after
  • durationMs: time spent masking for that hook invocation
  • bytesIn / bytesOut: UTF-8 byte size before/after masking
  • parts / partsChanged: part-level metrics for chat.message
  • toolName: tool name for tool.execute.after when available

Supported Patterns

This plugin is deliberately conservative: it prefers high-confidence regex matches and only applies entropy masking in limited contexts.

Regex Detectors

When a match is found, the value is replaced with a placeholder like <SECRET: AWSKeyDetector>.

| Placeholder Label | What It Detects | | -------------------------- | -------------------------------------------------------------------------------------- | | PrivateKeyDetector | PEM/OpenSSH/PGP private key blocks + common private key headers | | ArtifactoryDetector | JFrog Artifactory access tokens (AKC… / AP…) | | AWSKeyDetector | AWS access key IDs (AKIA/ASIA/…) and AWS secret access keys in assignment-like context | | AzureStorageKeyDetector | Azure Storage AccountKey= values | | CloudantDetector | Cloudant credentials in URLs and common assignment patterns | | BasicAuthDetector | Basic auth in URLs (password only) and Authorization: Basic ... headers | | DiscordBotTokenDetector | Discord bot tokens | | GitHubTokenDetector | GitHub personal access tokens (ghp_, gho_, ghu_, ghs_, ghr_) | | GitLabTokenDetector | GitLab tokens (glpat-, gldt-, glft-, glsoat-, glrt-) | | IbmCloudIamDetector | IBM Cloud IAM API keys in assignment-like context | | IbmCosHmacDetector | IBM COS HMAC secret access keys in assignment-like context | | MailchimpDetector | Mailchimp API keys (<32 hex>-us<region>) | | NpmDetector | npm auth tokens in .npmrc-style _authToken= lines | | OpenAIDetector | OpenAI API keys (both current structured and legacy sk- formats) | | PypiTokenDetector | PyPI tokens (pypi-AgEI... / pypi-AgEN...) | | SendGridDetector | SendGrid API keys (SG.<...>.<...>) | | SlackDetector | Slack tokens (xox...) and Slack webhook URLs | | SoftlayerDetector | SoftLayer API keys in assignments and in SOAP API URLs | | SquareOAuthDetector | Square OAuth secrets (sq0csp-...) | | StripeDetector | Stripe secret keys (sk_live_..., sk_test_...) | | TelegramBotTokenDetector | Telegram bot tokens (<digits>:<35 chars>) | | TwilioKeyDetector | Twilio Account SIDs (AC...) and API keys (SK...) | | MailDetector | Email addresses (PII) |

Content-Aware Detectors

These are not simple regex replacements across all text; they apply only in constrained contexts.

| Placeholder Label | What It Detects | Notes | | ------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | KeywordDetector | Values adjacent to common secret keywords (password, token, api_key, etc.) | Masks the value portion of key = value / key: "value" / password is value patterns | | JwtTokenDetector | JWT-like tokens | Only masked if base64url decoding succeeds and header/payload parse as JSON | | HexHighEntropyString | High-entropy hex strings | Only masked when quoted, or when keyword context appears nearby | | Base64HighEntropyString | High-entropy base64/base64url-ish strings | Only masked when quoted, or when keyword context appears nearby |