KeyDrift
Free scan
criticalBolt.new·Anthropic API key

Your Bolt.new app is serving its anthropic api key to every visitor

Billable API access to the Claude API.

Why Bolt does this

Bolt scaffolds Vite projects inside a WebContainer, where everything runs in the browser by definition. Code that works in the preview keeps the key client-side when it is deployed, because nothing in the generated project ever moved it to a server.

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.

No account. Read-only — the scanner only ever issues GET requests, and never stores a key: findings carry a masked prefix and a fingerprint.

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.

  1. 1Revoke the key in the Anthropic console.
  2. 2Review usage for the billing period.
  3. 3Put the API call behind a server route with your own rate limiting.
Open the revocation page

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_ANTHROPIC_API_KEY;
const result = await new Anthropic({ apiKey: key }).messages.create(body);

After — stays on a serverless function on your deploy target

// supabase/functions/chat/index.ts  — runs on a serverless function on your deploy target
Deno.serve(async (request) => {
  const key = Deno.env.get('ANTHROPIC_API_KEY')!; // never sent to the browser
  const result = await new Anthropic({ apiKey: key }).messages.create(body);
  return Response.json(result);
});

// src/components/Chat.tsx
const result = await fetch('/functions/v1/chat', { method: 'POST' }).then((r) => r.json());

How KeyDrift detects it

Matches the `sk-ant-api` and `sk-ant-admin` prefixes with their version segment.

It will happen again

Bolt scaffolds Vite projects inside a WebContainer, where everything runs in the browser by definition. 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.