https://github.com/spences10/nopeek
CLI for LLM agent secret safety — load env secrets without exposing values
https://github.com/spences10/nopeek
agents cli coding-agents env llm redaction secrets security
Last synced: about 10 hours ago
JSON representation
CLI for LLM agent secret safety — load env secrets without exposing values
- Host: GitHub
- URL: https://github.com/spences10/nopeek
- Owner: spences10
- License: mit
- Created: 2026-04-03T15:04:10.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-06-19T20:33:46.000Z (15 days ago)
- Last Synced: 2026-06-19T22:14:16.937Z (15 days ago)
- Topics: agents, cli, coding-agents, env, llm, redaction, secrets, security
- Language: TypeScript
- Homepage:
- Size: 241 KB
- Stars: 11
- Watchers: 0
- Forks: 1
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# nopeek
[](https://viteplus.dev)
[](https://vitest.dev)
CLI for LLM agent secret safety. nopeek loads environment secrets for
coding agents without exposing secret values in tool output or chat
context. Agents see key _names_, not key _values_.
## Quick Start
In an LLM coding session, tell the agent to use nopeek:
```
"run npx nopeek load .env then use $DATABASE_URL to query the users table"
"use npx nopeek load .env --only STRIPE_KEY and then curl the billing API"
"run npx nopeek load .env --only API_KEY,API_SECRET and test the auth endpoint"
```
The agent runs the command, gets back only the key name, and uses the
variable in subsequent commands without seeing the actual value.
## How It Works
Shell command output visible to an LLM coding agent may be sent to a
model provider and retained by that provider. nopeek prevents secrets
from appearing in that output.
**Step 1.** Tell your agent to use nopeek. It just needs to run the
command; it will self-discover subcommands and flags from the CLI
output:
```bash
npx nopeek load .env --only DATABASE_URL
```
**Step 2.** nopeek injects the value into the session environment and
prints only the key name:
```
Loaded 1 key from .env: DATABASE_URL
```
**Step 3.** The agent can now use the variable by name without ever
seeing the value:
```bash
psql $DATABASE_URL -c "SELECT count(*) FROM users"
```
> **Important:** Your agent does not know about nopeek unless you
> mention it. You do not need to spell out the full command, just
> mention `{npx,pnpx,bunx} nopeek` and the agent can discover the
> rest.
### Optional agent reminders
If you use a harness with persistent system-prompt extensions, add a
small reminder so the model reaches for nopeek automatically instead
of needing the Quick Start text in every prompt. For Pi users, the
`@spences10/pi-nopeek` extension does this:
```bash
pi install npm:@spences10/pi-nopeek
```
[`my-pi`](https://github.com/spences10/my-pi) already includes this
reminder by default. This is optional: nopeek remains harness-agnostic
and still works anywhere when you mention it in the session.
Three modes depending on environment:
| Context | What happens |
| --------------------------------------------- | ------------------------------------------------------------------ |
| Agent session with env-file injection support | Writes directly to env file; future commands can use the variables |
| Agent session without env-file injection | Writes to temp file and outputs a `source` command |
| Regular shell | Prints `export` statements for `eval` |
When env-file injection is unavailable, `load` cannot mutate the
parent shell or future unrelated shell processes. Use the reported
`next_command`, or run your command in the same shell after sourcing
or evaluating the output.
## Usage
No install needed. Your agent runs it directly via `npx`:
```bash
npx nopeek load .env
npx nopeek load .env --only DATABASE_URL
npx nopeek run .env --only API_KEY -- sh -c 'curl -H "Authorization: Bearer $API_KEY" https://api.example.com'
npx nopeek set MY_API_KEY --from-env
npx nopeek status
```
All commands are designed to be run inside an LLM coding session. Just
mention `nopeek` in your prompt. The agent will discover the right
subcommand from the CLI output.
## Commands
### `load` - Load secrets from .env or .tfvars files
```bash
npx nopeek load .env
npx nopeek load .env --only DATABASE_URL,API_KEY
npx nopeek load .env --shell bash # eval/source-safe shell output
npx nopeek load .env --persist # also save to config for future sessions
npx nopeek load terraform.tfvars --only prod_password
npx nopeek load production.tfvars.json --only db_password
```
Supported formats:
| Extension | Format |
| ---------------- | ----------------------------- |
| `.env`, `.env.*` | `KEY=value` |
| `.tfvars` | `key = "value"` (HCL strings) |
| `.tfvars.json` | JSON top-level strings |
For `.tfvars` files, only top-level quoted string values are loaded.
Maps, lists, numbers, and booleans are skipped.
The `--persist` flag saves keys to `~/.config/nopeek/config.json` so a
SessionStart hook can auto-inject them on future sessions.
`load` returns a `method` field in JSON output. If `method` is
`env_file`, the keys are available to future session commands. If
`method` is `export` or `source_file`, the keys are not available
until you evaluate the returned `next_command`, for example:
```bash
eval "$(npx nopeek load .env --shell bash)"
source <(npx nopeek load .env --shell bash)
npx nopeek load .env --shell fish | source
```
`--shell` supports `bash`, `zsh`, and `fish`. Shell assignments are
written to stdout for the shell to consume; status/progress messages
go to stderr.
### `run` - Run one command with loaded secrets
```bash
npx nopeek run .env --only API_KEY -- node ./script.js
npx nopeek run .env --only INGEST_TOKEN,FEEDGEN_SERVICE_URL -- sh -c 'curl -H "Authorization: Bearer $INGEST_TOKEN" "$FEEDGEN_SERVICE_URL"'
npx nopeek run terraform.tfvars --only prod_password -- ./deploy.sh
npx nopeek run production.tfvars.json --only DATABASE_URL -- sh -c 'psql "$DATABASE_URL" -c "select 1"'
```
`run` injects selected keys into only the child process environment
and preserves the child command exit code. Use `sh -c` when you need
shell expansion, pipes, redirects, or inline `$VARIABLE` expansion
inside the child process.
### `set` - Store a secret key
```bash
npx nopeek set MY_API_KEY --from-env # read from current shell env
npx nopeek set MY_API_KEY # interactive prompt (TTY only)
```
Stores to `~/.config/nopeek/config.json` with `0600` permissions. This
is plaintext at rest; use `set`/`--persist` only for secrets you are
comfortable storing in your user config.
> **Note:** `--value` is rejected inside detected LLM agent sessions.
> The value would appear in the conversation. Use `--from-env`
> instead.
### `list` - Show available keys
```bash
npx nopeek list
```
Shows key names and sources without values.
### `remove` - Remove a stored key
```bash
npx nopeek remove MY_API_KEY
```
### `init` - Scan and configure cloud CLIs
```bash
npx nopeek init
```
Detects installed cloud CLIs, checks their auth configuration, and
stores profile mappings.
| CLI | Safer pattern | Detection |
| -------- | ---------------------------------------------------- | -------------------------------------------------- |
| `aws` | Named profiles (`AWS_PROFILE`) | `~/.aws/credentials` + env vars |
| `hcloud` | Named contexts (`HCLOUD_CONTEXT`) | `~/.config/hcloud/cli.toml` |
| `gcloud` | Named configurations (`CLOUDSDK_ACTIVE_CONFIG_NAME`) | active gcloud account + inline credential env vars |
| `az` | Azure CLI cached login + active subscription | `az account show` + inline credential env vars |
### `status` - Show current state
```bash
npx nopeek status
```
Shows session type, stored keys, CLI profiles, and detected CLIs.
### `audit` - Scan for exposed secrets
```bash
npx nopeek audit
npx nopeek audit ./path/to/dir
```
Scans for `.env` files and reports secrets found using pattern
matching (AWS keys, bearer tokens, API keys, private keys, connection
strings, etc.). Checks `.gitignore` coverage.
## Security
- **Key name validation** - env key names are validated against
`^[a-zA-Z_][a-zA-Z0-9_]*$` to prevent shell injection
- **Secure file permissions** - config dir is `0700`, config file is
`0600`, temp env files are `0600`
- **Atomic writes** - config is written via temp file + rename to
prevent corruption
- **Key validation before output** - invalid env names are rejected
before shell exports are printed
- **No values in stdout** - in detected agent sessions, values are
written to env files or temp files; only `source` paths or key names
reach stdout
- **Plaintext config warning** - persisted keys are local plaintext
secrets protected by file permissions, not encryption
## Recommended Agent Deny Rules
nopeek is the primary defense, but deny rules are a useful safety net
in any agent runtime that supports them. Block the agent from reading
secret files or embedding credentials inline in commands:
```jsonc
{
"permissions": {
"deny": [
// Block reading secret files
"Read(.env)",
"Read(*.env)",
"Read(*.tfvars)",
"Read(*credentials*)",
"Read(*secret*)",
"Read(**/.config/gcloud/**)",
"Read(**/.azure/**)",
"Read(*service-account*.json)",
// Block cat/head on secret files
"Bash(cat .env)",
"Bash(cat *.env*)",
"Bash(cat *tfvars*)",
"Bash(cat *credentials*)",
"Bash(cat *secret*)",
// Block inline credentials in commands
"Bash(PGPASSWORD*)",
"Bash(*HCLOUD_TOKEN*)",
"Bash(hcloud context create*)",
"Bash(*GOOGLE_APPLICATION_CREDENTIALS*)",
"Bash(*CLOUDSDK_AUTH_ACCESS_TOKEN*)",
"Bash(*GOOGLE_OAUTH_ACCESS_TOKEN*)",
"Bash(*AZURE_CLIENT_SECRET*)",
"Bash(*ARM_CLIENT_SECRET*)",
"Bash(*AZURE_PASSWORD*)",
"Bash(*AZURE_ACCESS_TOKEN*)",
"Bash(*ARM_ACCESS_KEY*)",
// Block fetching secret values from cloud providers
"Bash(*secretsmanager get-secret-value*)",
"Bash(*hetzner*secret*)",
"Bash(*hetzner*access_key*)",
"Bash(gcloud auth print-access-token*)",
"Bash(gcloud auth application-default print-access-token*)",
"Bash(az account get-access-token*)",
],
},
}
```
Without these rules, an agent can still read a `.tfvars` or `.env`
file directly and hardcode the values into a shell command. nopeek
prevents secrets from appearing in _output_, but deny rules prevent
the agent from _reading_ the files in the first place.
## Limitations
- **Pattern-based secret detection is best-effort.** The audit
patterns catch known formats but cannot catch every possible secret.
- **Agent detection is marker-based.** nopeek detects common agent
environment markers and env-file injection, but no CLI can guarantee
every harness is identified.
- **Temp files exist on disk briefly.** Written to `/tmp/nopeek/` with
`0600` perms, but values are on disk until the file is cleaned up.
- **Persisted keys are plaintext.** `set` and `load --persist` store
values in `~/.config/nopeek/config.json` with `0600` permissions.
- **Output redaction is not comprehensive.** Prefer loading secrets as
environment variables so values never need to be printed.
## License
MIT