Open source, for GitLab and Google Cloud

Leaked Google keys, retired and proven dead.

When a Google API key leaks into a commit, VibeGuardian moves your app onto a new key, deletes the leaked one, and asks Google from five places until every one of them refuses it. Your app keeps serving the whole time.

Run a live drill: watch Google judge a deleted key
Incident · GitLab pipeline 2959096158

Leaked Google API key sha256:ebd854fa6ca5

demo/app/main.py:31 · flagged by GitLab secret detection

PROVEN DEAD
Retired in 23.4 s
  1. 0.1 sLeaked key found in the project
  2. 1.7 sHeld in Secret Manager, used by Cloud Run vg-demo
  3. 5.3 sReplacement made, same restrictions
  4. 10.7 sGoogle accepts the replacement
  5. 20.9 svg-demo healthy on the new key
  6. 23.4 sLeaked key deleted
Proven dead · 60 of 60 refusals
  • CI runnerGitLab12/12
  • Belgiumeurope-west112/12
  • Tokyoasia-northeast112/12
  • Sydneyaustralia-southeast112/12
  • São Paulosouthamerica-east112/12

Drill 10, 17 Oct 2026. The leaked key was deleted 76 s after the push and proven dead 191 s after it, with no flaps.

Built on GitLab CI/CD GitLab Duo Google Cloud Run Secret Manager Workload Identity Federation OpenTelemetry
Why prove it

Deleting a key is a request, not a result.

Google's servers cache key checks, so a deleted key can keep working for a while, and how long depends on where you ask. In this drill the CI runner saw 12 refusals in a row. Europe then answered with real data. VibeGuardian waits until every place that answers has refused the key 12 times in a row.

Drill 11 · one deleted key, every probe, from five places refusedHTTP 200: the deleted key worked
Drill 11: probes of a deleted key from five places. The CI runner saw 12 refusals in a row by 96.2 s after deletion; Europe answered HTTP 200 at 78.5 s and 111.4 s; proven dead from all five at 177.9 s.Runner alone: dead at 96.2 sProven dead from all five: 177.9 sCI runnerGitLabBelgiumeurope-west1HTTP 200 · 78.5 sHTTP 200 · 111.4 sSão Paulosouthamerica-east1HTTP 200 · 39.2 sTokyoasia-northeast1Sydneyaustralia-southeast140 s60 s80 s100 s120 s140 s160 s180 s
Drill 11: probes of a deleted key from five places. Europe answered HTTP 200 at 78.5 s and 111.4 s after deletion, after the CI runner alone had called it dead at 96.2 s.Runner alone: 96.2 sAll five: 177.9 sCI runnerGitLabBelgiumeurope-west178.5 s111.4 sSão Paulosouthamerica-east139.2 sTokyoasia-northeast1Sydneyaustralia-southeast1

Seconds after the key was deleted. Every dot is one probe from the raw log, 17 Oct 2026.

Don't take the chart on trust. Run a live drill: VibeGuardian makes a throwaway Gemini key, gets Gemini 4 Argon to answer with it, deletes it, and asks again from five places until each one refuses it. About three minutes, real Google, and a link to the proof. In 20 drills in a row, a deleted key still got an answer in 16.

Run a live drill
How it works

One pipeline answers the leak.

Separate GitLab jobs, each with its own narrow Google identity and no stored key. The job that retires a key cannot reach the prover, so it never grades its own work. Google's answer is the only verdict.

Retire

Your app never notices.

VibeGuardian finds where the key lives, makes a replacement with the same restrictions, rolls each service onto it and checks it serves the new key. Only then is the leaked key deleted.

  • Everything rolls back if a step fails; the old key stays
  • Classic AIza keys and Google's newer AQ. keys
Drill 5 · the demo app, one request a second

315 of 315 OK

Traffic moved to the replacement 4 s before the leaked key was deleted.

Drill 5: 315 of 315 requests OK while the leaked key was retired; traffic moved to the replacement before the leaked key was deleted.leaked key deletedleaked keyreplacement
Decide

A policy a push can't loosen.

A small file says what may happen without a person. CI reads it from the default branch, so the commit that leaks a key can't change it. Anything bigger waits for someone to approve it in GitLab.

  • Protected services always wait for a person, who is named in the incident
  • An unrestricted key's replacement is limited to the APIs it really called
.vibeguardian.toml
[retire]
auto = true
max_services = 3
protected = []

[replacement]
limit_to_used_apis = true
used_days = 30
Who used it

Google's own counts, by region.

Per-key request counts for the minutes the key was public. Calls from regions where your services don't run are flagged, refused ones too.

Drill 13

5 calls

USED ELSEWHERE
  • us-central1 · your app0
  • asia-southeast15, all refused

Refused, but only someone holding the key could make them.

Write-up and fix

An incident and a merge request.

A GitLab Duo flow writes the incident from the job records, with fingerprints only, and opens the merge request that removes the key. A person merges it.

Drill 10, opened by the flow
#7
Leaked Google API key sha256:ebd854fa6ca5 retired and proven dead
securityvibeguardian::incident
!7
Remove leaked Google API key sha256:ebd854fa6ca5 from code
395 s after the push
Monitor

Every incident is a trace.

Each retire step and the proof go to GitLab Observability as spans, exposure as metrics, and each verdict as a log.

Drill 10 as a trace Drill 10 as a trace: leak incident 23.7 s, with a span per retire step.leak incident23.7 sfind consumerscreate replacementGoogle accepts itnew secret versionroll Cloud Rundelete leaked key
Before it leaks

Catch the key on the laptop, too.

Most leaks start on someone's machine. Two small tools catch a key there, so the pipeline has less to do.

Pre-commit hook

A commit with a key in it doesn't happen.

Only the lines being committed are read. Google keys in both formats are stopped, and so are OpenAI, Anthropic, Hugging Face and Perplexity keys, with where to revoke each.

$ git commit -m "Call Gemini"
VibeGuardian: this commit adds an API key, so it was stopped. Nothing was committed.
  app.py:3  Google API key  sha256:2031a1295e43
Keep keys out of the code: read them from an environment variable or a secret manager.
If a Google key was ever pushed or shared, retire it: vg retire --key-env <VAR> --project <project>
To commit anyway, for a value you know is not a real key: git commit --no-verify
Desktop · preview

Copy a key, see if it's live.

A small Windows tray app spots a Google key on your clipboard, asks Google whether it works, and can retire it for you. It shows a fingerprint, never the key.

The tray app showing a copied key as LIVE, with a Retire it button The same key after retiring: DEAD in 75 s
Measured

Every number comes from a real key.

Each figure is from a drill on live Google Cloud keys, with its raw log in the repository. Read the measurements

76 s

From push to leaked key deleted, with the app healthy throughout. Drill 10

315/315

Requests served OK while the leaked key was retired. Drill 5

60/60

Refusals from five places, 191 s after the push. Drill 10

111 s

After deletion, Europe still answered a deleted key with HTTP 200. Drill 11

59.7 min

A stolen service-account token kept working after its key was disabled. Disabling the account stopped it in 11.7 s.

16/20

Live drills where a call sent after deletion still got an answer, the latest 92.8 s after. All 20 then proven dead. Live drills

GitLab

All nine DevOps stages.

Plan

Every leak becomes an incident issue, updated as evidence arrives.

Create

A pre-commit hook stops keys; the fixer opens the merge request that removes one.

Verify

Unit tests, an identity check for each CI identity, and a gate that stays red while a key is in the code.

Package

The prover's image is built in CI and pushed to GitLab's container registry.

Secure

Secret detection, container scanning, and redaction before the agent sees the code.

Release

The prover is deployed to four regions as a GitLab environment.

Configure

Keyless Workload Identity Federation, with one narrow identity per job.

Monitor

Who used the key, and every incident as a trace in GitLab Observability.

Govern

The policy file, the approval gate, and the approver on the record.

FAQ

Does VibeGuardian ever see my key?

Only to look it up and test it. Its value never reaches a log, an issue, a merge request, an artifact or the agent's context: everything names a key by its fingerprint, sha256: plus 12 hex characters. Keys reach the vg command through environment variables, so they stay out of shell history and job logs.

What if the new key breaks my app?

The replacement is rolled out and health-checked before the old key is deleted. If any step fails, VibeGuardian rolls everything back, leaves the old key alone, and the incident says a person is needed. One early drill did exactly that, and users saw 924 of 924 requests succeed.

Do I have to store a Google key in GitLab?

No. Each job trades its GitLab ID token for a short-lived Google token through Workload Identity Federation, and each job gets its own narrow identity: the one that retires keys can't prove, and the one that proves can't change anything.

Why ask from five places?

Because a deleted key doesn't stop working everywhere at once. In drill 11, a prover asking only from the CI runner would have called the key dead at 96 s; Europe answered with real data at 111 s. The probes run from the runner and from small private Cloud Run services in Belgium, Tokyo, Sydney and São Paulo.

Can I watch it work myself?

Yes. The live drill runs the proof on a fresh key while you watch: a throwaway Gemini key answers once, is deleted, and is then asked again from Iowa, Belgium, Tokyo, Sydney and São Paulo until every place refuses it 12 rounds in a row, with Gemini 4 Argon as the judge. Every drill keeps its own link, and every one is counted.

Which keys does it handle?

Google API keys in both formats: classic AIza keys and the newer AQ. authorization keys that Google AI Studio issues. Leaked service-account key files are retired from the command line with vg retire-sa. The pre-commit hook also stops OpenAI, Anthropic, Hugging Face and Perplexity keys and links to where each is revoked.

Does it work with GitHub?

The code is on GitHub, and the command line and the pre-commit hook work in any repository. The automatic pipeline runs on GitLab CI today; a GitHub Actions version is planned.

What does it cost?

VibeGuardian is free and open source under the MIT license. It runs in your own Google Cloud project; the provers are four small Cloud Run services that scale to zero when idle.

Your next leaked key, proven dead in about three minutes.

Set it up in your own Google Cloud project and GitLab project. The README walks you through it in six steps.