Improve README for match ops and LLM contract
This commit is contained in:
68
README.md
68
README.md
@@ -5,9 +5,10 @@ Safely inspect and match `.env` secrets **without ever printing values**.
|
|||||||
`envsitter` is designed for LLM/agent workflows where you want to:
|
`envsitter` is designed for LLM/agent workflows where you want to:
|
||||||
|
|
||||||
- List keys present in an env source (`.env` file or external provider)
|
- List keys present in an env source (`.env` file or external provider)
|
||||||
- Check whether a key’s value matches a candidate value you provide at runtime ("outside-in")
|
- Compare a key’s value to a candidate value you provide at runtime ("outside-in")
|
||||||
- Do bulk matching (one candidate against many keys, or candidates-by-key)
|
- Do bulk matching (one candidate against many keys, or candidates-by-key)
|
||||||
- Produce deterministic fingerprints for comparisons/auditing
|
- Produce deterministic fingerprints for comparisons/auditing
|
||||||
|
- Ask boolean questions about values (empty/prefix/suffix/regex/type-ish checks) without ever returning the value
|
||||||
|
|
||||||
Related: https://github.com/boxpositron/envsitter-guard — an OpenCode plugin that blocks agents/tools from reading or editing sensitive `.env*` files (preventing accidental secret leaks), while still allowing safe inspection via EnvSitter-style tools (keys + deterministic fingerprints; never values).
|
Related: https://github.com/boxpositron/envsitter-guard — an OpenCode plugin that blocks agents/tools from reading or editing sensitive `.env*` files (preventing accidental secret leaks), while still allowing safe inspection via EnvSitter-style tools (keys + deterministic fingerprints; never values).
|
||||||
|
|
||||||
@@ -48,6 +49,14 @@ The pepper file is created with mode `0600` when possible, and `.envsitter/` is
|
|||||||
|
|
||||||
## CLI usage
|
## CLI usage
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
- `keys --file <path> [--filter-regex <re>]`
|
||||||
|
- `fingerprint --file <path> --key <KEY>`
|
||||||
|
- `match --file <path> (--key <KEY> | --keys <K1,K2> | --all-keys) [--op <op>] [--candidate <value> | --candidate-stdin]`
|
||||||
|
- `match-by-key --file <path> (--candidates-json <json> | --candidates-stdin)`
|
||||||
|
- `scan --file <path> [--keys-regex <re>] [--detect jwt,url,base64]`
|
||||||
|
|
||||||
### List keys
|
### List keys
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -116,27 +125,6 @@ node -e "process.stdout.write('/^sk-[a-z]+-/i')" \
|
|||||||
envsitter match --file .env --key OPENAI_API_KEY --op exists --json
|
envsitter match --file .env --key OPENAI_API_KEY --op exists --json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Output contract (for LLMs)
|
|
||||||
|
|
||||||
General rules:
|
|
||||||
|
|
||||||
- Never output secret values; treat all values as sensitive.
|
|
||||||
- Prefer `--candidate-stdin` over `--candidate` to avoid shell history.
|
|
||||||
- Exit codes: `0` match found, `1` no match, `2` error/usage.
|
|
||||||
|
|
||||||
JSON outputs:
|
|
||||||
|
|
||||||
- `keys --json` -> `{ "keys": string[] }`
|
|
||||||
- `fingerprint` -> `{ "key": string, "algorithm": "hmac-sha256", "fingerprint": string, "length": number, "pepperSource": "env"|"file", "pepperFilePath"?: string }`
|
|
||||||
- `match --json` (single key) ->
|
|
||||||
- default op (not provided): `{ "key": string, "match": boolean }`
|
|
||||||
- with `--op`: `{ "key": string, "op": string, "match": boolean }`
|
|
||||||
- `match --json` (bulk keys / all keys) ->
|
|
||||||
- default op (not provided): `{ "matches": Array<{ "key": string, "match": boolean }> }`
|
|
||||||
- with `--op`: `{ "op": string, "matches": Array<{ "key": string, "match": boolean }> }`
|
|
||||||
- `match-by-key --json` -> `{ "matches": Array<{ "key": string, "match": boolean }> }`
|
|
||||||
- `scan --json` -> `{ "findings": Array<{ "key": string, "detections": Array<"jwt"|"url"|"base64"> }> }`
|
|
||||||
|
|
||||||
### Match one candidate against multiple keys
|
### Match one candidate against multiple keys
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -178,6 +166,27 @@ Optionally restrict which keys to scan:
|
|||||||
envsitter scan --file .env --keys-regex "/(JWT|URL)/" --detect jwt,url
|
envsitter scan --file .env --keys-regex "/(JWT|URL)/" --detect jwt,url
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Output contract (for LLMs)
|
||||||
|
|
||||||
|
General rules:
|
||||||
|
|
||||||
|
- Never output secret values; treat all values as sensitive.
|
||||||
|
- Prefer `--candidate-stdin` over `--candidate` to avoid shell history.
|
||||||
|
- Exit codes: `0` match found, `1` no match, `2` error/usage.
|
||||||
|
|
||||||
|
JSON outputs:
|
||||||
|
|
||||||
|
- `keys --json` -> `{ "keys": string[] }`
|
||||||
|
- `fingerprint` -> `{ "key": string, "algorithm": "hmac-sha256", "fingerprint": string, "length": number, "pepperSource": "env"|"file", "pepperFilePath"?: string }`
|
||||||
|
- `match --json` (single key) ->
|
||||||
|
- default op (not provided): `{ "key": string, "match": boolean }`
|
||||||
|
- with `--op`: `{ "key": string, "op": string, "match": boolean }`
|
||||||
|
- `match --json` (bulk keys / all keys) ->
|
||||||
|
- default op (not provided): `{ "matches": Array<{ "key": string, "match": boolean }> }`
|
||||||
|
- with `--op`: `{ "op": string, "matches": Array<{ "key": string, "match": boolean }> }`
|
||||||
|
- `match-by-key --json` -> `{ "matches": Array<{ "key": string, "match": boolean }> }`
|
||||||
|
- `scan --json` -> `{ "findings": Array<{ "key": string, "detections": Array<"jwt"|"url"|"base64"> }> }`
|
||||||
|
|
||||||
## Library API
|
## Library API
|
||||||
|
|
||||||
### Basic usage
|
### Basic usage
|
||||||
@@ -192,6 +201,18 @@ const fp = await es.fingerprintKey('OPENAI_API_KEY');
|
|||||||
const match = await es.matchCandidate('OPENAI_API_KEY', 'candidate-secret');
|
const match = await es.matchCandidate('OPENAI_API_KEY', 'candidate-secret');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Match operators via the library
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { EnvSitter } from 'envsitter';
|
||||||
|
import type { EnvSitterMatcher } from 'envsitter';
|
||||||
|
|
||||||
|
const es = EnvSitter.fromDotenvFile('.env');
|
||||||
|
|
||||||
|
const matcher: EnvSitterMatcher = { op: 'partial_match_prefix', prefix: 'sk-' };
|
||||||
|
const ok = await es.matchKey('OPENAI_API_KEY', matcher);
|
||||||
|
```
|
||||||
|
|
||||||
### Bulk matching
|
### Bulk matching
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@@ -202,6 +223,9 @@ const es = EnvSitter.fromDotenvFile('.env');
|
|||||||
// One candidate tested against a set of keys
|
// One candidate tested against a set of keys
|
||||||
const matches = await es.matchCandidateBulk(['OPENAI_API_KEY', 'ANTHROPIC_API_KEY'], 'candidate-secret');
|
const matches = await es.matchCandidateBulk(['OPENAI_API_KEY', 'ANTHROPIC_API_KEY'], 'candidate-secret');
|
||||||
|
|
||||||
|
// One matcher tested against a set of keys
|
||||||
|
const prefixMatches = await es.matchKeyBulk(['OPENAI_API_KEY', 'ANTHROPIC_API_KEY'], { op: 'partial_match_prefix', prefix: 'sk-' });
|
||||||
|
|
||||||
// Candidates-by-key
|
// Candidates-by-key
|
||||||
const byKey = await es.matchCandidatesByKey({
|
const byKey = await es.matchCandidatesByKey({
|
||||||
OPENAI_API_KEY: 'sk-...',
|
OPENAI_API_KEY: 'sk-...',
|
||||||
|
|||||||
Reference in New Issue
Block a user