Bots harvest keys pushed to GitHub within minutes. Deleting the commit does not undo that. This is the calm runbook: kill the key at the provider, check what it was used for, then clean up your history.
Jump to the rotation table → Check what else is exposed (free)Most advice online gets the order backwards. It walks you through rewriting git history, force-pushing, and hoping nobody saw. That is theater.
Here is the reality. Automated scrapers watch public GitHub commits and copy anything that looks like a credential, usually within minutes of the push. Once a key is copied, no amount of history editing takes it back. GitHub's own docs on removing sensitive data say the same thing twice over: revoking the secret is the first step, and a rewritten history can still be read in clones, forks, cached views, and old pull requests.
So the key has to die at the provider first. That is the only action that actually removes the risk. Scrubbing history is hygiene. Do it second, and do it calmly.
The four steps, in the order that matters.
.env and your deploy environment. Never commit it..env and friends to .gitignore, and check whether other keys are sitting in the same file or the same old commits. If one leaked, its neighbors usually did too.Secret scanning runs automatically and for free on public repositories, across your whole git history. When it finds a key format from a partner provider (Anthropic, Stripe, AWS, and Slack are on the partner list), GitHub reports it to that provider so they can act, which may include revoking it. Separately, push protection is on by default for your account when pushing to public repos, and blocks pushes containing known key formats.
Helpful, but do not rely on it. Not every provider is a partner, not every key format is recognized, and the scrapers race the defenders. Assume the key was copied and rotate anyway.
The email is not a false alarm. It is a machine telling you exactly one thing: your key was found somewhere public.
If it came from OpenAI: OpenAI scans the public internet and published app bundles for its own key format. When it finds your key, it disables it immediately and emails you. The key is already dead. Your job is to find where it leaked, then create a fresh one.
If it came from Anthropic, Stripe, or another provider: GitHub's secret scanning reports partner-format keys found in public repos straight to the provider. So the provider can know about your leak before you do, even if you never noticed the push. (Anthropic key specifically? We wrote a dedicated page for that.)
Where did it leak? Three usual suspects, in order of likelihood:
git log -S "sk-" --all --oneline (swap in your key's prefix). If any .env file ever appears in history, every key that was ever in it needs rotating.NEXT_PUBLIC_ or VITE_ ship to the browser by design. Open your live site, view the page source and the built JavaScript, and search for your key's prefix. If it is there, anyone who visited your site has it..env, .git, or backup files to anyone who asks. Our free scan checks for exactly this kind of exposure, passively and with no signup.Find the source before you mint the new key. Otherwise the new key takes the same road out.
Every major provider keeps usage logs. Here is where to look, and what to look for.
Open the usage page. Look at the last 7 days for spikes, and for models you do not use. Then open the limits page and lower your spending caps if they are loose.
Open the usage page in the console. Same drill: check the last 7 days for volume you did not generate.
Check payments for charges or refunds you do not recognize, then the API request logs, which show the IP behind each request. Unfamiliar IPs after the leak date are your answer.
Open the Logs section of your project dashboard and look for queries against your tables that your app did not make. A leaked service role key bypasses your row-level security rules, so take this one seriously.
Search CloudTrail for recent calls by the leaked key, and check billing for new charges. Look for compute instances, users, or buckets you did not create. Stolen AWS keys are routinely used to mine cryptocurrency at your expense.
Review your security log for authentication events you do not recognize. If the token could write to repos, skim recent commit history for commits that are not yours.
The honest part about refunds: sometimes a provider will forgive fraudulent usage from a first-time leak, and sometimes they will not. None of them promise it. It is always worth asking support, politely, with a short factual timeline: when the key leaked, when you rotated it, and which usage is not yours. Set spending caps where the provider offers them, so the next mistake has a ceiling.
The exact console page and the 2-3 steps that matter, per provider. In every case, finish by confirming the old key fails (a test call should be rejected with an authentication error, typically a 401) and by updating the key everywhere it lives: local .env, CI secrets, and your deployed environment.
| Provider | Console page | Steps |
|---|---|---|
| OpenAI | platform.openai.com/api-keys | Revoke the leaked key. Create a new secret key (shown once, copy it now). Redeploy with the new key. |
| Anthropic | console.anthropic.com/settings/keys | Delete the leaked key. Create a new key and copy it. Update ANTHROPIC_API_KEY everywhere and redeploy. |
| Stripe | dashboard.stripe.com/apikeys | Roll the live secret key. Choose immediate expiry for the old one unless automated systems need a grace window. Copy the new key (shown once). |
| AWS | console.aws.amazon.com/iam | Find the IAM user's key and mark it inactive right away. Create a replacement key. After confirming nothing broke and no damage was done, delete the old key. |
| GitHub | github.com/settings/tokens | Delete the leaked token. Generate a new one with the same scopes. Search your code and CI config for anywhere the old token lingers. |
| aistudio.google.com/app/apikey or console.cloud.google.com/apis/credentials | Delete the leaked key and create a new one (AI Studio for AIza... keys, Cloud Console for GCP keys). Restrict the new key to specific domains, IPs, and APIs. |
|
| Supabase | supabase.com/dashboard → project → Settings → API | On new-style keys (sb_secret_...): create a replacement secret key, switch your backend to it, then delete the leaked one. On legacy keys: regenerate the JWT secret, which invalidates the anon and service role keys together, so update both. Keep secret and service role keys server-side only. |
This same runbook ships as a guided walkthrough (/lictor-rotate) inside our free, open-source Claude Code plugin. Install it with:
/plugin marketplace add Raffa-jarrl/Lictor-AI
/plugin install lictor-security-suite@lictor-ai
A leaked key is rarely alone. The same habits that put one key in a public commit tend to leave a .env file reachable on the live site, or a second key in the frontend bundle.
So finish with a check. Our free scanner looks at your live site passively and needs no signup. For your codebase, the plugin above runs a 48-check audit catalog covering leaked keys, exposed configs, and the rest of what gets apps breached, in plain English.
And because "trust us" is not evidence: we publish a reproducible benchmark. In a blind run on a 30-case stratified sample of OWASP Benchmark 1.2, the audit scored an F1 of 93.8%. That is a 30-case, injection-focused sample, not the full benchmark, and we say so right in the results.
Building with AI tools? Our security checklist for Lovable and other AI app builders covers the leaks these tools tend to create.
Today you found out about the leak from a bot, a provider email, or a bill. That is the worst possible alarm system.
Lictor's continuous monitoring watches your exposed surface for you, from $49/mo, so the next leak is a notification while it is still small, not a surprise on the invoice. It is run by the same person who wrote this runbook: a security engineer with 20 years in the field who does real responsible-disclosure work.
Either, as long as the old key stops working. Some consoles offer a roll or regenerate button (Stripe), others make you delete the key and create a new one (OpenAI, Anthropic). The button's name does not matter. What matters: the leaked key is dead, the new key is deployed everywhere, and a test call with the old key returns a 401 error.
No. Bots copy keys from public commits within minutes, and GitHub's own docs say rewritten history can still be read in clones, forks, cached views, and old pull requests. Rotate the key first. Scrub history second, as cleanup.
OpenAI scans the public internet and published app bundles for its key format, disables any key it finds, and emails you. The email means your key really was public somewhere: a repo, a frontend bundle, or a misconfigured site. Find the source, then create a fresh key.
Check the provider's usage logs: OpenAI's usage page, Anthropic's console usage page, Stripe's payments and API request logs (which include IPs), Supabase's project logs, and AWS CloudTrail. Look for spikes after the leak date, unfamiliar IPs, and resources you did not create.