Why the same API key that shipped your prototype becomes a critical bug in production
Obfuscating an API key harder doesn't work because any shipped binary can be decompiled; the real fix is moving the key off the device entirely.
The instinct, when you realize your API key is sitting in your app's source code, is to hide it better: scramble the string, split it into pieces, obscure it somehow. None of that actually works, and understanding why points at the real fix.
The same key, a flipped risk
A bundled API key works perfectly fine during development. It's only once you ship the app that it becomes a vulnerability, and the key itself hasn't changed at all, only the environment around it. A shipped binary can always be decompiled, string and all, no matter how cleverly that string was scrambled beforehand. Obfuscation raises the effort required to extract the key. It doesn't remove the key from the binary, and removal is the only thing that actually matters here.
Dev code versus release code
Here's the concrete version of the problem. During development, an app calls the Claude API directly, with something like .apiKey("sk-ant-...") sitting right there in the source. Before release, that line has to change to something like .proxied(headers:[...]), pointed at a small relay server run by your own company. That relay server sits between the app and the API, injecting the real key on the server side rather than shipping it inside the app at all.
Why the threat model changes
The reason this matters comes down to who's holding the device. On your own development machine, the key just needs to work; nobody hostile has access to that environment. On a device someone else holds, the key needs to survive them taking the binary apart, and no amount of scrambling changes that outcome. Only removing the key from the device entirely does. That's the actual shift between prototype and production: not the key itself, but whether an untrusted party can physically hold the thing the key lives inside.
One boundary, not everywhere
On release, the fix is that the app never holds the real key at all. It sends a request token instead, and the relay server injects the actual key server-side before forwarding the request on to Claude. Importantly, this boundary isn't needed everywhere. A server you fully control, one no end user ever touches directly, can keep using a bundled key directly without this relay layer, because nobody outside your control ever gets to hold that binary apart. The moment a user could hold the binary, though, the key has to move off the device.
The real fix
A key you can decompile isn't actually hidden, it's just harder to read at that moment, and "harder to read" is not a security boundary, it's a delay. The real fix isn't finding a better hiding place inside the app. It's moving the key off the device entirely, into a relay server that only your company controls.
Key takeaways
- A bundled API key that works fine in development becomes a vulnerability the instant the app ships, because a shipped binary can always be decompiled.
- Obfuscating or scrambling the key string doesn't remove the risk, it only adds friction to extracting it.
- The fix is a backend relay: the app sends a request token instead of the real key, and a server your company controls injects the actual key server-side.
- This relay boundary is only necessary once an untrusted user can hold the binary; a server you fully control can keep using a bundled key directly.
- The underlying shift from prototype to production is about who can physically access the code, not about the key itself.
Try it yourself
Try this prompt with Claude: "I'm building an app that calls the Claude API directly with a bundled key, and I need to ship it. Walk me through the backend relay pattern I need: what my app should send instead of the key, what the minimum relay server needs to do, and how it keeps the real key safe even if someone decompiles my app." Run that against your own app before you ship it. This explainer is part of Humanitarians AI's Claude Basics series.
Chapters
Full transcript(auto-generated, with timestamps)
How do I hide my API key better?
[0:00]Someone asks how to hide their API key better before shipping. Wrong fix, you don't hide it, you move it. Why the same key that shipped your prototype becomes a bug in production. A bundled key works perfectly in
Same key, flipped risk
[0:11]Development and turns into a shipping vulnerability the moment you release the app. The instinct is to hide it harder, scramble the string, split it in pieces. None of that holds. A shipped binary can always be decompiled string and all, however well it was scrambled. Here's
The anchor: dev code vs. release code
[0:25]The concrete case. In development, the app calls Cloud directly, API key SKA right there in the source. Before release, that line has to change to proxied headers and a small relay server at your company. Calm sits in between injecting the real key on the server side. Here's why the threat model
Why: same lock, different holder
[0:42]Changed. On your own machine, the key just needs to work. On a device someone else holds, the key needs to survive them taking the binary apart and no amount of scrambling changes that, only removing the key from the device does.
The anchor returns: one boundary, not everywhere
[0:55]So, on release, the app never holds the real key. It sends a request token and the relay injects the actual key server side before forwarding to Cloud. That boundary isn't needed everywhere. A server you fully control that no user ever touches can keep using the bundled key directly. The moment a user could hold the binary, the key has to move. A
Carry-out
[1:13]Key you can decompile isn't hidden, it's just harder to read. The real fix isn't a better hiding place, it's moving the key off the device entirely.
Your turn
[1:22]Your turn. Here's the prompt, read it with me. I'm building an app that calls the Cloud API directly with a bundled key and I need to ship it. Walk me through the back end relay pattern I need, what my app should send instead of the key, what the minimum relay server needs to do, and how it keeps the real key safe even if someone decompiles my app. Lay 'em in for bear. Why the same API key that shipped your prototype becomes a critical bug in
Outro
[1:45]Production. Lay 'em in for bear.
More from HAI
2:03Why web search never runs your code but your own tool always does — with identical syntax
4:10When AI Agents Disagree: Part 1 — The Mechanics of Cross-Agent Validation in Mycroft
1:55Computer Use: Demo to Production.
4:44When AI Agents Disagree: Part 2 — Building the Code in Mycroft
2:13The model says "I have no preferences" while assigning 74% to survival.
9:48