
Find credentials in screenshots, save them to your secret manager, and irreversibly redact them from the image — local, offline, OCR-based.
Find credentials in screenshots, extract them to a secret manager, and
irreversibly redact them from the images — so secrets don't rot in your
notes, screenshots, and ~/Desktop.
Local. Offline. Silent. No network, no telemetry by default.
screenscrub bridges two existing tool categories: GUI redactors (Xnapper, Shhshot, macshot) blur PII by hand but can't tell a secret from an address or capture what they hide; secret scanners (gitleaks, TruffleHog) find real credentials but only in text, never in a PNG. screenscrub brings gitleaks-grade detection — plus a red-team pack and entropy analysis — to pixels, headlessly: it captures each secret to your manager, irreversibly redacts the image, and re-OCRs the result to prove the secret is gone.
A safety net, not a guarantee — screenscrub never claims an image is "guaranteed clean"; OCR and detection both miss things, so always eyeball the results. Read this before you rely on it.
screenscrub scan ~/Screenshots # dry run: detect + report, no writes
screenscrub clean ~/Screenshots --out ~/clean \ # extract, redact, verify, quarantine
--sink file://./secrets.age --yes
screenscrub verify ~/clean # prove a folder is clean (exit≠0 if not)
screenscrub clean --keep-originals ./tmp
Cleaning 7 image(s) in ./tmp (7 worker(s))…
[1/7] tmp/ssh.jpg — 6 region(s) → tmp/ssh.redacted.jpg ✓ verified
[2/7] tmp/key.jpg — 13 region(s) → tmp/key.redacted.jpg ✓ verified
[3/7] tmp/test2.jpg — 12 region(s) → tmp/test2.redacted.jpg ✓ verified
[4/7] tmp/test5.jpg — 13 region(s) → tmp/test5.redacted.jpg ✓ verified
[5/7] tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
[6/7] tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
[7/7] tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
7/7 file(s) flagged, 382 region(s) total, 3 file(s) errored/unverified.
By kind:
high-entropy 343
keyword-secret 15
db-connection-uri 10
github-pat 6
unix-crypt-hash 6
aws-access-key 1
stripe-key 1
Needs manual attention (3):
tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
| Before — secret values visible | After clean — irreversibly redacted |
![]() |
![]() |
The same screenshot before and after clean: every secret value is decoded to pixels, boxed, and re-encoded to a new image (irreversible) — labels and structure stay readable.
Requires the tesseract OCR binary on PATH:
brew install tesseractwinget install UB-Mannheim.TesseractOCR (or the UB Mannheim installer)apt-get install tesseract-ocrThen build:
go build -o screenscrub ./cmd/screenscrub
Cross-compiles to macOS (arm64/amd64) and Windows (amd64) — it shells out to tesseract rather than using cgo. Only tested on macOS.
scan <dir|file> — dry runDetects and reports; writes nothing.
screenscrub scan ~/Screenshots --ignore 'node_modules/**' --ignore '*.thumb.png'
screenscrub scan shot.png --json # machine-readable report
screenscrub scan ~/Screenshots --preview ~/review # eyeball what clean would redact
--preview <dir> writes a non-destructive copy of each flagged image with every
detected region outlined (not filled), so you can confirm what clean would
redact before committing. ⚠️ These copies still contain the secrets — the dir
is created 0700 and must not be shared.
The report ends with a rollup: a per-kind breakdown and an explicit "Needs manual attention" list — files that errored, failed verification, or produced no OCR text on what should be a screenshot (a silent OCR miss reads identically to a clean image, so it's surfaced rather than trusted).
scan --fail-on-findings exits non-zero when anything is detected — a pre-commit/CI gate.
clean <dir|file> — extract, redact, quarantineSCREENSCRUB_PASSPHRASE=… screenscrub clean ~/Screenshots \
--out ~/Screenshots/clean \
--sink file://./secrets.age \
--yes
For each image with findings, clean:
<name>.redacted.png to --out (or beside the source),<out>/quarantine/ encrypted with age.Secrets go to whichever sink you pass to --sink: age file (default), 1Password
(op://), KeePassXC (keepassxc://), macOS Keychain (keychain://), or
HashiCorp Vault (vault://) — or hardware-backed age recipients (YubiKey,
Touch ID). See Secret sinks.
Flags:
| Flag | Meaning |
|---|---|
--out <dir> | where redacted copies go (default: beside the source) |
--sink <uri> | file://./secrets.age · op://Vault · keepassxc://./v.kdbx · keychain://service · vault://KV |
--recipient <age1…> | encrypt to an age recipient (repeatable): X25519, YubiKey, or Touch ID |
--recipients-file <f> | file of age recipients, one per line |
--passphrase | passphrase for the file sink / quarantine (prefer the env var or --config) |
--watch | keep running; process new screenshots as they land (fsnotify) |
--purge | shred originals instead of quarantining (see caveat below) |
--keep-originals | write redacted copies only; leave originals in place (no quarantine, no passphrase, no --yes) — mutually exclusive with --purge |
--yes | confirm destructive original handling (required) |
--config <file> | JSON config; lets you keep the passphrase out of argv |
--no-verify | skip the re-OCR verification of redacted output (not recommended) |
-i, --interactive | open each flagged image and confirm redact/skip before touching it |
--llm | use Claude to drop benign over-flags; sends candidate strings to the Anthropic API |
--llm-api-key | Anthropic API key for --llm (or ANTHROPIC_API_KEY) |
--jobs N | process N images in parallel (default: CPU count) |
--manifest <file> | write a JSON map of original→redacted→regions |
--ocr-raw / --ocr-upscale N | disable dark-terminal preprocessing / upscale before OCR |
--exclude-kind <kind> | suppress findings of a kind (repeatable), e.g. high-entropy — offline precision knob |
--min-confidence <0..1> | drop findings below this confidence — offline precision knob |
--json | emit the report as JSON |
--ignore <glob> | ignore glob, repeatable; ** matches any depth |
--lang | tesseract language (default eng) |