docs: stop advertising sk-1234 as the master key in shipped configs and examples

Shipped proxy configs now read general_settings.master_key from
os.environ/LITELLM_MASTER_KEY, the .env examples ship a blank value with
the openssl generate command above it, and READMEs, the missing env vars
page and Admin UI code snippets show a generate command or the
<your-master-key> placeholder instead of the literal sk-1234

The two CircleCI docker runs that mount proxy_server_config.yaml and
oai_misc_config.yaml now pass LITELLM_MASTER_KEY so their runtime key is
unchanged
This commit is contained in:
ryan-crabbe-berri 2026-09-19 12:59:48 -07:00
parent b946d12ffd
commit c8e0f2ddb4
34 changed files with 81 additions and 42 deletions

View file

@ -1745,6 +1745,7 @@ jobs:
docker run -d \
-p 4000:4000 \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e USE_PRISMA_MIGRATE=True \
-e FAKE_OPENAI_API_BASE=http://host.docker.internal:8190 \
-e AZURE_API_KEY=$AZURE_API_KEY \
@ -1840,6 +1841,7 @@ jobs:
docker run -d \
-p 4000:4000 \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/circle_test \
-e LITELLM_MASTER_KEY="sk-1234" \
-e AZURE_API_KEY=$AZURE_API_KEY \
-e AZURE_API_BASE=$AZURE_API_BASE \
-e AZURE_API_VERSION="2024-05-01-preview" \

View file

@ -26,6 +26,7 @@ NOVITA_API_KEY = ""
INFINITY_API_KEY = ""
# Development Configs
LITELLM_MASTER_KEY = "sk-1234"
# Generate one with: echo "LITELLM_MASTER_KEY=sk-$(openssl rand -hex 32)"
LITELLM_MASTER_KEY = ""
DATABASE_URL = "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
STORE_MODEL_IN_DB = "True"

View file

@ -268,10 +268,13 @@ If you want to build the Docker image yourself:
# Build using the non-root Dockerfile
docker build -f docker/Dockerfile.non_root -t litellm_dev .
# Generate a master key. Requests send it as the bearer token
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
# Run with your config
docker run \
-v $(pwd)/proxy_config.yaml:/app/config.yaml \
-e LITELLM_MASTER_KEY="sk-1234" \
-e LITELLM_MASTER_KEY \
-p 4000:4000 \
litellm_dev \
--config /app/config.yaml --detailed_debug

View file

@ -168,7 +168,7 @@ from a2a.utils.constants import TransportProtocol
from uuid import uuid4
base_url = "http://localhost:4000/a2a/my-agent" # LiteLLM proxy + agent name
headers = {"Authorization": "Bearer sk-1234"} # LiteLLM Virtual Key
headers = {"Authorization": "Bearer <your-master-key>"} # LiteLLM master key or a virtual key
async with httpx.AsyncClient(headers=headers, timeout=60.0) as http_client:
resolver = A2ACardResolver(httpx_client=http_client, base_url=base_url)
@ -233,7 +233,7 @@ async with stdio_client(server_params) as (read, write):
```bash
curl -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
-H 'Authorization: Bearer sk-1234' \
-H 'Authorization: Bearer <your-master-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "gpt-4o",
@ -255,7 +255,7 @@ curl -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
"LiteLLM": {
"url": "http://localhost:4000/mcp/",
"headers": {
"x-litellm-api-key": "Bearer sk-1234"
"x-litellm-api-key": "Bearer <your-master-key>"
}
}
}

View file

@ -3,7 +3,8 @@
# YOU MUST CHANGE THESE BEFORE GOING INTO PRODUCTION
############
LITELLM_MASTER_KEY="sk-1234"
# Generate one with: echo "LITELLM_MASTER_KEY=sk-$(openssl rand -hex 32)"
LITELLM_MASTER_KEY=""
############
# Database - You can change these to any PostgreSQL database that has logical replication enabled.

View file

@ -86,7 +86,7 @@ import anthropic
# point anthropic sdk to litellm proxy
client = anthropic.Anthropic(
base_url="http://0.0.0.0:4000",
api_key="sk-1234",
api_key="<your-master-key>",
)
response = client.messages.create(

View file

@ -183,14 +183,14 @@ def get_provider_container_config(
```bash
# Create container via Azure
curl -X POST "http://localhost:4000/v1/containers" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer <your-master-key>" \
-H "custom-llm-provider: azure" \
-H "Content-Type: application/json" \
-d '{"name": "My Azure Container"}'
# List container files via Azure
curl -X GET "http://localhost:4000/v1/containers/cntr_123/files" \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer <your-master-key>" \
-H "custom-llm-provider: azure"
```
@ -219,12 +219,13 @@ python -m pytest tests/test_litellm/containers/ -v
Test via proxy:
```bash
# Start proxy
# Start proxy (proxy_config.yaml reads its master key from LITELLM_MASTER_KEY)
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)"
cd litellm/proxy && python proxy_cli.py --config proxy_config.yaml --port 4000
# Test endpoints
curl -X GET "http://localhost:4000/v1/containers/cntr_123/files" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
```
---

View file

@ -148,7 +148,7 @@ litellm --config config.yaml --detailed_debug
```bash
curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
-H 'Authorization: Bearer <your-master-key>' \
-d '{
"model": "my-bitbucket-model",
"messages": [{"role": "user", "content": "IGNORED"}],

