Vibecode Beratung Engineering review for AI-built software

Field notes AI code security

Exposed API keys in AI-built apps: find and rotate them

How to tell a public key from a leaked secret in a Lovable, Bolt or Cursor app, how to rotate a Supabase secret key safely, and how to stop the next leak.

AI code security 4 min read

At a glanceRotating a leaked Supabase secret key without breaking your app
  1. 1Fix the root cause firstFind out how the key leaked and close that path. A new key that leaks the same way buys you nothing.
  2. 2Create a new secret keyCreate it in Settings > API Keys in the Supabase Dashboard.
  3. 3Replace the key everywhereUpdate every server, Edge Function and job that used the old key. A legacy service_role key becomes a new secret key.
  4. 4Confirm nothing uses the old keyCheck every component before the next step, because deleting a secret key cannot be undone.
  5. 5Retire the compromised keyDelete the old secret key, or deactivate the legacy keys, which is reversible if you missed a client.

Graphic: Vibecode Beratung · Based on the sources below

Your AI coding tool needed a key to make a feature work, so it put one in the code. The feature works. The open question is which key it was, and who else can now read it.

In apps built with Lovable, Bolt, v0, Cursor or Claude Code, this question has a precise answer. Some keys are meant to be public. Others give full access to your data and must never leave your server. This article shows how to tell them apart, what to do if a secret key has leaked, and how to make the next leak less likely.

Public key or leaked secret: the first check

If your app uses Supabase as its backend, it has two kinds of keys. The Supabase API keys documentation frames the choice by where the code runs:

  • Publishable key (sb_publishable_..., legacy name anon): for anything you ship, such as a web page or mobile app. Anyone can read it, so it only reaches what row level security allows.
  • Secret key (sb_secret_..., legacy name service_role): for servers, Edge Functions and cron jobs. It bypasses row level security completely and has full access to your project’s data.

So a publishable key in your JavaScript bundle is expected. A secret key there is a SEV-1 finding. One detail helps when you search: the legacy keys are long JWTs that begin with eyJ. Both the legacy anon key and the legacy service_role key look like that, so any eyJ string in client code needs a closer look. Supabase also notes that if a tool, tutorial or AI assistant tells you to copy a long eyJ key, it was written for the legacy system.

Search the built bundle, not only the source files, and look at the requests in the browser’s Network inspector. Supabase lists both as ways anyone can retrieve a key from a public component.

Imagine a SaaS founder whose admin dashboard could not load all users. The AI assistant fixed it by switching the client to the service_role key. The page now works, and every visitor’s browser holds a key that ignores every policy. Our article on Supabase row level security in Lovable and Bolt apps explains why that bypass matters so much.

Why leaks are a volume problem, not a carelessness problem

GitHub published data on this in October 2026. In Secret protection must scale with software, it reports that one in three pull requests on GitHub now involves an AI agent, up from fewer than one in ten a year earlier. A new secret appears in publicly visible code about once every two seconds.

Between Q2 2024 and Q2 2026, screened pushes grew 2.84 times and pushes carrying credentials grew 2.59 times. GitHub found no detectable trend in the share of pushes that contain a secret. In other words, developers did not become more careless. They simply produce more code, so the absolute number of leaks grows with it.

The slow part is the cleanup. GitHub reports that the mean time to manually revoke a leaked secret is around 40 days, and roughly one in five took more than 90 days. A key stays usable during all of that time.

SEV-1: rotate a leaked secret key

If a secret or service_role key has reached a browser, a public repository or a shared document, treat it as exposed. Removing the line from your code is not enough. Supabase’s procedure starts with a step that is easy to skip under pressure: fix the root cause of the leak before you rotate anything. Then create a new key, replace it everywhere, confirm that every component uses the new key, and only then retire the old one.

The order matters. Deleting a new-style secret key cannot be undone, so a missed server would simply stop working. For legacy keys, Supabase uses deactivation instead, which is reversible. Supabase also points out that a secret key returns HTTP 401 in a browser, but an attacker can still use it from other tools. That browser block is not protection after a leak.

SEV-2: make the next leak less likely

  • Keep secret keys out of source control, including CI scripts. Use the platform’s own secrets storage.
  • Use one secret key per backend component. If one leaks, you rotate only that one.
  • Never send keys over chat, email or in a URL, and never log them. Supabase suggests logging at most 6 characters of the random part.
  • Turn on push protection. According to GitHub’s secret detection post, push protection scans changes before a push is accepted, while post-push scanning creates alerts after a commit. GitHub says push protection stops about 30% of newly detected secrets before they enter repository history. The other 70% are found after they are already exposed.

GitHub also notes that generic secrets, such as an internal database password, often have no recognizable prefix. Pattern matching alone misses or misjudges them, which is why GitHub now adds a context-aware classifier. For you, this means a clean scan is a good signal, not a guarantee.

What this means for your project

Start with the bundle. If you find only publishable or legacy anon keys, your risk moves to row level security, which you can check against the production readiness checklist for AI-built apps. If you find a secret or service_role key, treat it as SEV-1: fix the cause, rotate in the documented order, and check what that key could have reached in the meantime.

The harder part is judging what an AI tool may have wired up across servers, functions and third-party APIs. That is part of a production readiness review.

Not sure which keys your app ships to the browser? Book a free intro call and we will tell you honestly whether a review of your bundle and backend makes sense.

Frequently asked questions

Is it a problem that my Supabase key is visible in the browser?

Not if it is the publishable key or the legacy anon key. Supabase designs these to be public, and they only reach what row level security allows. A secret key or a service_role key in browser code is a different matter and a launch blocker.

How do I recognize a Supabase secret key in my code?

New secret keys start with sb_secret_, and publishable keys start with sb_publishable_. Legacy keys are long JWTs that begin with eyJ, and both the anon and the service_role key look like that, so any eyJ string in client code needs a closer look.

Is deleting the key from my code enough after a leak?

No. Once a key has been pushed or shipped, treat it as exposed and rotate it. Supabase recommends fixing the root cause, creating a new key, replacing it everywhere, confirming that, and only then retiring the old key.

Can GitHub stop secrets before they reach my repository?

Push protection scans changes before a push is accepted and blocks recognizable credentials. According to GitHub, it stops about 30% of newly detected secrets before they enter repository history, so it reduces leaks but does not replace a review.

How long do leaked keys usually stay valid?

GitHub reports that the mean time to manually revoke a leaked secret is around 40 days, and roughly one in five took more than 90 days. During that time the key can still be used by anyone who found it.

Share this article

Related field notes

  1. Can a user make themselves admin? Role columns in Supabase AI code security · 4 min
  2. Supabase now needs explicit grants on new tables: what to do AI code security · 4 min
  3. An MCP server for your Supabase app: security checks first AI code security · 4 min

Intro call

Book a free intro call

30 minutes, free of charge. Tell us about your app or your team, and we will tell you honestly whether and how we can help. We reply within one business day.

  • You describe the app or the team setup.
  • We ask about the stack, the data and what is at stake.
  • You get an honest view of whether a review, a rescue or a rollout makes sense.
No newsletter. Nothing is passed on.

We use your details only to answer your request. More in the privacy policy.