I Committed an API Key to a Public Repo. Here’s the Whole Remediation, Not the Sanitised Version.

Compliant by Design — Essay 01


Most security write-ups you read are from companies, filtered through legal and PR until nothing real survives. This one is from a solo founder, about my own mistake, with the actual commands. Because the gap between “we take security seriously” and what actually happens at 9pm when you find a live key in your git history is where all the useful lessons live.

What happened

I was mid-way through building a deploy gate for VeriShield — a scam-protection product for Australians over 50, which means it handles exactly the kind of user who can least afford my mistakes. While auditing the repo I found an Anthropic API key sitting in .env.example.

Not .env. That was gitignored, like a good citizen. The example file — the one that exists specifically to be committed — had a real key pasted into it. Past me, setting up the project, had copied the wrong file’s contents as the template.

If you’re building with AI APIs, pause and check your own .env.example right now. This failure mode is stupid, common, and invisible, because the file is supposed to be in the repo.

Why this matters more in a regulated product

For a hobby project, a leaked key is a billing problem. For a product handling personal information of Australians, it’s a different category. Under the Notifiable Data Breaches scheme, the question I had to be able to answer wasn’t “is the key revoked?” — it was “could this access have exposed personal information, and can I evidence that it didn’t?”

An API key to an LLM provider doesn’t directly expose my database. But the assessment discipline is the point: I treated it as a potential incident and worked it as one, because the version of me that shrugs at small incidents is the version that mishandles a real one.

The remediation, in order

  1. Revoke first, investigate second. The key was dead within minutes of discovery. Everything else can wait; a live credential can’t.
  2. Issue the new key into .env only — the gitignored file — and confirm the app picked it up in every environment.
  3. Fix the template. .env.example now contains ANTHROPIC_API_KEY=your-key-here. A placeholder, committed, like it should have been from day one.
  4. Handle the history. The dead key still exists in git history. I made a deliberate, documented decision: rather than rewrite history, I allowlisted the revoked key in .gitleaks.toml so scanning stays clean without hiding that the event happened. A revoked key in history is an artefact; a scanner that cries wolf forever is a process failure.
  5. Make the class of error impossible, not just this instance. Gitleaks now runs as part of the pipeline. The lesson isn’t “be more careful” — carefulness doesn’t scale. The lesson is that the control has to be automated or it doesn’t exist.

What I’d tell a team doing this properly

If this had been a company under the Privacy Act rather than a pre-launch solo product, the additional steps are: a written assessment of whether personal information was accessible (the NDB scheme’s “eligible data breach” test), a decision log even when the answer is no, and usage-log review on the provider side for the exposure window. I did the lightweight versions of all three — not because a regulator was watching, but because the product I’m building sells trust to people who’ve already been burned once.

The uncomfortable takeaway

The incident took under an hour to fix. The interesting part isn’t the fix — it’s that I only found it because I’d committed to a security sweep before shipping, and the sweep was a checklist, not a vibe. Solo founders skip this because there’s no one to be embarrassed in front of. Build for the version of events where there is.


Compliant by Design is a fortnightly series on building AI products under Australian regulation — Privacy Act, APPs, NDB, health data — written from inside the work, not above it.