API key rotation in 90 minutesA quarterly discipline, not a project.
That OpenAI key you generated in 2024 is still active. It has seen three freelance contracts, two laptops, a Replit session you forgot about, and at least one .env file that ended up in a public repo. Ninety minutes, once a quarter, gets your blast radius under control.
-
01
State
Key from 2024 still active
Seen by 3 contracts, 2 laptops, 1 forgotten Replit session
blast radius: unknown -
02
Method
90-min quarterly rotation
List, rotate, verify, revoke, document in one session
once per quarter -
03
Result
Recent-leak surface only
Any undetected compromise is less than 90 days old
blast radius: bounded
TL;DR· the answer, in twenty seconds
What: Most solo developers have API keys they generated once and never touched again. If one of those keys leaked, they would not know until a bill arrived or a service notified them. Keys in use for years accumulate surface area with every tool, config file, and laptop that ever held them.
Fix: Block out 90 minutes every quarter. List every key, rotate them one at a time, verify your stack still works, then revoke the old keys and update your password manager. The process below has a specific time budget for each phase.
Lesson: Rotation is not about paranoia. It is about shortening the window between a leak and the moment you catch it. Quarterly rotation means any undetected leak is at most 90 days old.
You generated an API key in 2024, dropped it in .env, and have not thought about it since. That key has probably been in a Git repo, in a Vercel environment, on two laptops, maybe in a Claude Code session whose settings.local.json you never checked. Knostic found env vars in about 1-in-13 npm packages they scanned in February 2026. GitGuardian's 2026 State of Secrets Sprawl reports AI-assisted commits leak at roughly twice the baseline rate.
The key may be fine. You have no way to know. That is the problem.
Rotation is the answer most developers skip because it sounds like enterprise overhead. It is not. Done quarterly, in one focused session, it takes 90 minutes and requires no tooling beyond what you already have.
The short version
- A key that has never been rotated has accumulated blast radius since the day you created it.
- Quarterly rotation means any undetected leak is at most 90 days old when you catch it.
- The 90-minute budget covers listing, rotating, verifying, revoking, and documenting.
- Per-vendor rotation takes 3-5 minutes per key if you know where to find the dashboard.
- The one step most people skip: revoking the old key after confirming the new one works.
The 90-minute breakdown
This is a calendar block, not a project. Schedule it once a quarter, same day every quarter. March, June, September, December work well because they align with statement periods and most billing cycles.
Phase 1: list every key (10 minutes)
Before you rotate anything, you need a complete list. Open your password manager and search for "key", "token", "secret", and "sk-". Open your shell config (~/.zshrc, ~/.bashrc, ~/.bash_profile) and grep for exports:
grep -E 'export .*(KEY|TOKEN|SECRET|PASSWORD)=' ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null
Check your CI environments. GitHub Actions secrets live at github.com/<org>/<repo>/settings/secrets/actions. Vercel project env vars live at vercel.com/<team>/<project>/settings/environment-variables. Fly.io secrets: fly secrets list -a <app-name>.
Write down every key you find. Provider, name, where it lives (local .env, GitHub, Vercel, Fly, Render, Railway), and when you last touched it. This list is your rotation checklist for the session.
Common candidates for solo stacks:
- OpenAI (
sk-...) - Anthropic (
sk-ant-...) - Stripe publishable and secret keys
- GitHub personal access token or fine-grained token
- AWS access key ID + secret
- Vercel deploy token
- Fly.io deploy token
Phase 2: rotate one at a time (30 minutes)
Pick the first key on your list. Generate a new one in the provider dashboard. Update every place the old one lives before you do anything else. Then move to the next key.
Do not generate all new keys first and update later. You will lose track of which new key belongs where.
For each key:
- Generate the new key in the provider dashboard.
- Copy the new key. Most dashboards only show it once.
- Update your local
.envfile. - Update your CI environment (GitHub Actions, Vercel, Fly, etc.).
- Update your password manager entry with the new key.
- Note the old key value. You will need it to confirm which one to revoke.
- Do not revoke yet. Verification comes next.
If a key lives in more than one place (local .env and Vercel and GitHub Actions), update all three before moving to the next key. Partial updates cause outages.
Phase 3: verify everything still works (20 minutes)
Deploy a smoke test or run your test suite against staging. For most solo projects, a few manual checks cover it:
# OpenAI: test a completion
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'
# Stripe: list customers (zero results is fine)
curl https://api.stripe.com/v1/customers \
-u "$STRIPE_SECRET_KEY:"
# GitHub: check the token's scopes
curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user
Run your app locally with the new keys exported. Click through the critical paths: auth, payment, the AI feature. If something fails, the old key is still valid for another 10 minutes while you troubleshoot. Do not revoke until you see green.
Phase 4: revoke old keys and document (20 minutes)
Return to each provider dashboard. Revoke the old key by name or last-four. If you noted the old key values in phase 2, matching them to the dashboard entry takes under a minute per key.
After revoking, update your password manager. Add a note with today's date and the next rotation date (three months out). Some password managers let you set an expiration alert. Use it.
Write two lines in a private note or KEYS.md inside a .gitignored folder:
2026-04-15: rotated OpenAI, Anthropic, Stripe secret, GitHub PAT, AWS
Next rotation: 2026-07-15
That is the audit trail that lets you answer "when did I last rotate this?" without digging through dashboards.
Phase 5: buffer (10 minutes)
One key will break something. Not because you made an error but because you forgot you passed it to a webhook, a Zapier step, a local LLM proxy, or a tool that cached it. Budget the ten minutes. It will get used.
Per-vendor quick notes
OpenAI. Dashboard at platform.openai.com/api-keys. Name your keys so the old one is obvious when you come back to revoke it. Project-scoped keys (available since late 2024) limit blast radius by default. If you are still using organization-level keys, migrate to project keys during this rotation.
Anthropic. Console at console.anthropic.com. Keys live under your account settings. Anthropic does not currently show last-used timestamps in the dashboard, so you cannot tell from the UI whether a key has been used since you created it. Rotate on schedule regardless.
Stripe. Dashboard at dashboard.stripe.com/apikeys. Stripe distinguishes test and live keys. Rotate both. The live secret key (sk_live_...) is the one that costs you money if it leaks. Stripe's restricted keys let you scope permissions (e.g., read-only for a reporting tool). If you are using a full-access secret key for a use case that only needs read access, create a restricted key during this rotation.
GitHub. Personal access tokens at github.com/settings/tokens. Fine-grained tokens (PATs scoped to specific repos and permissions) are available and better than classic tokens for most uses. If you are still on a classic PAT with broad scopes, convert during this rotation. GitHub sends an email when a token is about to expire, which is useful if you set an expiration date.
AWS. IAM console at console.aws.amazon.com/iam. Rotate via aws iam create-access-key and aws iam delete-access-key. The AWS CLI config file (~/.aws/credentials) holds the key ID and secret. Update it. Also check CodeBuild, Lambda environment variables, and any ~/.aws/credentials profiles you created for specific projects.
Make it sustainable
The main reason developers skip rotation is the startup cost. Finding all the keys, remembering where they live, and updating a dozen places feels like a day of work the first time. It is not. The first rotation takes longer than 90 minutes. Every one after that does not, because you have the list.
Two things that cut the friction between rotations:
A GitHub Action that runs quarterly gives you a warning before the deadline:
name: Key age check
on:
schedule:
- cron: '0 9 1 */3 *' # first day of each quarter, 9am UTC
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Remind
run: echo "Quarterly key rotation due. Run the rotation checklist."
This does not rotate the keys. It triggers an email via the GitHub Actions notification system. Replace the echo with a curl to a webhook for Slack or Discord instead.
On any Linux or macOS system with sendmail or a mail relay configured, a crontab entry does the same thing without GitHub:
# crontab -e
0 9 1 1,4,7,10 * echo "Quarterly API key rotation due" | mail -s "Key rotation reminder" you@example.com
The reminder runs on the first day of January, April, July, and October.
What the conventional advice gets wrong
Most rotation guides tell you to rotate "regularly." That is not advice. Every 30 days produces key fatigue: broken deploys, half-updated configs, developers who start skipping the step entirely. Every 180 days is long enough that a key leaked on day two sits active for nearly six months.
Ninety days threads the needle for solo work. GitGuardian's 2026 State of Secrets Sprawl puts AI-assisted commits at roughly twice the baseline leak rate, and attackers move fast after a discovery. A calendar quarter gives you a bounded worst-case. Not zero risk. Bounded.
One more thing the guides understate: rotation does not help if you keep the old key active. That step 4 above, revoking the old key, is where the security comes from. New key plus active old key gives attackers two targets instead of one.
Quarterly rotation checklist
## API key rotation - [DATE]
### Phase 1: inventory (10 min)
- [ ] Grep shell configs for exported secrets
- [ ] List all CI environment secrets (GitHub, Vercel, Fly, Render)
- [ ] Check password manager for API key entries
- [ ] List: provider, key name, where it lives, last rotation date
### Phase 2: rotate (30 min)
- [ ] OpenAI: new key generated, all envs updated, old key noted
- [ ] Anthropic: new key generated, all envs updated, old key noted
- [ ] Stripe (test + live): new keys generated, all envs updated
- [ ] GitHub PAT: new token generated, all envs updated
- [ ] AWS: new access key, CLI config updated, old key noted
- [ ] [Other keys from inventory]
### Phase 3: verify (20 min)
- [ ] Smoke test against staging or prod
- [ ] Critical paths tested manually (auth, payments, AI features)
- [ ] No errors in application logs
### Phase 4: revoke and document (20 min)
- [ ] Old keys revoked in each provider dashboard
- [ ] Password manager entries updated with new keys and rotation date
- [ ] Rotation log updated (date + next rotation date)
### Phase 5: buffer (10 min)
- [ ] Checked webhooks, Zapier, and any third-party integrations
- [ ] Confirmed no cached keys in local LLM proxies or agent tool configs
What this means for your stack
Rotation bounds the window. It does not eliminate the surface. A key that never leaves your local machine and your CI environment has a smaller blast radius than one that has passed through six tools, two coding agents, a debug log, and a shared .env file. The goal is to shrink the number of places any given key has ever lived.
For AI coding agent users, the rotation urgency is higher than most guides acknowledge. Knostic's February 2026 research found env vars captured in Claude Code's settings.local.json shipped into npm packages. Any agent session run before Anthropic patched that behavior in late February 2026 may have captured keys you were using at the time. If you ran agent sessions before that patch, those sessions' state files may still contain keys you have not rotated. The 90-minute process above handles them.
Past rotation, the next layer is to stop treating keys as durable credentials that sit in ambient shell exports for months. Keys work better when they are scoped and injected into a specific process at exec time, not globally available to every tool that reads your environment. hasp is one working implementation of that model. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, and the next agent session receives a reference instead of a raw key value. Source-available (FCL-1.0), local-first, macOS and Linux, no account required.
Rotation is still worth doing even with a broker in place. Ninety days means your worst-case exposure window is bounded. That is the discipline. The tooling is secondary.
Sources· cited above, in one place
Stop handing the agent your real keys.
hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.
- Local, encrypted vault — no account, no cloud, no telemetry by default.
- Brokered run — agent gets a reference, the child process gets the value.
- Pre-commit + pre-push hooks catch managed values before they ship.
- Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.