Files
envsitter/AGENTS.md
T
David Ibia 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
Document 0.1.0 changes, releasing, and the docs site
2026-10-20 15:10:15 +01:00

149 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `.env` files 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 and `envsitter-guard` see)
- `src/envsitter.ts`: `EnvSitter` read-only facade (keys, fingerprints, matchers, scan, redactor)
- `src/cli.ts`: CLI entrypoint (`bin` -> `dist/cli.js`); one `if (cmd === ...)` block per command in `run()`
- `src/file-ops.ts`: file-level mutations for the library API (read, edit, atomic write only when `write: true`)
- `src/pepper.ts`: pepper resolution + auto-create; `src/fingerprint.ts`: HMAC-SHA-256
- `src/sources/`: `DotenvFileSource`, `ExternalCommandSource` (both parse with `dotenv/parse.ts`)
- `src/dotenv/document.ts`: the only dotenv grammar; round-trippable, keeps comments, blank lines, and line/column issues
- `src/dotenv/parse.ts`: values-only view of `document.ts` (read path)
- `src/dotenv/edit.ts`: pure string-in/string-out edits (copy, format, annotate, add/set/unset/delete); owns the edit types that `file-ops.ts` re-exports
- `src/dotenv/io.ts`: `readTextFileOrEmpty`, `writeTextFileAtomic` (keeps file mode, writes through symlinks, new files `0600`); `src/dotenv/utils.ts`: value quoting
- `src/paths.ts`: path policy (`isEnvFile`, `isExampleEnvFile`, `isSensitiveEnvFile`, `isEnvSitterPepperFile`)
- `src/regex.ts`: `regexTest`; use it for every user-supplied regex (resets `lastIndex` for `g`/`y` flags)
- `src/redact.ts`: `createRedactor` behind `EnvSitter#redactor()` and the `redact` command; decides which values look secret (key names, token shape, URL passwords, PEM)
- `src/test/*.test.ts`: Node test suite (compiled to `dist/test/*.test.js`); `cli.test.ts` spawns `dist/cli.js`
- `.github/workflows/`: `ci.yml` (push, PR, reusable), `release.yml` (tag `vX.Y.Z` -> CI -> npm trusted publishing -> GitHub Release), `pages.yml` (deploys `site/`); `.github/dependabot.yml` groups 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.md` is its design contract and is not published
### Gotchas
- Grammar changes go in `dotenv/document.ts` only; `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.ts` is the policy `envsitter-guard` enforces. Widening or narrowing `isSensitiveEnvFile` changes what agents are blocked from; add a case to `paths.test.ts` for every change.
- `src/test/envsitter.test.ts` resolves the default pepper (`<cwd>/.envsitter/pepper`) unless `ENVSITTER_PEPPER` is set, so `npm test` creates 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`.
```bash
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 tag `vX.Y.Z`. The Release workflow does the rest; never `npm publish` by hand.
- Push to `origin` (Gitea, `git.boxpositron.dev`); it mirrors to GitHub, where the workflows run. Jobs are guarded with `if: 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.yml` as 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"` in `package.json`.
- TS config is strict and includes:
- `strict: true`
- `noUncheckedIndexedAccess: true`
- `exactOptionalPropertyTypes: true`
- Use ESM import specifiers with `.js` extensions when importing local TS modules.
- Example: `import { parseDotenv } from '../dotenv/parse.js'`
- 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:
1) `node:` built-ins
2) third-party (if any)
3) local `./` and `../`
- Avoid unused imports; keep import lists minimal.
### Naming
- Types: `PascalCase` (`EnvSitterFingerprint`)
- Functions/vars: `camelCase` (`resolvePepper`, `candidateValue`)
- Constants: `camelCase` unless truly global constant.
- File names: `kebab-case.ts` for tests; otherwise match existing structure.
### Types and optional properties (important)
`exactOptionalPropertyTypes` is enabled.
- Do not pass `undefined` as a value for an optional property.
- Prefer conditional spreads:
- `...(maybe ? { prop: maybe } : {})`
- Prefer returning `undefined` by omitting optional properties rather than setting them.
### Error handling
- Prefer throwing `Error` with actionable messages.
- Avoid empty `catch` blocks.
- Avoid logging by default. If debugging output is needed:
- Gate it behind `process.env.ENVSITTER_DEBUG === '1'`.
### Security-sensitive comparisons
- Use constant-time comparison for digests:
- `timingSafeEqual` for fingerprint digest bytes.
### IO and side effects
- `.env` values should only exist in memory.
- Pepper auto-creation is allowed, but must write only to the gitignored location:
- default: `.envsitter/pepper`
- Never write `.env` values elsewhere (no caches, no exports to new files).
## CLI behavior
- CLI should return useful exit codes:
- `0` success / match found
- `1` mismatch / no match found
- `2` usage or runtime error
- Prefer JSON output for machine consumption when `--json` is 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.