> ## Documentation Index
> Fetch the complete documentation index at: https://docs.iriscode.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Git History Secrets Scan: Find Credentials Your History Still Remembers

> Deleting a key from a file doesn't delete it from your history. This finds the ones still sitting in old commits.

A key gets committed, someone notices, the line is deleted and the fix is pushed. The working tree is clean, but the key is still in the commit that introduced it, available to anyone who clones the repository.

The history secrets scan finds those. It requires **Pro**, and it runs the same [two-layer detection](/features/hardcoded-secrets) used on your working tree, applied to past commits.

## How it works

Every file version each commit *introduced* goes through the secrets engine, including committed `.env` files. Unchanged files aren't re-read, so a long history doesn't take forever.

* **One finding per secret.** The same key across ten commits reports once, with the first and last time it appeared, how many commits are involved, and which files.
* **Always masked.** You get `sk_live_****`. The real value never leaves the analysis engine, and never reaches a panel, a log or a JSON export.
* **Still there, or gone?** Each finding says whether the secret is *still in HEAD*, which you can click straight to, or historical only.
* **Merge commits count.** Content introduced by a merge itself, like a conflict resolution, gets scanned too.
* **Entirely local.** It shells out to your own `git`. No code, no history and no findings leave the machine.

## Running it

**In your editor:** Workspace tab → **Security Posture** → **Scan git history for secrets**, or **Iris Code: Scan Git History for Secrets** from the command palette. Results open in their own panel with filters for still-in-HEAD versus historical, and search.

**In a terminal:**

```bash theme={null}
iris secrets --history              # last 50 commits
iris secrets --history --depth 500  # last 500
iris secrets --history --depth 0    # everything
iris secrets --history --format json --output history-scan.json
```

Exits `1` if it finds anything in range, so you can gate on it.

## It always tells you what it didn't check

A scan that covers less than it appears to is worse than no scan, because the result gets trusted. Every limit is reported alongside the findings:

| Limit           | Default    | What it says                                                              |
| --------------- | ---------- | ------------------------------------------------------------------------- |
| Depth           | 50 commits | "history continues beyond depth 50 - rerun with `--depth 0`"              |
| File size       | 1 MiB      | Counted in the skipped-blobs total                                        |
| Total scan size | 50 MiB     | "depth reduced to N of M commits (size cap)", keeping the newest          |
| Shallow clone   | -          | "this is a shallow clone - run `git fetch --unshallow` for full coverage" |

So "no secrets found" never stands in for "no secrets found in the portion that was scanned".

## Acting on a finding

**Rotate the credential.** This applies even to historical-only findings, where the secret has long since been removed from the working tree.

Anyone who has ever cloned the repository holds that commit locally, as does any fork, CI system or backup that touched it. There is no way to establish who read it, so the value has to be treated as compromised.

Removing it from history as well requires a rewrite with `git filter-repo`, after which everyone re-clones. That is worth doing for a public repository, but it does not replace rotation: by the time a rewrite happens, the value has already been exposed.
