Honesty note up top. This is a real scan of a real codebase I own — Ignaria, a SaaS I built and have run for most of the last year. I ran my own assessment practice's pipeline on it. The numbers below are the actual scanner output, not a hypothetical. Secret values are redacted everywhere (you never publish live secrets, even your own). File paths and finding types are real, because they're mine to show. The four "rotate this week" credentials were live in HEAD at scan time; I rotated them before publishing this.
The setup
Ignaria is a monorepo. One repo, 46 feature branches, 6,890 commits of history, backend and frontend packages, a Phoenix app, a Postgres database, and a Meilisearch search index. Real infrastructure, not a demo.
I'm a security consultant who sells a focused thing: taking the noisy wall of findings a secret scanner produces and turning it into a short, human-judged list an engineering leader can actually act on. So I pointed my own tooling at my own code. This is what came back.
The wall
289 raw findings.
That's what the scanner handed me: 202 pattern matches plus 87 credential candidates, across the full git history of all 46 branches. If you've ever opened a secret-scanning dashboard — GitHub's, GitGuardian's, anyone's — this is the moment where it dies. 289 rows. You scroll, your eyes glaze, you close the tab. The secret debt stays.
This is the problem I'm in business to solve: the scan is raw material, the triage is the value. So let me show you the triage.
The three-line answer
After dedup, after liveness verification, after filtering placeholders and history, those 289 rows became four actions:
Rotate this week — four real credentials, currently in HEAD, pointing at real infrastructure:
- Meilisearch API key —
packages/backend/index_meilisearch.py:27, hardcoded next to a URL to a public server. The same key shows up again in finding #3. - Database password —
packages/backend/index_meilisearch.py:22, hardcoded in a backend config dict. - Postgres URL with credentials + Meilisearch key —
packages/backend/reingest_reader.sh:6, apostgresql://user:[REDACTED]@[redacted-ip]:5432/...plus a--meilisearch-key, committed in a shell script against a real host. - Phoenix
SECRET_KEY_BASE—docker-compose.supabase.yml:130, a high-entropy framework secret (entropy 5.16) hardcoded in a compose file.
Rotate today — zero. Nothing verified live. I'll come back to that, because "zero verified live" is doing a lot of work in that sentence and it's where most scanners would mislead you.
Log and ignore — 90. Not actionable. I'll show you what's in there, because "ignore" is a judgment call, not a wave of the hand.
The part most scanners get quietly wrong
The four above are marked unverified, not "live." My verification engine
made read-only identity calls against each credential's provider API, and all
four came back false.
Here's the trap: a lot of tooling would report "0 live secrets found" and an
engineering director would exhale and close the dashboard. That's the wrong
read. "Unverified" means the verification engine couldn't confirm liveness —
revoked, unreachable, or (in this case) an unsupported provider. Meilisearch
and Phoenix SECRET_KEY_BASE aren't things you can ping with a standard
identity call. So the engine honestly says "I don't know," and the right
response is treat as live until a human confirms otherwise, not "ignore."
This is the gap I sell into. The scanner did its job. Nobody did the judgment. Those four credentials sat in HEAD, against real infrastructure, until a person (me, in this case — it's my repo) went and looked.
What got filtered, and why
90 findings dropped into "log and ignore." Breaking that down is the unsexy half of the triage — and it's the half that makes the short list trustworthy.
- 79 historical — credentials added and later removed across 6,890 commits and 46 branches. Already rotated or superseded. Not in current HEAD. Genuinely not actionable, but they're why the raw count looks scary: every secret you ever rotated and scrubbed still haunts your git history in the scanner's eyes.
- 11 current placeholders / test / false positives — example JWTs in
docs and
.env.example,changemelocal-dev Postgres strings, documentedcurlauth headers in deployment notes, exampleOPENAI_API_KEYvalues in setup guides, and one scanner false positive (a metric label,signups_since_…, matched as a key).
The breakdown:
| Type | Count | Why filtered |
|---|---|---|
| JWT | 28 | Example/test tokens in documentation and .env.example. |
| Generic API key | 19 | Example values in docs/.env.*.example; 1 scanner false positive. |
| Postgres | 19 | Example/local-dev connection strings (changeme / service-name host). |
| curl auth header | 17 | Documented example headers in deployment notes. |
| OpenAI | 6 | Example OPENAI_API_KEY values in setup guides. |
That's 90 rows an engineering director would have to open one by one to realize none of them matter. Nobody does. So either the real ones get missed inside the noise, or the team gives up and treats all 289 as "probably fine." Both outcomes are how secret debt ages.
What I did about it
It's my repo, so the "shared session" was me talking to myself: I rotated the
Meilisearch key, the database password, the Postgres URL credentials, and the
Phoenix SECRET_KEY_BASE; moved each into environment variables / a secret
store; and scrubbed the four from git history. The point of this piece isn't
the cleanup — it's that the cleanup took minutes once the three-line answer
existed, and it had never existed before because nobody's job was to produce
it.
What this is, and isn't
This isn't a demo on a fake repo. It's the real deliverable run on real code I own, with the real numbers — including the honest "zero verified live" that a less honest writeup would have buried, and the four unverified-but-real credentials that the "zero" would have let me ignore.
It's also not a sales pitch that your repos are full of secrets. Maybe they're clean. Two other repos I scanned with this same pipeline came back with zero real findings — that's a valid, realistic outcome. The value isn't in finding debt; it's in knowing, either way, with a list you can act on instead of a dashboard you avoid opening.