Replit Secrets and the private key that ended up in your bundle
Asymmetric private key material. For a Firebase or Google service account, this is full admin access to the project.
Why Replit does this
Replit Secrets are injected as environment variables at build time. A secret that gets a `VITE_` prefix so the frontend can "see" it stops being a secret — the prefix is the mechanism that copies it into the bundle.
Confirm it first
Before rotating anything, check whether the key is actually being served. Paste your deployed URL — KeyDrift downloads the same JavaScript a visitor gets and tells you what is in it.
Rotate the key
Do this before changing any code. The key has been served to browsers, cached by CDNs and very likely scraped already — removing it from the source does not un-publish it.
- 1Revoke the credential with its provider. Rotation is the only fix — the key is in the browser cache, in CDN edge nodes, and in whatever scraped the page.
- 2Review the account for activity you did not initiate.
- 3Move the call that needed it to a server route, so the browser never receives the replacement.
Move the call to a server
The replacement key must not follow the old one into the bundle, which means the code that uses it cannot live in the browser. Any variable named VITE_… is inlined at build time by design — the prefix is the mechanism, not a mistake.
Before — shipped to the browser
// src/components/Chat.tsx
// Vite substitutes the literal value here at build time.
const key = import.meta.env.VITE_API_KEY;
const result = await callTheApi(key, body);After — stays on the Express server in the same Repl
// supabase/functions/proxy/index.ts — runs on the Express server in the same Repl
Deno.serve(async (request) => {
const key = Deno.env.get('API_KEY')!; // never sent to the browser
const result = await callTheApi(key, body);
return Response.json(result);
});
// src/components/Chat.tsx
const result = await fetch('/functions/v1/proxy', { method: 'POST' }).then((r) => r.json());How KeyDrift detects it
Matches a PEM private key header. When it appears inside a service-account JSON structure it is relabelled accordingly, because a Google service account key is full administrative access rather than just key material.
It will happen again
Replit Secrets are injected as environment variables at build time. That has not changed because you fixed this one file — the next feature request produces the same shape of code. Continuous monitoring re-scans every deploy and tells you the moment a key comes back.