# envsitter-guard OpenCode plugin that prevents agents/tools from reading or editing sensitive `.env*` files, while still allowing safe inspection via EnvSitter (keys + deterministic fingerprints; never values). ## Quickstart (OpenCode) OpenCode supports loading plugins from npm or local plugin files. Reference docs: - https://opencode.ai/docs/plugins/ - https://opencode.ai/docs/config/#plugins ### Option A (recommended): load from npm via `opencode.json` Add the plugin package to your OpenCode config. `opencode.json`: ```json { "$schema": "https://opencode.ai/config.json", "plugin": ["envsitter-guard@latest"] } ``` Restart OpenCode after updating config. ## Why Accidentally printing `.env` contents is one of the easiest ways for an agentic workflow to leak secrets into: - chat transcripts - logs/tool output - commits/patches - screenshots / shared sessions `envsitter-guard` blocks risky operations and points you to safe alternatives. ## What it does This plugin provides safe EnvSitter-backed tools and blocks sensitive file access via OpenCode tool hooks. ### Safe tools (no values) These tools never return raw `.env` values: - `envsitter_keys`: list keys in a dotenv file - `envsitter_fingerprint`: deterministic fingerprint of a single key’s value - `envsitter_match`: boolean/shape checks and outside-in candidate matching (without printing values) - `envsitter_match_by_key`: bulk candidate-by-key matching (returns booleans only) - `envsitter_scan`: scan value *shapes* (jwt/url/base64) without printing values ### Blocking behavior - Sensitive paths: `.env`, `.env.local`, `.env.production`, etc. (`.env*`) - Allowed: `.env.example` - Always blocked: `.envsitter/pepper` Blocked operations via tool hooks: - `read` on sensitive `.env*` paths - `edit` / `write` / `patch` / `multiedit` on sensitive `.env*` paths When blocked, the plugin shows a throttled warning toast and suggests using EnvSitter tools instead. ## Tools All tools accept a `filePath` that defaults to `.env`. Tools only operate on `.env`-style files inside the current project. ### `envsitter_keys` Lists keys in a dotenv file. - Input: `{ "filePath"?: string, "filterRegex"?: string }` - Output: JSON `{ file, keys }` Example (inside OpenCode): ```json { "tool": "envsitter_keys", "args": { "filePath": ".env" } } ``` Optional filtering: ```json { "tool": "envsitter_keys", "args": { "filePath": ".env", "filterRegex": "/^(API_|DB_)/" } } ``` ### `envsitter_fingerprint` Computes a deterministic fingerprint of a single key. - Input: `{ "filePath"?: string, "key": string }` - Output: JSON `{ file, key, result }` Example (inside OpenCode): ```json { "tool": "envsitter_fingerprint", "args": { "filePath": ".env", "key": "DATABASE_URL" } } ``` ### `envsitter_match` Matches key values without printing them. - Input: - `{ "filePath"?: string, "op"?: string, "key"?: string, "keys"?: string[], "allKeys"?: boolean, "candidate"?: string, "candidateEnvVar"?: string }` - Output: - If `key` provided: JSON `{ file, key, op, match }` - If `keys` or `allKeys`: JSON `{ file, op, matches }` Notes: - Provide exactly one selector: `key`, `keys`, or `allKeys: true`. - For ops that compare against a candidate (`is_equal`, `partial_match_*`), provide `candidate` or `candidateEnvVar`. Examples (inside OpenCode): ```json { "tool": "envsitter_match", "args": { "filePath": ".env", "key": "SENTRY_DSN", "op": "exists" } } ``` ```json { "tool": "envsitter_match", "args": { "filePath": ".env", "key": "PORT", "op": "is_number" } } ``` ```json { "tool": "envsitter_match", "args": { "filePath": ".env", "key": "NODE_ENV", "op": "is_equal", "candidate": "production" } } ``` ### `envsitter_match_by_key` Bulk match candidates-by-key without printing values (returns booleans only). - Input: - `{ "filePath"?: string, "candidatesByKey"?: Record, "candidatesByKeyJson"?: string, "candidatesByKeyEnvVar"?: string }` - Output: JSON `{ file, matches }` Note: provide exactly one of `candidatesByKey`, `candidatesByKeyJson`, or `candidatesByKeyEnvVar`. Example (inside OpenCode): ```json { "tool": "envsitter_match_by_key", "args": { "filePath": ".env", "candidatesByKey": { "NODE_ENV": "production" } } } ``` ### `envsitter_scan` Scan value shapes (jwt/url/base64) without printing values. - Input: `{ "filePath"?: string, "detect"?: ("jwt"|"url"|"base64")[], "keysFilterRegex"?: string }` - Output: JSON `{ file, findings }` Example (inside OpenCode): ```json { "tool": "envsitter_scan", "args": { "filePath": ".env", "detect": ["jwt", "url"] } } ``` ## Install & enable in OpenCode (alternatives) ### Option B: local plugin file (project-level) If you want a local plugin file in-repo (or need local overrides), create `.opencode/plugin/envsitter-guard.ts`: ```ts import EnvSitterGuard from "envsitter-guard"; export default EnvSitterGuard; export { EnvSitterGuard } from "envsitter-guard"; ``` Then create `.opencode/package.json` with the dependency so OpenCode can install it: ```json { "dependencies": { "envsitter-guard": "latest" } } ``` Restart OpenCode; files in `.opencode/plugin/` are loaded automatically. ### Option C: global plugin file Place a plugin file in `~/.config/opencode/plugin/` if you want it enabled for all projects. (You can use the same contents as Option B.) ## Development ### Install ```bash npm ci ``` ### Typecheck ```bash npm run typecheck ``` ### Test ```bash npm test ``` ### Build (for publishing) ```bash npm run build ``` ## Notes - This project intentionally avoids reading or printing `.env` values. - EnvSitter CLI exists as an additional safe inspection option, for example: `npx envsitter keys --file .env`. ## License MIT. See `LICENSE`.