Sweep LLM agent logs (Claude Code, Codex) for leaked secrets and redact them in place.
LLM agents accumulate surprising amounts of secret material in their logs: API keys pasted into prompts, .env files read into tool results, curl -H "Authorization: Bearer ..." commands, Supabase access tokens baked into MCP config, GCP service-account private keys from cat credentials.json, 1Password outputs that got piped to stdout. llmscrub stacks several detectors to find them and redacts in place with backups.
Four layers:
- trufflehog — ~700 known-format detectors, many verified against live APIs
- gitleaks — regex-heavy ruleset, complementary to trufflehog (catches things like
curl -u user:passauth) - Built-in extras that trufflehog/gitleaks miss:
- PEM private-key blocks (including embedded in JSONL as
\n-escaped strings) - Environment-variable assignments with sensitive key names (
API_KEY=...,DATABASE_URL=...) Authorization: Bearer <token>andBasic <b64>headers- URL-embedded passwords (
scheme://user:pass@host) - Aggressive
KEY=VALredaction when the file context suggests a.envwrite
- PEM private-key blocks (including embedded in JSONL as
- 1Password sweep (opt-in via
--op) — pulls every concealed field from your signed-in 1Password vault and does exact-string matching against the logs. Catches values that are real secrets but don't look like one structurally (homegrown tokens, service passwords, anything the pattern detectors can't recognize). Filtered by entropy so short dictionary-word entries don't cause false positives.
brew install harqian/tap/llmscrubllmscrub scan # report what's there (read-only)
llmscrub scan -v # also list affected files
llmscrub redact --dry-run # preview redactions
llmscrub redact # redact in place with backup to ~/.llmscrub/backups/<ts>/
llmscrub redact --backup "" # disable backup (not recommended)
llmscrub redact --fast # skip gitleaks (~10× faster, lower recall)
llmscrub scan --fast # same for scan
llmscrub scan --op # also sweep all secrets from your 1Password vault
llmscrub redact --fast --op # recommended for the second pass after a pattern scanThe --op flag requires the 1Password CLI (op) installed and signed in (op signin). It walks every concealed field across all LOGIN/PASSWORD/DATABASE/SECURE_NOTE items, filters to plausible-looking secrets (entropy + length), then checks those values verbatim against the logs.
- "access-token=sbp_a6570cf63ad638537a4535fefe4cf2eb5006cd05"
+ "access-token=[REDACTED:SupabaseToken:1324f898]"The 8-char hash in the placeholder is sha256(raw)[:8] — collisions across rotated keys are visible, and you can correlate redactions without exposing the secret.
JSONL files are validated after redaction; if validation fails the file is restored from backup.
| Run | Detectors | Files redacted | Notable |
|---|---|---|---|
| Initial pass (April 18) | trufflehog + gitleaks + extras | 88 | pattern-recognizable tokens (Supabase, GitHub PATs, Cloudflare keys, PEM blocks) |
| 1Password pass (this release) | all of the above + --op |
44 additional, 860 substitutions | 7 real secrets pattern scanners had missed — including one 46-char API token that had leaked verbatim into 17 separate conversation logs |
The pattern-based layer catches the structurally-recognizable tokens. The 1Password layer catches everything else — homegrown tokens, user passwords, anything your agent pasted that happens to live in your vault.
- We do not guarantee zero false negatives. Novel-format tokens with no public signature (random 32-char alphanumeric strings used as API keys by some internal service) are undetectable by any scanner without a rule for that format. Our extra detectors catch many such cases via context (key name, URL placement, Bearer header) but some slip through.
- False positives are acceptable. We intentionally redact some non-secrets (UUIDs caught by heuristic detectors, high-entropy identifiers). Removing session IDs from logs doesn't harm anything; leaving an API key does.
- Rotate anything trufflehog flagged as
Verified=true. Those keys were actively working against the live API when scanned. Redacting the log doesn't revoke them.
Paste-able agent prompts — copy either block and give it to Claude Code, Codex, or any CLI-capable agent.
Install llmscrub and run a secret scan against my Claude Code and Codex logs,
then summarize the findings.
Steps:
1. brew install harqian/tap/llmscrub (installs trufflehog + gitleaks as deps)
2. llmscrub scan --fast (fast pass: trufflehog + built-in extras, skips gitleaks)
3. For a thorough pass afterwards: llmscrub scan (adds gitleaks; slower)
4. Report: total unique findings, breakdown by detector, and any detector named
with the "gl:" prefix or a trufflehog Verified=true entry — those need key
rotation, not just redaction.
Do not run `llmscrub redact` in this block — only scan.
Set up a launchd agent (macOS) or cron entry that runs llmscrub daily to redact
any newly-leaked secrets in my LLM agent logs.
Steps:
1. Ensure llmscrub is installed: brew install harqian/tap/llmscrub
2. On macOS, create ~/Library/LaunchAgents/com.harqian.llmscrub.plist with a
daily schedule (2am), command: /opt/homebrew/bin/llmscrub redact, stdout/
stderr to ~/.llmscrub/cron.log. Load it with: launchctl load <path>.
3. On Linux, add to crontab -e:
0 2 * * * /usr/local/bin/llmscrub redact >> ~/.llmscrub/cron.log 2>&1
4. Verify the job by running llmscrub redact --dry-run once by hand first, then
tail -f ~/.llmscrub/cron.log after the next scheduled run.
Backups land in ~/.llmscrub/backups/<timestamp>/ — mention to the user they may
want a cleanup policy (e.g. keep last 7 days) once they're comfortable with the
tool.
~/.claude/projects/— Claude Code's per-project JSONL session logs~/.codex/— Codex sessions, TUI logs, shell snapshots
Override with positional paths: llmscrub scan /path/to/logs.
- Screenshots, PDFs, and binary files are not scanned for visible tokens
- MCP server logs outside
~/.claudeand~/.codexrequire explicit paths 1password(op) command outputs are only caught if the resulting secret matches a known format — a plain-text password piped to stdout and never repeated is hard to identify structurally
MIT
