geiger
Atomburstofficial/geiger/CLAUDE.md
Read this first. It captures what isn't obvious from the code. (This file is public — project/business context lives elsewhere.) geiger — a Geiger counter for AI agents. One read-only command that inventories every AI agent, harness, MCP server, plugin, and AI extension on a machine and reports what each can touch. Published as geiger-scan on npm (npx geiger-scan), MIT, from github.com/Atomburstofficial/geiger. 1. Read-only. The only writes are the report files the user names (--json, --html). Never execute anything discovered…
CLAUDE.md153 starsChanged 24 days ago
# CLAUDE.md — working context for geiger Read this first. It captures what isn't obvious from the code. (This file is public — project/business context lives elsewhere.) ## What this is **geiger** — a Geiger counter for AI agents. One read-only command that inventories every AI agent, harness, MCP server, plugin, and AI extension on a machine and reports what each can touch. Published as `geiger-scan` on npm (`npx geiger-scan`), MIT, from `github.com/Atomburstofficial/geiger`. ## Hard invariants (the product IS these — never trade them away) 1. **Read-only.** The only writes are the report files the user names (`--json`, `--html`). Never execute anything discovered (no npm, no binaries, nothing). Reads configs and directories only. 2. **No telemetry.** No endpoint exists. Do not add one, "just for counts." 3. **Secrets by shape only.** Report key name + file + shape, never any part of a value. `redact()` runs on every serialized output path as defense-in-depth, and `test/engine.test.js` has a REDACTION GUARANTEE test that enforces it. Any new output path must go through `redact()`. 4. **Zero runtime dependencies, no build step.** The source people read is the code that runs — that readability is the trust model. Keep modules small; a reader should manage the whole thing in one sitting. 5. **Never crash.** Detectors run inside try/catch in `engine.run()`; failures become `diagnostics` entries, never swallowed, never fatal. File readers in `util/fsx.js` never throw; `readText` strips a leading UTF-8 BOM (Windows tools write one by default — a BOM'd config once vanished from reports); `parseJsonTolerant` handles comments, trailing commas, and lone Windows backslashes (documented `\t`-ambiguity caveat). Parse errors are labeled by location only: V8's "Unexpected token" messages quote file text, which can be part of a credential. Every detector that reads a JSON config must turn a parse error into `common.js#unparseableFinding`, never skip the file. 6. **Honest labels.** Exposure labels (EXECUTES, HOLDS-SECRETS, BROAD-FILESYSTEM, BROAD-WEB, NETWORK, UNKNOWN-ORIGIN) claim only what a config proves. Partially-parseable formats (TOML, JetBrains XML) are shape-scanned and marked `medium`/`low` confidence rather than skipped or overclaimed. Limitations are stated in README, terminal footer, and HTML footer — keep them current. ## Architecture - `bin/geiger.js` — CLI: flags `--help --json <f> --html <f> --strict --home <dir> --path <dir>` (repeatable) `--diff <baseline.json>`. With `--diff`, `--strict` gates on drift (new hot findings) only. - `src/engine.js` — `run()` (detector loop), `finding()` normalizer, `assessMcpServer()` (ONE definition of what an MCP server entry means: wrapper-aware `--` split for policy agents, npx/docker/local-script origin tracing), `actionsFor()` (plain-language remediation). - `src/detectors/` — one module per ecosystem, registered in `index.js`. Adding an ecosystem = one module + one line + a fixture + an assertion. `common.js#findingsFromMcpFile(file, detector, hostLabel, key)` for any JSON config holding MCP servers (key varies: `mcpServers`, `servers`, `context_servers`, `mcp`). - `src/redact.js` — shape regexes, `classifyValue`, `scanEnvObject` (key-name heuristic), `scanText`, `redact()`. - `src/patterns.js` — shape patterns for text geiger reads but never runs: `RISKY_COMMANDS` (pipe-to-shell, base64 exec, bare-IP endpoint, credential file read, history rewrite) and `STEERING_TEXT` (instruction override, hide-from-user, exfiltration, approval bypass). A hit is a note, never a verdict; keep the lists short and keep a benign twin in the tests so a new pattern cannot silently start false-positiving. - `src/skill-scan.js` — `inspectSkill(dir)` reads a skill's instruction file (SKILL.md and friends), folds YAML block-scalar descriptions (`description: >` — reading line one yields a useless `>`), and returns notes + shape-only secrets. The body is never copied into a report. - `src/detectors/git-hooks.js` — `.git/hooks` (never `*.sample`), `core.hooksPath`, and `[merge "x"] driver` commands. `programsIn()` reports the programs a hook invokes rather than its first line, because real hooks open with guard clauses: it skips comments, heredoc bodies, multi-line `python -c "..."` payloads and redirections. Git fixtures cannot be checked in (git refuses any path containing a `.git` component) — build them in a temp dir inside the test. - `src/platform.js` — all per-OS paths, honoring `GEIGER_HOME` / `GEIGER_PLATFORM` env for tests. Detectors stay platform-agnostic. - `src/diff.js` — baseline comparison; identity = detector|kind|name|origin; escalation (gained exposure/credential) = CHANGED, not remove+add. - `src/report/terminal.js` + `html.js` — both render the drift section; both pipe everything through `redact()`. - `data/known-agents.json` — community-updatable IDs (VS Code extensions, npm globals, browser-extension name hints). ## Testing - `npm test` (bare `node --test` — passing a dir breaks on Windows). - Fixtures live in `test/fixtures/home1` (a fake home dir; detectors read it via `GEIGER_HOME` + `GEIGER_PLATFORM=win32`). `empty` fixture must keep yielding zero findings/diagnostics. - Every new detector: fixture files + assertions in the fixture-home test. - Dogfood after changes: `node bin/geiger.js` on the dev machine — output should be diagnostic-free. ## Release process (fully automated — do NOT `npm publish` manually) 1. Bump `version` in package.json, write `docs/release-notes-vX.Y.Z.md` (H1 becomes the GitHub Release title, rest becomes the body). 2. Commit, push, then `git tag -a vX.Y.Z -m "..."` and push the tag. 3. `.github/workflows/release.yml` tests on a clean runner, publishes to npm via **Trusted Publishing** (OIDC — no tokens anywhere) with `--provenance`, and creates the GitHub Release. Registry propagation takes ~2–4 minutes; verify with `npm view geiger-scan version` and an `npx geiger-scan@X.Y.Z` smoke run. 4. `docs/` and `.github/` stay out of the npm tarball via the package.json `files` whitelist — keep it that way. ## Conventions - **No AI-attribution trailers in commits** (owner policy; history was rewritten once to remove them — don't reintroduce). - Comment style: sparse, only for constraints the code can't show. - Windows dev note: backslash-heavy content (fixtures, samples) goes through editor tooling, not bash heredocs/inline `node -e` — quoting mangles it. - Sample reports in `docs/` (`sample-report.html/.json/.png`) are generated from the fixture home and sanitized to generic paths (`C:\Users\alex`) — regenerate + re-sanitize + re-screenshot together if report rendering changes. - The README coverage table has a mirror on the product page (atomburst.io/geiger, maintained in a separate repo) — flag coverage changes so the page gets synced.
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

