Skip to content

BYOK for OpenAI API: Secure Key Management with Local Proxy

Problem

When I was building a client application that uses the OpenAI API, I faced a fundamental security problem:

Error: API key exposed in client-side code

Actually, it wasn’t an error message - it was worse. My OpenAI API key was sitting in plain text in my frontend JavaScript bundle. Anyone who opened the browser dev tools could see it.

I tried several approaches to hide it:

  • Environment variables? Only work server-side
  • Obfuscation? Trivial to reverse
  • Backend proxy? That’s what I needed, but building one from scratch seemed overkill

The real problem: client-side applications cannot safely store API keys. Any key embedded in a browser app, mobile app, or desktop binary can be extracted.

What Happened?

I was building a chat application and made the classic mistake:

exposed-key.js
// DON'T DO THIS - Key is visible in browser dev tools!
const client = new OpenAI({
apiKey: "sk-proj-xxxxxxxxxxxx", // Exposed!
dangerouslyAllowBrowser: true // This should have been a red flag
});

When I checked the browser’s Network tab, I saw my full API key in the request headers. Then I realized:

  1. Browser bundles expose everything - Minification doesn’t hide strings
  2. Logs capture secrets - Server logs, proxy logs, analytics all see the key
  3. Third-party integrations leak - Any service handling requests sees your key
  4. Key rotation is painful - When compromised, every client needs updating

I needed a way to use OpenAI’s API without ever exposing the real key to clients.

Why This Matters

The attack surface for exposed API keys is huge:

+------------------+ +------------------+ +------------------+
| Your Client | | Your Server | | OpenAI API |
| (Exposed) |---->| (Logs) |---->| (Billed to YOU)|
| API Key: xxx | | API Key: xxx | | |
+------------------+ +------------------+ +------------------+
| |
v v
Browser Dev Tools Server Logs
Network Tab Error Messages
Source Maps Third-party Services

Every hop in this chain is a potential leak point. I needed to reduce this surface area dramatically.

The Solution: BYOK with Local Proxy

BYOK (Bring Your Own Key) with a local proxy flips the architecture:

+------------------+ +------------------+ +------------------+
| Your Client | | ai-menshen | | OpenAI API |
| (Safe) |---->| Proxy |---->| |
| Proxy Token | | Real Key HERE | | |
+------------------+ +------------------+ +------------------+
|
v
Only ONE place
stores real keys

Clients authenticate with a proxy token. The proxy knows the real OpenAI key and injects it into upstream requests. This means:

  1. Clients never see real keys - They only have proxy tokens
  2. Key rotation is centralized - Change once at the proxy
  3. Access control per client - Each team/user gets their own token
  4. Full audit trail - Know who made what request

I found ai-menshen, a local-first proxy designed exactly for this use case.

How I Set It Up

Step 1: Install ai-menshen

install.sh
# One-liner install (macOS/Linux)
curl -fsSL https://raw.githubusercontent.com/jiacai2050/ai-menshen/main/install.sh | sh
# Or via Go
go install github.com/jiacai2050/ai-menshen@latest

Step 2: Generate Configuration

setup.sh
mkdir -p ~/.config/ai-menshen
ai-menshen -gen-config > ~/.config/ai-menshen/config.toml

Step 3: Configure BYOK Security

Here’s the critical configuration that keeps keys safe:

config.toml
# BYOK Configuration - Real keys stay server-side only
[auth]
enable = true
token = "${PROXY_TOKEN}" # Use env var, never hardcode
[providers]
base_url = "https://api.openai.com/v1"
api_key = "${OPENAI_API_KEY}" # Real key, server-side only
[storage.sqlite]
path = "./data/ai-menshen.db"
[logging]
log_request_body = true
log_response_body = true

Notice the ${VAR} syntax - this pulls from environment variables, keeping secrets out of config files.

Step 4: Set Environment Variables

secrets.sh
# NEVER commit these to git
export PROXY_TOKEN="my-internal-proxy-token"
export OPENAI_API_KEY="sk-proj-your-real-key-here"

Step 5: Run the Proxy

run.sh
ai-menshen -config ~/.config/ai-menshen/config.toml
# Output:
# 2026/03/30 10:00:00 Starting ai-menshen on :8080
# 2026/03/30 10:00:00 Dashboard available at http://localhost:8080/

Client-Side: No Real Keys Exposed

Now the client code changes dramatically:

safe_client.py
from openai import OpenAI
# Client only knows the proxy token, NOT your OpenAI key
client = OpenAI(
base_url="http://localhost:8080", # Point to proxy
api_key="my-internal-proxy-token" # Proxy token, not OpenAI key!
)
# This request goes through proxy with real key injection
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)

Even if someone extracts this token, they only get access through your proxy - which you control.

Multiple Teams with Separate Tokens

I wanted different tokens for different teams so I could audit and revoke individually:

team-tokens.sh
# Team Alpha's proxy instance
PROXY_TOKEN="team-alpha-token" ai-menshen -config config.toml -listen :8081 &
# Team Beta's proxy instance
PROXY_TOKEN="team-beta-token" ai-menshen -config config.toml -listen :8082 &

Now when I check the audit logs, I know exactly which team made each request.

Key Rotation Without Client Changes

This is where BYOK shines. When a key is compromised:

rotate.sh
# Old key compromised? Rotate ONCE at the proxy
export OPENAI_API_KEY="sk-proj-NEW-KEY"
# Restart proxy - clients don't need to change anything
# Their tokens still work, but now use the new upstream key
systemctl restart ai-menshen

No client updates. No app store releases. No user notifications.

The Mistake I Almost Made

I almost ran ai-menshen without authentication enabled:

insecure-config.toml
[auth]
enable = false # DANGER: Anyone can use your proxy!
[providers]
api_key = "${OPENAI_API_KEY}"

This would have been worse than nothing - the proxy would act as an open relay for my OpenAI quota. Always enable auth:

secure-config.toml
[auth]
enable = true
token = "${PROXY_TOKEN}" # Required for all API requests
user = "admin" # Dashboard Basic Auth
password = "${DASHBOARD_PASSWORD}"

Architecture Diagram

Here’s how the complete BYOK architecture looks:

BYOK Security Architecture
============================
+-------------+ +------------------------+ +-------------+
| Client A | | | | |
| Token: AAA |---->| | | |
+-------------+ | | | |
| ai-menshen Proxy | | OpenAI API |
+-------------+ | --------------- |---->| |
| Client B | | Real API Key: | | Billed to |
| Token: BBB |---->| sk-proj-xxx | | YOUR Acct |
+-------------+ | (Server-side only) | | |
| | | |
+-------------+ | Audit Log: ✓ | +-------------+
| Admin |---->| Cache: ✓ |
| Basic Auth | | Dashboard: ✓ |
+-------------+ +------------------------+
Security Benefits:
- Clients only know proxy tokens
- Real keys never leave server
- Token revocation is instant
- Per-client audit trail
- Key rotation is centralized

Summary

BYOK with a local proxy is the secure way to use OpenAI’s API. Real keys stay server-side, clients use revocable proxy tokens, and you maintain full audit visibility - all with zero changes to how clients call the API.

The key insights from my implementation:

  1. Never embed API keys in client code - They will be extracted
  2. Use environment variables for secrets - Not config files
  3. Enable authentication - Don’t run an open proxy
  4. Separate tokens per client/team - Enables revocation and auditing
  5. Rotate keys at the proxy - Clients don’t need to change

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments