Playbooks / Leaked API key
Playbook
Leaked API key: what to do in the first hour
A key was committed to a public repo, pasted into a chat, shown in a screenshot or logged somewhere it shouldn't be. Treat it as compromised, even if you deleted it seconds later, and work through these steps now.
Is someone using it right now? If you see charges, requests or changes you didn't make, revoke the key immediately and accept the downtime. A broken checkout for twenty minutes costs less than an attacker with your production key. Then come back to step 2.
Why deleting it isn't enough
Bots scan public GitHub for keys within minutes of a push. Removing the commit, force-pushing or making the repo private doesn't help: the value may already be copied, and it stays in forks, clones, caches and CI logs. The only fix is to make the leaked key stop working. Everything else is cleanup.
The steps
- Find where it leaked. Check your repo, logs and chat for the value, and remove it. Don't paste the value anywhere new while you look, including into a search box or an AI chat.
- Create a new key. In the account that owns the leaked key, create a replacement with the same permissions, or narrower ones if you can. If an AI agent created or saw the old key, treat that copy as exposed too.
- Save it and deploy it everywhere. Store the new value safely, update every environment variable and CI secret that held the old one, and redeploy so running apps pick it up.
- Verify it works. Run a request or a deploy and check that nothing is failing. Keep this short: you're measuring minutes, not days.
- Revoke the leaked key now. Do this as soon as the new key works. Until the old one is revoked, anyone who has it can still use it. If several projects share the key, revoking it once covers them all.
- Check for misuse. Look through the provider's usage, audit and billing logs since the leak for anything you don't recognize: requests, new users, new keys, changed settings, unexpected spend.
- Find other copies. Search for the same value in old
.envfiles, CI secrets, deploy logs, error trackers, chat history and AI agent sessions. Remove what you find. - Close the gap. Turn on secret scanning and push protection for your repos, and move to restricted or scoped keys so the next leak can do less.
If the key was pushed to GitHub
A pushed key is a common leak. Revoke it first, then deal with the history.
Why deleting the commit isn't enough
- Clones and forks keep it. Anyone who pulled the repo has the commit. GitHub's docs say that if the commit exists in a fork, it stays accessible there, and GitHub can't give you the fork owners' contact details.
- Caches keep it. The same page says the data can remain reachable by its commit hash in cached views, and in pull requests, until GitHub Support removes them.
- Assume it was already copied. Someone may have taken the key before you noticed, so don't rely on being first.
So the key is compromised, whatever you do to the repo. GitHub's own guidance is that the first step is to revoke or rotate the secret, and that once you have, rewriting history may not be warranted. For an OpenAI key, see OpenAI. For Stripe, see Stripe. For AWS access keys, see AWS. If the provider isn't listed, find it here or use its own docs.
Secret scanning and push protection
GitHub's secret scanning runs automatically and for free on public repositories, and scans your entire Git history on all branches. For secrets from its partner providers, GitHub notifies the provider, which can then take action such as revoking the credential. GitHub tells the provider, which may revoke it, but revocation isn't guaranteed. Those detections are reported to the provider and aren't shown in your repository alerts, so you may never see one. Check the provider's console and email, and revoke the key yourself either way.
Push protection blocks a push that contains a supported secret before it reaches the repository. It protects you only for the patterns it supports, and anyone with write access can bypass it by giving a reason. Turn it on for your repositories, and treat it as a seatbelt, not as a fix for a key already pushed.
Remove it from history afterwards
Once the key is revoked, removing it from history is hygiene: it stops people copying a dead key into new places and keeps scanners quiet. GitHub's docs describe using git filter-repo with the --sensitive-data-removal option, and note that you must coordinate with everyone who cloned the repo. Pull request refs can only be cleaned up by GitHub Support. Do this after the new key works, never instead of revoking.
The order, in short
If the key isn't being abused yet: new key → deploy everywhere → verify → revoke, all within the hour if you can. If it is being abused: revoke first, then do the rest while the app is down. Either way, the leaked key should be dead within hours, never days.
Checking for misuse
- Payment providers: look for refunds, payouts, new connected accounts, changed bank details and webhook endpoints you didn't add.
- AI APIs: look for a spike in usage or spend, especially from models or regions you don't use.
- Cloud accounts: look for new users, new access keys, new instances in unfamiliar regions and changed permissions.
- Code hosting tokens: look for new deploy keys, webhooks, collaborators, workflow changes and pushes you didn't make.
- Databases: look through connection history for clients you don't recognize, and check for unexpected reads, changes or new users.
If you find fraudulent charges, contact the provider's support with the dates and what you've already revoked. Many providers have a process for this, and acting quickly helps. Provider-specific details are on each provider page, for example Stripe, AWS, OpenAI and GitHub.
Database passwords
A leaked connection string is the same emergency. Create a second database user with the same permissions, move every connection to it, verify, then drop the old user. If you reset the password in place instead, the old one stops working at once, so be ready to update every connection string immediately.
Write it down
Record when the key leaked, where, when you revoked it and what you found in the logs. If customer data might have been accessed, you may have obligations to tell users or regulators, and the timeline is what you'll need.
Prevent the next one
- Keep secrets out of code: environment variables in your host, never committed
.envfiles. Add.env*to.gitignorefrom day one. - Use restricted or scoped keys, and separate keys per project and environment, so one leak doesn't open everything.
- Set spend limits and usage alerts where the provider offers them.
- Don't hand raw keys to AI agents. Give them a reference and approve access yourself.
- Rotate on a schedule. A key that's rotated regularly is a key you know how to replace fast. See how to rotate an API key without downtime.
Do this in StackHangar
StackHangar runs this playbook against your own stack. Your vault already knows which account owns the key and every project and deploy target that holds it, so step 3 isn't a guessing game.
- On Solo, the new key is pushed to Vercel, GitHub or Netlify and redeployed from your browser. The value goes straight to the provider and never reaches our servers.
- Your agent can propose the playbook when it spots a leak. You approve with a passkey, and the agent never sees the new value.
- Leak response is never paywalled. Free covers 2 projects.