View file

@ -148,7 +148,7 @@ litellm --config config.yaml --detailed_debug
```bash
curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
-H 'Authorization: Bearer <your-master-key>' \
-d '{
"model": "my-gitlab-model",
"messages": [{"role": "user", "content": "IGNORED"}],

View file

@ -80,4 +80,4 @@ litellm_settings:
drop_params: True
general_settings:
master_key: sk-1234 # REPLACE in production
master_key: os.environ/LITELLM_MASTER_KEY

View file

@ -73,7 +73,8 @@ def missing_keys_form(missing_key_names: str):
<h1>Environment Setup Instructions</h1>
<p>Please add the following variables to your environment variables:</p>
<pre>
<span class="env-var">LITELLM_MASTER_KEY="sk-1234"</span> <span class="comment"># Your master key for the proxy server. Can use this to send /chat/completion requests etc</span>
<span class="comment"># Generate one with: echo "LITELLM_MASTER_KEY=sk-$(openssl rand -hex 32)"</span>
<span class="env-var">LITELLM_MASTER_KEY=""</span> <span class="comment"># Your master key for the proxy server. Can use this to send /chat/completion requests etc</span>
<span class="env-var">LITELLM_SALT_KEY="sk-XXXXXXXX"</span> <span class="comment"># Can NOT CHANGE THIS ONCE SET - It is used to encrypt/decrypt credentials stored in DB. If value of 'LITELLM_SALT_KEY' changes your models cannot be retrieved from DB</span>
<span class="env-var">DATABASE_URL="postgres://..."</span> <span class="comment"># Need a postgres database? (Check out Supabase, Neon, etc)</span>
<span class="comment">## OPTIONAL ##</span>

View file

@ -198,7 +198,7 @@ model_list:
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
# Opt-in: let CheckBatchCost track cost for unmanaged batches created with a raw
# gs:// (Vertex) or s3:// (Bedrock) input_file_id. Requires a matching deployment
# configured for the batched model. Defaults to false.

View file

@ -49,4 +49,4 @@ litellm_settings:
drop_params: True
general_settings:
master_key: sk-1234 # REPLACE in production
master_key: os.environ/LITELLM_MASTER_KEY

View file

@ -72,4 +72,4 @@ files_settings:
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
master_key: os.environ/LITELLM_MASTER_KEY # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys

View file

@ -29,7 +29,7 @@ model_list:
model: openai/*
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
custom_auth: custom_auth_basic.user_api_key_auth
pass_through_endpoints:
- path: "/azure-config-passthrough"

View file

@ -5,7 +5,7 @@ model_list:
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
database_url: "postgresql://user:password@localhost:5432/litellm"
# Reject requests that contain client-side metadata.tags

View file

@ -29,7 +29,7 @@ guardrails:
# Optional: Configure general settings
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
# Optional: Add logging configuration
litellm_settings:

View file

@ -25,7 +25,7 @@ litellm_settings:
# 1. Apply guardrail to a specific request:
# curl --location 'http://localhost:4000/chat/completions' \
# --header 'Authorization: Bearer sk-1234' \
# --header 'Authorization: Bearer <your-master-key>' \
# --header 'Content-Type: application/json' \
# --data '{
# "model": "gpt-4",
@ -35,7 +35,7 @@ litellm_settings:
# 2. Apply guardrail with dynamic parameters:
# curl --location 'http://localhost:4000/chat/completions' \
# --header 'Authorization: Bearer sk-1234' \
# --header 'Authorization: Bearer <your-master-key>' \
# --header 'Content-Type: application/json' \
# --data '{
# "model": "gpt-4",

View file

@ -49,7 +49,7 @@ mcp_servers:
# General Settings
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
store_model_in_db: false
# LiteLLM Settings

View file

@ -45,7 +45,7 @@ model_list:
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: sk-1234
master_key: os.environ/LITELLM_MASTER_KEY
litellm_settings:
drop_params: True

View file

@ -48,7 +48,7 @@ GET /v1/workflows/runs/{run_id}/messages Conversation history (ordered by se
```bash
# Create a run
curl -X POST http://localhost:4000/v1/workflows/runs \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer <your-master-key>" \
-H "Content-Type: application/json" \
-d '{"workflow_type": "shin-builder", "metadata": {"title": "Fix login bug"}}'
@ -56,19 +56,19 @@ curl -X POST http://localhost:4000/v1/workflows/runs \
# Mark step started (sets status → running)
curl -X POST http://localhost:4000/v1/workflows/runs/abc-123/events \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer <your-master-key>" \
-H "Content-Type: application/json" \
-d '{"event_type": "step.started", "step_name": "grill", "data": {"claude_session_id": "sess-789"}}'
# Store a conversation message
curl -X POST http://localhost:4000/v1/workflows/runs/abc-123/messages \
-H "Authorization: Bearer sk-1234" \
-H "Authorization: Bearer <your-master-key>" \
-H "Content-Type: application/json" \
-d '{"role": "user", "content": "What is the expected behavior?", "session_id": "sess-789"}'
# Restart recovery: fetch active runs and resume from last event's data.claude_session_id
curl "http://localhost:4000/v1/workflows/runs?status=running,paused&workflow_type=shin-builder" \
-H "Authorization: Bearer sk-1234"
-H "Authorization: Bearer <your-master-key>"
```
## Status Auto-Update Rules

View file

@ -220,7 +220,7 @@ router_settings:
model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"}
general_settings:
master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
master_key: os.environ/LITELLM_MASTER_KEY # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
store_model_in_db: True
proxy_budget_rescheduler_min_time: 60
proxy_budget_rescheduler_max_time: 64

View file

@ -41,6 +41,8 @@ The repo ships with a working example config:
```bash
export OPENAI_API_KEY=sk-... # underlying models hit OpenAI
export LITELLM_MASTER_KEY="sk-$(openssl rand -hex 32)" # the example config reads its master key from here
echo "$LITELLM_MASTER_KEY" # copy it, the chat page and dashboard ask for it
uv run litellm \
--config litellm/proxy/example_config_yaml/adaptive_router_example.yaml \
--port 4000
@ -83,19 +85,19 @@ The dashboard is a single static HTML file. Either:
In the connect bar, fill in:
- **Proxy URL:** `http://localhost:4000`
- **Master Key:** the `master_key` from your config (`sk-1234` in the example).
- **Master Key:** the `LITELLM_MASTER_KEY` printed in step 1.
Click **Connect**. The dashboard polls `GET /adaptive_router/state` every
500ms (admin-only endpoint, returns one snapshot per configured router).
## 5. Drive synthetic traffic
In a second terminal:
In a second terminal, replacing `<your-master-key>` with the key printed in step 1:
```bash
uv run python scripts/adaptive_router_demo/traffic.py \
--proxy-url http://localhost:4000 \
--api-key sk-1234 \
--api-key <your-master-key> \
--router smart-cheap-router \
--rounds 100 \
--rate 0.5

View file

@ -0,0 +1,12 @@
import re
from litellm.proxy.common_utils.admin_ui_utils import missing_keys_form
def test_missing_keys_form_shows_generate_command_instead_of_a_literal_master_key():
html = missing_keys_form(missing_key_names="DATABASE_URL, LITELLM_MASTER_KEY")
assert "DATABASE_URL, LITELLM_MASTER_KEY" in html
assert 'echo "LITELLM_MASTER_KEY=sk-$(openssl rand -hex 32)"' in html
suggested_master_key_values = re.findall(r'LITELLM_MASTER_KEY="([^"]*)"', html)
assert suggested_master_key_values == [""]

View file

@ -82,14 +82,14 @@ llm = AzureOpenAI(
engine="azure-gpt-3.5", # model_name on litellm proxy
temperature=0.0,
azure_endpoint="${base_url}", # litellm proxy endpoint
api_key="sk-1234", # litellm proxy API Key
api_key="<your-master-key>", # litellm proxy API Key
api_version="2023-07-01-preview",
)
embed_model = AzureOpenAIEmbedding(
deployment_name="azure-embedding-model",
azure_endpoint="${base_url}",
api_key="sk-1234",
api_key="<your-master-key>",
api_version="2023-07-01-preview",
)

View file

@ -59,7 +59,7 @@ const HowItWorks: React.FC = () => {
language="bash"
code={`curl -X POST -i http://your-proxy:4000/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-H "Authorization: Bearer <your-master-key>" \\
-d '{
"model": "gemini/gemini-2.5-pro",
"messages": [{"role": "user", "content": "Hello"}]

