A provider key is a spending limit and a data feed. Guard it like both.
Last lesson covered keys in general. ML service keys deserve their own
lesson because the blast radius is different: whoever holds your
Anthropic or OpenAI key can spend your budget at API rates and
send prompts as you — and bots scrape GitHub for sk- prefixes
around the clock. A leaked provider key gets exercised in minutes,
not weeks. The bill arrives later.
One key per environment
The single-key setup — same ANTHROPIC_API_KEY on your laptop, in
staging, and in prod — fails the moment anything goes wrong. Issue
three keys instead:
- dev — lives in your local
.env, low spend limit. If it leaks, you rotate it over coffee and prod never notices. - staging — lives in the deploy platform's secret store.
- prod — lives only in the platform's secret store. It has
never touched a laptop, a notebook, or a
.envfile. Nobody can paste what they've never seen.
Separate keys also mean separate usage graphs. When spend spikes, the dashboard tells you which environment leaked — that's your incident triage done before it starts.
The leak nobody greps for: your own logs
Everyone knows not to commit keys. The sneakier failure is the inference log. Claude adds "helpful" debugging to your service and you get:
DEBUG headers={'authorization': 'Bearer sk-ant-prd-2481...'}
ERROR retry with key sk-ant-prd-2481... failed
Those lines flow into a log aggregator that half the company can search and that keeps data long after the deploy is gone. The key is now stored in plaintext, outside every secret store, indexed. Rules:
- Log request IDs, latency, token counts. Never headers, never client config, never the key.
- If you must confirm a key loaded, log a redacted prefix:
key[:7] + "...". Enough to see which key, useless to a thief. - Scan logs for key prefixes (
sk-ant-,sk-) the same way you scan commits. It's a substring check — ten lines of Python.
Rotation, the short version
Found a key in a log or a commit? Revoke first, investigate
second. Provider dashboard → revoke old key → issue new → update
the secret store → redeploy → audit usage between leak and
revocation. Because your code reads os.environ and the value lives
in one place per environment, the whole dance is ten minutes. That
convenience is the actual payoff of everything in this chapter.
Run the editor. Three environments, three keys, and only redacted prefixes ever reach stdout.