Skip to content
SSShipSafeStack

Checklist

How to keep API keys out of AI-generated code

Short answer. Give the agent names of environment variables, not the secret values. Publishable keys may ship in the client when the vendor says so. Secret keys, service-role keys, and webhook signing secrets stay on the server. If a value landed in a chat or a commit, rotate it before you redeploy.

Updated 21 Oct 2026

This is a practical checklist, not a guarantee. It is not a penetration test, a compliance audit, or proof that an app is safe to launch.

The frontend checklist is how to search the bundle after the app is built. This page is how to keep the secret out of the code an agent writes. They overlap. Do both.

Hand the agent a name, not a value

Agents inline a key because the request fails without it. Put the value in the host's secret store, or in a local file that git and the agent ignore, and tell the agent the variable name.

Cursor's help center says .env files are ignored by default, and that .cursorignore blocks Agent from extra paths you list. It also says a terminal command or an MCP tool can still read an ignored file. Lovable's secrets docs say VITE_ values belong in .env because they are public build-time values, and that API keys for Stripe, Resend, or a model belong in Secrets, which Edge Functions read. Bolt's secrets docs say the same shape: server functions read the secret, and the value should not be plain text in the code.

Do not paste a live key into the chat to "make it work." The transcript is another copy.

Sort the key before the agent touches it

KeyVendor's instructionWhere it may live
Stripe pk_Publishable. Safe in frontend code. It cannot create charges or read account data.Client
Stripe sk_ or rk_Not safe to expose. Stripe says a restricted key is the one to give an agent, with only the permissions that job needs.Server environment
Stripe whsec_Webhook signing secret. Verify the raw body with it. It is separate from the API key.Server environment
Supabase publishable or legacy anonSafe in the browser only with Row Level Security and least-privilege grants.Client
Supabase secret or service_roleBypasses Row Level Security. Never in the client.Server environment
Firebase API key restricted to Firebase APIsPublic by design. Security Rules authorize the data.Client config
A model, email, or database URL with a passwordTreat as secret unless that vendor's docs call it publishable.Server environment

Stripe's key docs also say not to put sensitive keys in source or in files you commit, and not to share them in chat or email. Restricted keys are drop-in replacements for secret keys on the server. An unrestricted secret key is the one Stripe says not to start new work with.

Stop the file from being written

  • Add .env, .env.*, *.pem, and downloaded credential files to .gitignore before the first agent run.
  • In Cursor, add the same patterns to .cursorignore if they are not already covered. The setup is in how to secure a Cursor-built app.
  • In Lovable, reject a VITE_ name for a secret. Their product blocks that prefix in the Secrets UI and tells you to use .env only for browser values. See how to secure a Lovable app.
  • In Bolt, add the value under the database Secrets tab when the agent asks, instead of into a source file. See how to secure a Bolt.new app.
  • On the host, mark the variable as a secret where the host has that switch. Vercel, Netlify, and Render all document a place for this. A NEXT_PUBLIC_ or VITE_ prefix still ships the value to the browser on those hosts.

Aikido's free plan lists secrets detection in the IDE, in CI, and in git, including pre-commit blocking. That is a scanner, not a substitute for the ignore file. The product comparison is Snyk vs Socket vs Aikido.

If the secret is already in the code

Rotate it at the vendor first. Assume the old value was copied the moment it was on screen or in git. Then delete it from the working tree, from any committed .env, and from chat logs you control. A new deploy that still contains the old line publishes the replacement.

Search the built JavaScript as well as the source. The steps are the frontend checklist. Supabase's dashboard can issue a new secret key. Stripe's dashboard can roll a key and a webhook secret. Do the rotation in the dashboard, and put only the new value in the server environment.

FAQ

Can I give Cursor my Stripe secret key so it can test payments?

Give it a restricted test key, in an environment variable, with only the permissions that test needs. Stripe's restricted-key docs say those keys are the ones to hand an AI agent, and that an unrestricted secret key can do anything in the account. Do not put either key in a file the agent will commit.

Is the Supabase anon key safe in generated client code?

Supabase says the publishable key, and the older anon key, may be in the browser when Row Level Security and grants are set. The secret key and the service-role key bypass those policies. Generated apps often copy the wrong one because both are on the same settings screen. The setup is row level security for an AI-built app.

What if the agent encoded the key so it is not a plain string?

That is still the secret, split or encoded. Stripe's guidance is to keep sensitive keys out of source. Decoding a string in the client does not make it a server secret. Move the call to a server route and delete the encoded value.

Does ignoring .env fix a key that was already committed?

No. Ignore rules stop a later add. They do not remove a commit that already contains the value, and they do not rotate it. Rotate at the vendor, then remove the value from the tree you deploy.

Sources

Checked 21 Oct 2026.

Newsletter

A short weekly note on new tools and security checklists. No spam, unsubscribe anytime.