View file

@ -236,6 +236,7 @@ describe("AgentBuilderView", () => {
const snippet = await screen.findByTestId("code-block");
expect(snippet).toHaveTextContent("https://proxy.example.com/v1/chat/completions");
expect(snippet).toHaveTextContent('"model": "support-agent"');
expect(snippet).toHaveTextContent("x-litellm-api-key: Bearer <your-master-key>");
});
it("mints a key scoped to the selected agent", async () => {

View file

@ -90,7 +90,7 @@ function ConnectTabContent({
? createdKeyValue.startsWith("Bearer ")
? createdKeyValue
: `Bearer ${createdKeyValue}`
: "Bearer sk-1234";
: "Bearer <your-master-key>";
const curlExample = `curl -L -X POST '${baseUrl}/v1/chat/completions' \\
-H 'x-litellm-api-key: ${apiKeyForCurl}' \\
-d '{

View file

@ -45,6 +45,16 @@ describe("PromptCodeSnippets", () => {
expect(screen.getByRole("combobox", { name: "Language" })).toHaveTextContent("Python (OpenAI SDK)");
});
it("shows a key placeholder when there is no access token", async () => {
const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never });
render(<PromptCodeSnippets promptId="welcome" model="gpt-4o" accessToken={null} version="2" />);
await user.click(screen.getByRole("button", { name: /get code/i }));
await screen.findByText("Generated Code");
await user.click(screen.getByRole("button", { name: /copy to clipboard/i }));
expect(await navigator.clipboard.readText()).toContain("'Authorization: Bearer <your-master-key>'");
});
it("includes the viewed environment in every generated request", async () => {
const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never });
render(

View file

@ -61,7 +61,7 @@ const PromptCodeSnippets: React.FC<PromptCodeSnippetsProps> = ({
apiBase = proxySettings.PROXY_BASE_URL;
}
const effectiveApiKey = accessToken || "sk-1234";
const effectiveApiKey = accessToken || "<your-master-key>";
// Generate code based on selected language and tab
const generateCode = () => {

View file

@ -1084,7 +1084,7 @@ config = {
"${selectedMcpServer.server_name}": {
"url": "${getProxyBaseUrl()}/${selectedMcpServer.server_name}/mcp",
"headers": {
"x-litellm-api-key": "Bearer sk-1234"
"x-litellm-api-key": "Bearer <your-master-key>"
}
}
}

View file

@ -22,6 +22,11 @@ describe("getCurlCommand", () => {
const result = getCurlCommand("gpt-4o", "");
expect(result).toContain("Your query here");
});
it("should show a key placeholder instead of a literal key", () => {
const result = getCurlCommand("gpt-4o", "test query");
expect(result).toContain("'Authorization: Bearer <your-master-key>'");
});
});
describe("runSemanticFilterTest", () => {

View file

@ -71,7 +71,7 @@ export const runSemanticFilterTest = async ({
export const getCurlCommand = (testModel: string | null, testQuery: string) =>
`curl --location 'http://localhost:4000/v1/responses' \\
--header 'Content-Type: application/json' \\
--header 'Authorization: Bearer sk-1234' \\
--header 'Authorization: Bearer <your-master-key>' \\
--data '{
"model": "${testModel ?? "YOUR_MODEL"}",
"input": [

View file

@ -1275,7 +1275,7 @@ config = {
"${selectedMcpServer.server_name}": {
"url": "${getProxyBaseUrl()}/${selectedMcpServer.server_name}/mcp",
"headers": {
"x-litellm-api-key": "Bearer sk-1234"
"x-litellm-api-key": "Bearer <your-master-key>"
}
}
}
@ -1315,7 +1315,7 @@ config = {
"${selectedMcpServer.server_name}": {
"url": "${getProxyBaseUrl()}/${selectedMcpServer.server_name}/mcp",
"headers": {
"x-litellm-api-key": "Bearer sk-1234"
"x-litellm-api-key": "Bearer <your-master-key>"
}
}
}