{"slug": "show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk", "title": "Show HN: Claude Scrub - redact secrets from Claude Code history on disk", "summary": "Developer overflowy released claude-scrub, an open-source Go tool that scans the plain-text history Claude Code stores under ~/.claude and replaces detected secrets with labels such as DB_PASSWORD=[REDACTED:db_password]. The tool covers session transcripts including subagent and tool-result files, prompt history, the paste cache, file history and shell snapshots, drawing on four sources ranked from a user secrets file and --env-file down to gitleaks rules covering 200+ token formats, and it can run automatically via a SessionEnd hook in ~/.claude/settings.json. It requires Go 1.26 or newer, ships builds for darwin_amd64, darwin_arm64, linux_amd64, linux_arm64, windows_amd64.exe and windows_arm64.exe, skips files modified in the last 10 minutes unless --min-age is changed, and refuses to write without a terminal unless --force is passed.", "body_md": "Claude Code keeps everything you and the model saw in plain text under\n`~/.claude`. That includes every `cat .env`, every config file it read and\nevery token you pasted into a prompt. claude-scrub finds the secrets in that\nhistory and replaces each one with a label:\n\n```\nDB_PASSWORD=[REDACTED:db_password]\n```\n\nIt covers session transcripts (subagents and tool results included), prompt history, the paste cache, file history and shell snapshots. It leaves settings, plugins and live session state alone.\n\nDownload the file for your platform from the\n[releases page](https://github.com/overflowy/claude-scrub/releases/latest),\nmake it executable, and put it on your `PATH`. With the GitHub CLI, on an\nApple Silicon Mac:\n\n```\ngh release download -R overflowy/claude-scrub -p '*darwin_arm64'\nchmod +x claude-scrub_*_darwin_arm64\nmv claude-scrub_*_darwin_arm64 ~/.local/bin/claude-scrub\n```\n\nThe other builds are `darwin_amd64`, `linux_amd64`, `linux_arm64`,\n`windows_amd64.exe` and `windows_arm64.exe`. `checksums.txt` has their\nSHA-256 sums. The macOS binaries aren't signed, so if macOS refuses to run\none, clear the quarantine flag with\n`xattr -d com.apple.quarantine ~/.local/bin/claude-scrub`.\n\nNeeds Go 1.26 or newer.\n\n```\ngo install github.com/overflowy/claude-scrub@latest\n```\n\nThis puts the binary in `$(go env GOPATH)/bin`, usually `~/go/bin`.\n\nNeeds Go 1.26 or newer and [Task](https://taskfile.dev).\n\n```\ngit clone https://github.com/overflowy/claude-scrub\ncd claude-scrub\ntask install   # builds ~/.local/bin/claude-scrub\n```\n\n`task --list` shows the other tasks. `task check` runs gofmt, go fix, go vet\nand the tests without changing anything.\n\nStart with a dry run. It prints a table of labels with how many values and occurrences it found, and changes nothing.\n\n```\nclaude-scrub --dry-run\n```\n\nTo see why something was flagged, ask for the text around each value of a label. The snippet is redacted too.\n\n```\nclaude-scrub -n --explain token\n```\n\nWhen the report looks right, run it for real. It asks before writing.\n\n```\nclaude-scrub\n```\n\nWithout a terminal to confirm on, it refuses to write unless you pass\n`--force`.\n\nIt skips files modified in the last 10 minutes, so it won't rewrite a session\nthat's still running. Change that with `--min-age`. Paths given as arguments\nare scanned whatever their age.\n\nAdd a SessionEnd hook to `~/.claude/settings.json` and each session gets\nscrubbed when it ends. Use the path you installed the binary to:\n\n```\n{\n  \"hooks\": {\n    \"SessionEnd\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"~/.local/bin/claude-scrub --hook\" }] }\n    ]\n  }\n}\n```\n\nThe hook redacts that session's transcript, its subagent and tool result\nfiles, and `history.jsonl`. It prints nothing unless it redacted something\nor failed.\n\nPut values that must always go in `~/.config/claude-scrub/secrets`, one\n`label=value` per line. You can also point `--env-file` at a `.env`, shell or\nYAML file and it picks out the secrets in it.\n\nPut false positives in `~/.config/claude-scrub/allow`, one value per line.\n`label:NAME` skips a whole label.\n\nThere are four sources, from most to least trusted:\n\n1. Your secrets file and any `--env-file` .\n2. Values assigned to secret-looking names, such as `DB_PASSWORD=...` ,`\"apiKey\": \"...\"` or`--password=...` . The value has to look like a secret:\nat least 8 characters, a mix of character types, and not a path, date,\nplaceholder or piece of code.\n3. Passwords in URLs (`postgres://user:pass@host` ) and`Authorization: Bearer` or`Basic` headers.\n4. The [gitleaks](https://github.com/gitleaks/gitleaks) rules, which know about\n200+ token formats (AWS, GitHub, Slack, OpenAI, private keys and more).\n\nEvery value found anywhere gets redacted everywhere. If a password shows up\nonce as `DB_PASSWORD=...` and later on its own in a `psql` command, both go.\n\nTranscripts are JSON lines, and the same secret can sit in them as is, escaped once, or escaped twice when a tool returned JSON. claude-scrub looks for all three forms and only cuts where the cut can't break an escape sequence. Any line that would stop being valid JSON is left alone and reported.\n\nEvery other byte stays the same. Each file is written to a temporary file and renamed into place, keeping its mode and modification time. If a running session appends to a file while it's being rewritten, claude-scrub notices and tries again.\n\nA dry run over 1.3 GB of history takes about 5 seconds on an 18-core M5 Pro. Most lines contain none of the keywords any rule needs, so they're dropped before a regex runs. Very long lines, like a minified bundle a tool printed, are cut into windows around each keyword.\n\n- Detection is best effort. A password that never appears next to a\nsecret-looking name is missed unless it's in your secrets file. The same\ngoes for derived values, like a base64-encoded `id:key` .\n- `--explain` and`--peek` print text from your history. Everything found is\nredacted in it, but anything the detectors missed shows as is.\n- Redaction is permanent. There is no backup, since a backup would hold the secrets.", "url": "https://wpnews.pro/news/show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk", "canonical_source": "https://github.com/overflowy/claude-scrub", "published_at": "2026-10-29 16:01:16+00:00", "updated_at": "2026-10-29 16:17:06.727300+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents", "ai-safety"], "entities": ["Claude Code", "claude-scrub", "overflowy", "Go", "gitleaks", "GitHub", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk", "markdown": "https://wpnews.pro/news/show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk.md", "text": "https://wpnews.pro/news/show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk.txt", "jsonld": "https://wpnews.pro/news/show-hn-claude-scrub-redact-secrets-from-claude-code-history-on-disk.jsonld"}}