2bd7359056
CI / Test (ubuntu-latest, Node 22) (push) Has been skipped
CI / Test (ubuntu-latest, Node 24) (push) Has been skipped
CI / Test (ubuntu-latest, Node 26) (push) Has been skipped
Pages / Build site (push) Has been skipped
Pages / Deploy (push) Has been skipped
CI / Test (macos-latest, Node 24) (push) Has been cancelled
CI / Test (windows-latest, Node 24) (push) Has been cancelled
7.3 KiB
7.3 KiB
AGENTS.md (envsitter)
This repo contains envsitter: a TypeScript (ESM) library + CLI for safely inspecting and matching .env secrets without exposing values.
Ground rules (security)
- Never print or log raw secret values from
.envfiles or external providers. - Do not add any API that returns a plaintext value (no
getValue()style APIs). - Prefer comparisons via deterministic fingerprints (HMAC) and boolean match results.
- Prefer passing candidate secrets via stdin (to avoid shell history):
--candidate-stdin. - Keep all generated peppers/secrets out of git (see
.gitignore). envsitter-guard(separate repo) exposes this package's exports to AI agents as tools, so every new export is agent-reachable.
Project layout
src/index.ts: public exports (the only surface npm users andenvsitter-guardsee)src/envsitter.ts:EnvSitterread-only facade (keys, fingerprints, matchers, scan, redactor)src/cli.ts: CLI entrypoint (bin->dist/cli.js); oneif (cmd === ...)block per command inrun()src/file-ops.ts: file-level mutations for the library API (read, edit, atomic write only whenwrite: true)src/pepper.ts: pepper resolution + auto-create;src/fingerprint.ts: HMAC-SHA-256src/sources/:DotenvFileSource,ExternalCommandSource(both parse withdotenv/parse.ts)src/dotenv/document.ts: the only dotenv grammar; round-trippable, keeps comments, blank lines, and line/column issuessrc/dotenv/parse.ts: values-only view ofdocument.ts(read path)src/dotenv/edit.ts: pure string-in/string-out edits (copy, format, annotate, add/set/unset/delete); owns the edit types thatfile-ops.tsre-exportssrc/dotenv/io.ts:readTextFileOrEmpty,writeTextFileAtomic(keeps file mode, writes through symlinks, new files0600);src/dotenv/utils.ts: value quotingsrc/paths.ts: path policy (isEnvFile,isExampleEnvFile,isSensitiveEnvFile,isEnvSitterPepperFile)src/regex.ts:regexTest; use it for every user-supplied regex (resetslastIndexforg/yflags)src/redact.ts:createRedactorbehindEnvSitter#redactor()and theredactcommand; decides which values look secret (key names, token shape, URL passwords, PEM)src/test/*.test.ts: Node test suite (compiled todist/test/*.test.js);cli.test.tsspawnsdist/cli.js.github/workflows/:ci.yml(push, PR, reusable),release.yml(tagvX.Y.Z-> CI -> npm trusted publishing -> GitHub Release),pages.yml(deployssite/);.github/dependabot.ymlgroups weekly npm and Actions updates.github/scripts/changelog-section.mjs: prints a version's CHANGELOG section; fails if missing or still(Unreleased)site/: static docs site for envsitter and envsitter-guard (no build step);site/DESIGN.mdis its design contract and is not published
Gotchas
- Grammar changes go in
dotenv/document.tsonly;parseDotenv,validate, and every edit follow automatically. - CLI file commands are thin wrappers over
file-ops.ts; change behavior there, then keep the CLI's text (non-JSON) output in step. src/paths.tsis the policyenvsitter-guardenforces. Widening or narrowingisSensitiveEnvFilechanges what agents are blocked from; add a case topaths.test.tsfor every change.src/test/envsitter.test.tsresolves the default pepper (<cwd>/.envsitter/pepper) unlessENVSITTER_PEPPERis set, sonpm testcreates that file on first run.
Build / typecheck / test
There is no linter configured (no ESLint/Prettier). Use tsc + tests as quality gates. Husky pre-commit runs npm test.
npm install
npm run build # tsc -p tsconfig.json -> dist/
npm run typecheck # tsc --noEmit
npm test # build, then scripts/run-tests.mjs runs every dist/test/*.test.js
# tests execute from compiled JS, so build before running one file or one test
npm run build && node --test dist/test/envsitter.test.js
npm run build && node --test --test-name-pattern "outside-in" dist/test/envsitter.test.js
--test-name-pattern takes a substring/regex matching the test('name', ...) title.
Releasing
- Bump
package.json, date the CHANGELOG heading, commit, push tagvX.Y.Z. The Release workflow does the rest; nevernpm publishby hand. - Push to
origin(Gitea,git.boxpositron.dev); it mirrors to GitHub, where the workflows run. Jobs are guarded withif: github.server_url == 'https://github.com'so Gitea's runner skips them. - npm trusted publishing (OIDC) authenticates the publish; no npm token exists in the repo. Keep
release.ymlas the publishing workflow name, because the npm trusted-publisher config pins it.
Bun
The CLI is runnable under Bun:
bun src/cli.ts keys --file .env
Tests currently run under Node’s built-in test runner. If you add Bun tests, keep Node tests working.
Code style and conventions
TypeScript / module system
- This repo is ESM:
"type": "module"inpackage.json. - TS config is strict and includes:
strict: truenoUncheckedIndexedAccess: trueexactOptionalPropertyTypes: true
- Use ESM import specifiers with
.jsextensions when importing local TS modules.- Example:
import { parseDotenv } from '../dotenv/parse.js'
- Example:
- Prefer
node:specifiers for built-ins:import { readFile } from 'node:fs/promises'
Formatting
- No formatter is enforced.
- Keep diffs small and consistent with existing file style.
- Prefer 2-space indentation and trailing commas where already used.
Imports
- Group imports by origin:
node:built-ins- third-party (if any)
- local
./and../
- Avoid unused imports; keep import lists minimal.
Naming
- Types:
PascalCase(EnvSitterFingerprint) - Functions/vars:
camelCase(resolvePepper,candidateValue) - Constants:
camelCaseunless truly global constant. - File names:
kebab-case.tsfor tests; otherwise match existing structure.
Types and optional properties (important)
exactOptionalPropertyTypes is enabled.
- Do not pass
undefinedas a value for an optional property.- Prefer conditional spreads:
...(maybe ? { prop: maybe } : {})
- Prefer conditional spreads:
- Prefer returning
undefinedby omitting optional properties rather than setting them.
Error handling
- Prefer throwing
Errorwith actionable messages. - Avoid empty
catchblocks. - Avoid logging by default. If debugging output is needed:
- Gate it behind
process.env.ENVSITTER_DEBUG === '1'.
- Gate it behind
Security-sensitive comparisons
- Use constant-time comparison for digests:
timingSafeEqualfor fingerprint digest bytes.
IO and side effects
.envvalues should only exist in memory.- Pepper auto-creation is allowed, but must write only to the gitignored location:
- default:
.envsitter/pepper
- default:
- Never write
.envvalues elsewhere (no caches, no exports to new files).
CLI behavior
- CLI should return useful exit codes:
0success / match found1mismatch / no match found2usage or runtime error
- Prefer JSON output for machine consumption when
--jsonis provided. - Never print secret values; only keys, booleans, metadata (length), and fingerprints.
Editor/assistant rules
- No Cursor rules found in
.cursor/rules/or.cursorrules. - No Copilot instructions found in
.github/copilot-instructions.md.
If you add any of the above rule files later, update this document to summarize the relevant constraints.