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

7.3 KiB
Raw Blame History

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.

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.