The incident that gave this post its title started with a routine command. I was exploring whether to integrate a different knowledge-graph tool into my Obsidian vault, and ran a grep across the whole vault to see how often certain strings showed up.
One of the regexes happened to match strings that looked like API keys. sk_live_, sk-proj-, postgres:// with embedded passwords, JWT tokens starting with eyJ. The grep was meant to be informational. Instead it returned hits.
The hits were in a three-month-old session archive from IvyBloom — a transcript of an AI debug session that a collaborator had run during a deploy issue. The fastest way to give the AI context had been to paste the entire .env file into the chat. The AI helped fix the deploy. The transcript was archived. The transcript still had the .env file in it. Three months later.
The Three-Layer Spread
The keys weren't just sitting in a transcript file. The transcript file lives in an Obsidian vault. The Obsidian vault is on Google Drive with version history. The Google Drive account is connected to NotebookLM, which indexes everything in the drive for AI search.
- Local layer. The keys were in a file on disk. Routine grep would find them.
- Sync layer.Google Drive's version history kept every revision of every file. Even if I'd deleted the .env paste the next day, the prior version would still be retrievable.
- Index layer.NotebookLM had crawled the drive and built a semantic search index. A natural-language query like "what was the production database password" could theoretically retrieve them.
None of these layers individually leaked the keys to the public internet. Together, they meant the keys were cloud-resident, retrievable in three different ways, and any access to my Google account would surface them.
The First Pass Wasn't Enough
The immediate response was the obvious one: rotate the keys that were exposed, redact the file. I rotated the OpenAI key, redacted the transcript, committed the redaction, moved on. A morning's work.
The next day, a second grep — with a slightly different regex — caught a Postgres connection URL the first pass had missed. Postgres URLs embed the password as part of thepostgres://user:pass@host/db format. My first regex had matched on sk_ and JWT prefixes, not on Postgres syntax. A whole credential plane had survived the first cleanup.
That was the bigger lesson. Credentials are independent planes.Rotating the OpenAI key doesn't rotate the Supabase service role key. The Supabase rotation doesn't touch the Postgres direct password. The Postgres rotation doesn't affect Stripe. Each credential plane has its own rotation path, its own blast radius, and its own pattern. A cleanup that only knows one pattern leaves the others sitting there.
The Pattern Source of Truth
Out of the second pass came a small file that's now load-bearing for the whole defense: a single source of truth listing every credential pattern I need to watch for. JWT (eyJ...), OpenAI (sk-proj-...), Stripe live keys (sk_live_...), AWS (AKIA...), GitHub tokens (ghp_...), PEM private key blocks, Postgres URLs with embedded passwords, generic 32+ character hex secrets.
Every defense layer reads from that file. Update the patterns once; every scanner picks up the change. Add a new vendor; add its pattern to the list and every defense knows about it on next run.
Four Enforcement Layers in 24 Hours
Because Day 4 had already taught me that memory is a wish, I didn't try to solve this with "remember to redact." I built four enforcement layers within 24 hours, each at a different point in the lifecycle:
- PostToolUse hook. Every time the AI writes a session archive (or any file containing a transcript), a hook scans the new content against the credential patterns. Hits get loud — a clear warning, the file path, and the offending lines. Mechanical, not best-effort.
- SessionStart monthly vault sweep. On the first session of each month, a script walks the entire Obsidian vault and re-scans every file against the patterns. Catches anything that landed before the hook was added, or anything the hook somehow missed.
- Pre-upload gate to NotebookLM.Any vault bundle bound for upload to a cloud AI tool runs through the pattern scan first. If the scan finds anything, the upload is blocked. The bundle stays in a staging directory until it's clean.
- Security Boundary in every project's AGENTS.md.A short section at the top of every project's agent constitution: do not paste real credentials into AI conversations, use placeholders, here's what the placeholder format looks like, here's why this matters.
The Hook Caught a Real One
The PostToolUse hook's first scan over historical archives — separate from the monthly vault sweep — caught a Postgres URL that the human-driven first-pass cleanup had missed. Not in the same file. A different transcript from a different month, also containing a leaked credential, also synced to cloud.
That was the moment the case study went from "rule candidate" to "PROVEN ACTIVE" in my SOP. A human had cleaned what a human could see. The hook found what the human had missed, the same week the hook was written. Layer 4 enforcement, exactly as predicted by Day 4's lesson.
Who This Bites
This isn't a JumpOnion-specific problem or an IvyBloom-specific problem. Anyone who:
- Uses an AI chat to debug deploy issues (and pastes config for context),
- Saves chat transcripts to a knowledge vault (for future reference),
- Syncs that vault to a cloud drive (for backup or sharing),
- Has connected any AI-search tool to that cloud drive
…is currently running the same three-layer pipeline I was. If any debug session ever included real credentials, those credentials are now in the same cloud-indexed chain. They won't leak publicly. They might be retrievable by anyone who compromises any of the cloud accounts involved. That's a meaningfully different attack surface from "the keys live in a .env file on my laptop."
Without SOP, With SOP
Rotate the visible key, redact the file you saw, move on. Miss the Postgres URL in a different transcript because the regex was tuned for OpenAI patterns. Miss the JWT in a third transcript because nobody scanned for JWTs. Months later, an account compromise traces back to a credential the redaction pass never touched.
Single pattern source of truth. PostToolUse hook every write. Monthly vault sweep. Pre-upload gate. Every credential plane covered. Hook catches the missed Postgres URL the same week it's installed. Future leaks become impossible to merge in.
The Real Lesson
Three pieces stack to the same conclusion as Day 4's memory lesson: the cure is hooks, not memory. A rule that says "don't paste credentials into AI chats" lives in human attention. Human attention fails under deadline pressure, exhaustion, or just a collaborator doing what felt like the fastest path. A hook that scans every write doesn't care about deadlines.
Your AI debug log isn't private. It syncs to your drive. It indexes into your AI search. It lives there forever. If you're going to use AI to help with anything that touches credentials, you have two choices: paste only placeholders, or build hooks that catch the moments you forget. Or both. Probably both.
That's the end of this run. Nine days, six lessons written down — all of them paid for in real production incidents over the prior weeks. None of them survived as wishes. The ones that survived were the ones that descended into hooks.
More to come — the projects keep producing material faster than the blog can keep up. If you want the methodology behind all of this packaged up, the pricing page lists what's available.