Skip to content

feat(ai): add Claude Code Max/Pro subscription pass-through to LiteLLM - #1467

Merged
Aviator-Coding merged 4 commits into
mainfrom
fm/homeops-litellm-claude-code-max
Aug 28, 2026
Merged

feat(ai): add Claude Code Max/Pro subscription pass-through to LiteLLM#1467
Aviator-Coding merged 4 commits into
mainfrom
fm/homeops-litellm-claude-code-max

Conversation

@Aviator-Coding

Copy link
Copy Markdown
Owner

Intent

Wire LiteLLM (kubernetes/apps/base/ai/litellm/, operator-shaped: LiteLLMProxy + LiteLLMModel + LiteLLMVirtualKey CRs from the home-operations litellm-operator) to support the Claude Code Max/Pro SUBSCRIPTION pass-through pattern from https://docs.litellm.ai/docs/tutorials/claude_code_max_subscription. Captain request 2026-08-27: 'use the claude code subscription claude-code cli and get more insight.'

MECHANISM, which is unlike every other model in this repo: the client is a claude CLI on a person's workstation, logged in via Claude Code's own subscription OAuth. It sends its OWN Authorization: Bearer sk-ant-oat... header on every request. LiteLLM holds NO shared credential for this model and forwards the client's token upstream; tokens bill that person's flat-rate plan. The cluster's payoff is observability (per-request tokens/latency, attributable per virtual key) for traffic that previously bypassed the proxy. There is deliberately NO 1Password item and NO ExternalSecret change for this model - that absence is correct, not an omission.

DELIBERATE DECISION 1 (captain-approved, supersedes the original instruction): do NOT set general_settings.forward_client_headers_to_llm_api. The task originally said to add it because the upstream tutorial calls it 'Required: forwards OAuth token to Anthropic'. Research against our pinned image ghcr.io/berriai/litellm-non_root:v1.98.0 found that claim wrong on both halves: (a) it never forwards Authorization - its only effect is _get_forwardable_headers, an allow-list of x-* (minus x-stainless*) plus anthropic-beta; (b) the OAuth token instead rides a separate UNGATED path, add_provider_specific_headers_to_request, called unconditionally at litellm_pre_call_utils.py:1704, so pass-through works with the flag off; and (c) the flag is genuinely proxy-global, so setting it would forward every client x-* header to every backend (xai, zai, openrouter, local llama.cpp) for zero benefit. The task's own escape clause required stopping rather than shipping an unsafely-scoped flag; the captain reviewed the evidence and approved shipping without it. A narrower per-model form (litellm_settings.model_group_settings.forward_client_headers_to_llm_api, a list of model names) exists and is documented as the option if it is ever needed. Do not 'fix' this by adding the flag.

DELIBERATE DECISION 2 (captain-approved): the new model ships WITH its own LiteLLMVirtualKey, scoped to that one model, carrying rpmLimit/tpmLimit only and deliberately NO maxBudget - unique among the keys in app/virtualkeys/. A budget there would be silently inert because the model is priced at $0, and a cap that can never trip reads as protection that does not exist. Sized as one interactive CLI user, same order of magnitude as the existing opencode workspace key (8 rpm / 200000 tpm), set to 10 / 250000 because Claude Code's agentic loop spends requests per tool call rather than per human turn.

DELIBERATE DECISION 3: explicit $0 info.extra prices on the model, a documented exception to that directory's standing 'no hand-written prices on cloud models' rule - and the same rule underneath, since the rule is 'declared prices must match the invoice' and this invoice is a flat-rate subscription with no per-token line, so LiteLLM's metered cost map is what would lie and would inflate a D4 budget with dollars nobody is charged. Verified in the running pod that explicit 0 is honoured rather than read as unset (use_custom_pricing_for_model tests is not None, not truthiness) and that omitting the keys is what falls back to the metered map.

DELIBERATE DECISION 4: the model CR carries a non-secret placeholder apiKey string 'sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'. This is NOT a leaked credential and authenticates nothing. It is load-bearing: omitting apiKey does NOT make the model credential-less, because AnthropicModelInfo.get_api_key is api_key or get_secret_str('ANTHROPIC_API_KEY') and the proxy pod holds ANTHROPIC_API_KEY via envFrom - verified live in-pod - so a caller who forgot the OAuth header would SILENTLY bill the household's metered account. The placeholder keeps api_key non-None (blocking that fallback) while carrying the sk-ant-oat prefix so the request takes the OAuth branch and fails as a clean Anthropic 401 'OAuth access token is invalid.' with zero spend. A real client token overrides it.

MODEL ID: resolved against Anthropic's real current direct catalog rather than copying the tutorial's stale examples (it shows claude-sonnet-4-20250514 / claude-3-5-*). Uses anthropic/claude-sonnet-5, the same dash-form id family as the working sibling claude-sonnet-5 CR.

SECURITY REVIEW, done before shipping and verified live against the running proxy with the flag absent from the rendered config (so these describe production as it already is): virtual-key governance is NOT bypassable by a client header - the demo key is denied claude-sonnet-5 with 403 identically with and without a fake Authorization header, and its allowed model returns 200 identically either way, on both /v1/chat/completions and /v1/messages. Separately confirmed a PRE-EXISTING behaviour that this PR neither introduces nor changes: a client sk-ant-oat token already overrides the shared ANTHROPIC_API_KEY on Anthropic-family models a caller is entitled to (Anthropic answered 'OAuth access token is invalid.' for a fake token sent to claude-sonnet-5, proving our real key was never sent), and it briefly cooled that model group down for all consumers. It is bounded (that prefix only, anthropic/bedrock/vertex_ai only, only when authenticating via x-litellm-api-key) and cannot bypass allow-lists. That finding is reported to the captain OUTSIDE this task and is explicitly NOT something this PR should fix - do not expand scope to address it.

SCOPE: strictly additive. One LiteLLMModel CR, one LiteLLMVirtualKey (+ its PushSecret, matching the sibling pattern), a new runbook doc, README updates and one AGENTS.md knowledge entry. No existing model CR, virtual key, allow-list, fallback chain, ExternalSecret or generalSettings value is modified. The new model is deliberately absent from every fallback chain, because a config-declared fallback bypasses the calling key's allow-list and pointing one at a model that requires a client-supplied token would fail every caller who does not send one.

Documentation deliverable includes the client-side runbook: ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, ANTHROPIC_CUSTOM_HEADERS carrying the virtual key as x-litellm-api-key (never as Authorization, which is reserved for the subscription token), how to get the virtual key, and the one-time interactive OAuth login - which is stated plainly as a manual per-person step whose headless verification is out of scope, same as any other manual credential step in this repo.

Local validation already run: flux-local 'task flux:test:all' 227 passed; kustomize builds clean; server-side dry-run of both new CRs accepted by the live CRD and the operator's failurePolicy:Fail webhook; no key-alias collision in the proxy's Postgres; pre-commit hooks including the secret detector pass.

What Changed

  • Adds a claude-code-subscription LiteLLMModel (Anthropic claude-sonnet-5 via client OAuth pass-through, placeholder apiKey, explicit $0 pricing, no cluster credential) and a matching single-model LiteLLMVirtualKey + PushSecret with rpm/tpm limits only.
  • Documents the pass-through mechanism, deliberate non-use of forward_client_headers_to_llm_api, pricing/budget exceptions, security findings, and client runbook in docs/ai-system/litellm/claude-code-subscription.md, with README and AGENTS.md cross-links.
  • Adds scripts/ci/litellm-claude-code-subscription-test.py to lock the CR shape and documented decisions.

Risk Assessment

✅ Low: Strictly additive LiteLLM model and virtual-key wiring that matches every captain-approved constraint (placeholder apiKey, $0 prices, rpm/tpm-only key, no header-forward flag, no fallback membership) and follows existing sibling CR patterns without touching live shared config.

Testing

Added and ran a focused operator-render + kustomize + runbook contract test, re-ran the existing fallback-chain suite for regression, and executed the same subscription test inside the pinned litellm-non_root:v1.98.0 image to prove OAuth placeholder routing, ANTHROPIC_API_KEY fallback blocking, and explicit $0 custom pricing (metered sonnet 0.007 vs zeroed 0.0). No live cluster or interactive Claude OAuth session was available; those remain out of scope per the intent. All targeted checks passed.

Evidence: Operator file-mode render of subscription model + virtual key (no forward flag, not in fallbacks)

subscription_model: anthropic/claude-sonnet-5 + sk-ant-oat-PLACEHOLDER + $0 prices; forward_client_headers_to_llm_api_set: false; fallbacks only chat-ha/auto→claude-sonnet-5; virtualkey rpm=10 tpm=250000 no maxBudget

operator_file_mode_render:
  subscription_model:
    model_name: claude-code-subscription
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN
    model_info:
      input_cost_per_token: 0
      output_cost_per_token: 0
  general_settings:
    store_model_in_db: false
  forward_client_headers_to_llm_api_set: false
  availability_fallback_map:
    chat-ha:
    - claude-sonnet-5
    auto:
    - claude-sonnet-5
virtualkey_spec:
  proxyRef: litellm
  keyAlias: claude-code-subscription
  secretName: litellm-key-claude-code-subscription
  secretKey: key
  models:
  - claude-code-subscription
  rpmLimit: 10
  tpmLimit: 250000
Evidence: Host subscription contract test transcript (40 passed)
APP_DIR=/Users/coder/.no-mistakes/worktrees/07f90326a876/01M12T2GKHMC18J7G3BP9S8EZK/kubernetes/apps/base/ai/litellm/app
rendered model_list=33 names_has_subscription=True
[PASS] claude_code_subscription_model_present_in_operator_render — n_models=33 has=True
[PASS] upstream_model_id_is_anthropic_claude_sonnet_5_dash_form — model='anthropic/claude-sonnet-5'
[PASS] api_key_is_present_non_env_placeholder — api_key='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'
[PASS] api_key_carries_sk_ant_oat_prefix_for_oauth_branch — prefix_ok=True
[PASS] sibling_claude_sonnet_5_still_uses_shared_env_key — sonnet_api_key='os.environ/ANTHROPIC_API_KEY'
[PASS] model_info_declares_explicit_zero_token_prices — model_info={'input_cost_per_token': 0, 'output_cost_per_token': 0}
[PASS] zero_prices_are_numeric_zero_not_none — types=(int,int)
[PASS] general_settings_does_not_enable_forward_client_headers — general_settings.forward=None
[PASS] litellm_settings_has_no_model_group_forward_headers_list — model_group_settings.forward=None
[PASS] proxy_cr_generalSettings_omits_forward_client_headers — keys=['store_model_in_db']
[PASS] proxy_cr_litellmSettings_omits_global_forward_flag — keys=['cache', 'cache_params', 'callbacks']
[PASS] fallback_maps_parse — avail={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] claude_code_subscription_not_a_fallback_primary — in_avail=False in_ctx=False
[PASS] claude_code_subscription_not_a_fallback_target — avail_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] virtualkey_cr_present — keys=['claude-code-subscription', 'demo', 'ha-demo', 'opencode', 'repo-wiki', 'router-demo']
[PASS] virtualkey_scoped_only_to_subscription_model — models=['claude-code-subscription']
[PASS] virtualkey_key_alias_matches_name — keyAlias='claude-code-subscription'
[PASS] virtualkey_has_no_maxBudget — spec_keys=['keyAlias', 'models', 'proxyRef', 'rpmLimit', 'secretKey', 'secretName', 'tpmLimit']
[PASS] virtualkey_rpm_tpm_match_captain_sizing — rpm=10 tpm=250000
[PASS] only_subscription_key_lacks_maxBudget — no_budget_keys=['claude-code-subscription']
[PASS] no_other_virtualkey_holds_subscription_model — other_holders=[]
[PASS] sibling_opencode_key_still_has_maxBudget_and_lower_rpm — opencode={'maxBudget': '10.00', 'rpmLimit': 8, 'tpmLimit': 200000, 'models': ['auto']}
[PASS] pushsecret_companion_present — n=1
[PASS] pushsecret_targets_litellm_consumer_item — remote={'remoteKey': 'litellm-consumer-claude-code-subscription', 'property': 'key'} selector={'secret': {'name': 'litellm-key-claude-code-subscription'}}
[PASS] externalsecret_file_still_single_shared_secret — n=1
[PASS] externalsecret_does_not_reference_subscription_model_or_placeholder — es_name=litellm
[PASS] externalsecret_still_pulls_shared_ai_keys_item — extracts=['litellm', 'cloudnative-pg', 'ai-keys']
[PASS] kubectl_kustomize_app_succeeds — bytes=28420
[PASS] kustomize_emits_subscription_model_key_pushsecret — missing=[]
[PASS] kustomize_emitted_model_keeps_placeholder_and_zero_prices — params={'apiKey': 'sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN', 'model': 'anthropic/claude-sonnet-5'} extra={'input_cost_per_token': 0, 'output_cost_per_token': 0}
[PASS] kustomize_emitted_key_has_limits_no_budget — spec={'keyAlias': 'claude-code-subscription', 'models': ['claude-code-subscription'], 'proxyRef': 'litellm', 'rpmLimit': 10, 'secretKey': 'key', 'secretName': 'litellm-key-claude-code-subscription', 'tpmLimit': 250000}
[PASS] runbook_doc_exists — bytes=14340
[PASS] runbook_documents_client_env_contract — missing=[]
[PASS] runbook_model_value_is_claude_code_subscription — model assignment present
[PASS] runbook_forbids_putting_virtual_key_in_authorization — authorization guidance present
[PASS] runbook_states_oauth_login_is_manual_per_person — oauth login section present
[PASS] runbook_states_headless_oauth_out_of_scope — headless scope note present
[PASS] runbook_records_forward_client_headers_deliberately_off — flag decision recorded
[PASS] app_readme_links_subscription_pass_through_section — readme_bytes=17194
[PASS] litellm_runtime_available_for_subscription_proofs — litellm not installed here - semantic CR proofs still ran; re-run under ghcr.io/berriai/litellm-non_root:v1.98.0 for runtime proofs

=== SUMMARY ===
passed=40 failed=0 total=40
wrote /var/folders/yr/h20mxtv56yj1c1pt9tr1_kbc0000gn/T/no-mistakes-evidence/01M12T2GKHMC18J7G3BP9S8EZK/claude-code-subscription-test.json
Evidence: LiteLLM v1.98.0 runtime proofs transcript (47 passed)

use_custom_pricing zero: top/metadata/messages=True; completion_cost metered=0.007 zeroed=0.0; is_anthropic_oauth_key(placeholder)=True; get_api_key(None) falls back to env; get_api_key(placeholder) does not

APP_DIR=/repo/kubernetes/apps/base/ai/litellm/app
rendered model_list=33 names_has_subscription=True
[PASS] claude_code_subscription_model_present_in_operator_render — n_models=33 has=True
[PASS] upstream_model_id_is_anthropic_claude_sonnet_5_dash_form — model='anthropic/claude-sonnet-5'
[PASS] api_key_is_present_non_env_placeholder — api_key='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'
[PASS] api_key_carries_sk_ant_oat_prefix_for_oauth_branch — prefix_ok=True
[PASS] sibling_claude_sonnet_5_still_uses_shared_env_key — sonnet_api_key='os.environ/ANTHROPIC_API_KEY'
[PASS] model_info_declares_explicit_zero_token_prices — model_info={'input_cost_per_token': 0, 'output_cost_per_token': 0}
[PASS] zero_prices_are_numeric_zero_not_none — types=(int,int)
[PASS] general_settings_does_not_enable_forward_client_headers — general_settings.forward=None
[PASS] litellm_settings_has_no_model_group_forward_headers_list — model_group_settings.forward=None
[PASS] proxy_cr_generalSettings_omits_forward_client_headers — keys=['store_model_in_db']
[PASS] proxy_cr_litellmSettings_omits_global_forward_flag — keys=['cache', 'cache_params', 'callbacks']
[PASS] fallback_maps_parse — avail={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] claude_code_subscription_not_a_fallback_primary — in_avail=False in_ctx=False
[PASS] claude_code_subscription_not_a_fallback_target — avail_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] virtualkey_cr_present — keys=['claude-code-subscription', 'demo', 'ha-demo', 'opencode', 'repo-wiki', 'router-demo']
[PASS] virtualkey_scoped_only_to_subscription_model — models=['claude-code-subscription']
[PASS] virtualkey_key_alias_matches_name — keyAlias='claude-code-subscription'
[PASS] virtualkey_has_no_maxBudget — spec_keys=['keyAlias', 'models', 'proxyRef', 'rpmLimit', 'secretKey', 'secretName', 'tpmLimit']
[PASS] virtualkey_rpm_tpm_match_captain_sizing — rpm=10 tpm=250000
[PASS] only_subscription_key_lacks_maxBudget — no_budget_keys=['claude-code-subscription']
[PASS] no_other_virtualkey_holds_subscription_model — other_holders=[]
[PASS] sibling_opencode_key_still_has_maxBudget_and_lower_rpm — opencode={'maxBudget': '10.00', 'rpmLimit': 8, 'tpmLimit': 200000, 'models': ['auto']}
[PASS] pushsecret_companion_present — n=1
[PASS] pushsecret_targets_litellm_consumer_item — remote={'remoteKey': 'litellm-consumer-claude-code-subscription', 'property': 'key'} selector={'secret': {'name': 'litellm-key-claude-code-subscription'}}
[PASS] externalsecret_file_still_single_shared_secret — n=1
[PASS] externalsecret_does_not_reference_subscription_model_or_placeholder — es_name=litellm
[PASS] externalsecret_still_pulls_shared_ai_keys_item — extracts=['litellm', 'cloudnative-pg', 'ai-keys']
[PASS] kubectl_kustomize_app_succeeds — skipped: kubectl not in this environment (host run covers kustomize)
[PASS] kustomize_emits_subscription_model_key_pushsecret — models_has=True keys_has=True
[PASS] kustomize_emitted_model_keeps_placeholder_and_zero_prices — params={'model': 'anthropic/claude-sonnet-5', 'apiKey': 'sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'} extra={'input_cost_per_token': 0, 'output_cost_per_token': 0} (source CR; no kubectl)
[PASS] kustomize_emitted_key_has_limits_no_budget — spec={'proxyRef': 'litellm', 'keyAlias': 'claude-code-subscription', 'secretName': 'litellm-key-claude-code-subscription', 'secretKey': 'key', 'models': ['claude-code-subscription'], 'rpmLimit': 10, 'tpmLimit': 250000} (source CR; no kubectl)
[PASS] runbook_doc_exists — bytes=14340
[PASS] runbook_documents_client_env_contract — missing=[]
[PASS] runbook_model_value_is_claude_code_subscription — model assignment present
[PASS] runbook_forbids_putting_virtual_key_in_authorization — authorization guidance present
[PASS] runbook_states_oauth_login_is_manual_per_person — oauth login section present
[PASS] runbook_states_headless_oauth_out_of_scope — headless scope note present
[PASS] runbook_records_forward_client_headers_deliberately_off — flag decision recorded
[PASS] app_readme_links_subscription_pass_through_section — readme_bytes=17194
[PASS] litellm_runtime_available_for_subscription_proofs — import ok
[PASS] runtime_use_custom_pricing_honours_explicit_zero — top=True metadata=True messages=True empty=True none=True
[PASS] runtime_completion_cost_zero_custom_vs_metered_sonnet — metered=0.007 zeroed=0.0
[PASS] runtime_router_preserves_explicit_zero_model_info — model_info={'input_cost_per_token': 0.0, 'output_cost_per_token': 0.0, 'id': 'eb7a36f092e92c288725977bc63a71283880c9936fefc72f35df5d64b4837bff', 'db_model': False}
[PASS] runtime_placeholder_detected_as_anthropic_oauth_key — placeholder='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'
[PASS] runtime_metered_api_key_not_detected_as_oauth — metered prefix rejected
[PASS] runtime_get_api_key_none_falls_back_to_env_anthropic_key — got='sk-ant-api03-TEST-SHARED-METERED-KEY'
[PASS] runtime_get_api_key_placeholder_does_not_fall_back_to_env — got='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'

=== SUMMARY ===
passed=47 failed=0 total=47
wrote /evidence/claude-code-subscription-runtime.json
Evidence: Runtime proof machine-readable results JSON
{
  "results": [
    {
      "name": "claude_code_subscription_model_present_in_operator_render",
      "ok": true,
      "detail": "n_models=33 has=True"
    },
    {
      "name": "upstream_model_id_is_anthropic_claude_sonnet_5_dash_form",
      "ok": true,
      "detail": "model='anthropic/claude-sonnet-5'"
    },
    {
      "name": "api_key_is_present_non_env_placeholder",
      "ok": true,
      "detail": "api_key='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'"
    },
    {
      "name": "api_key_carries_sk_ant_oat_prefix_for_oauth_branch",
      "ok": true,
      "detail": "prefix_ok=True"
    },
    {
      "name": "sibling_claude_sonnet_5_still_uses_shared_env_key",
      "ok": true,
      "detail": "sonnet_api_key='os.environ/ANTHROPIC_API_KEY'"
    },
    {
      "name": "model_info_declares_explicit_zero_token_prices",
      "ok": true,
      "detail": "model_info={'input_cost_per_token': 0, 'output_cost_per_token': 0}"
    },
    {
      "name": "zero_prices_are_numeric_zero_not_none",
      "ok": true,
      "detail": "types=(int,int)"
    },
    {
      "name": "general_settings_does_not_enable_forward_client_headers",
      "ok": true,
      "detail": "general_settings.forward=None"
    },
    {
      "name": "litellm_settings_has_no_model_group_forward_headers_list",
      "ok": true,
      "detail": "model_group_settings.forward=None"
    },
    {
      "name": "proxy_cr_generalSettings_omits_forward_client_headers",
      "ok": true,
      "detail": "keys=['store_model_in_db']"
    },
    {
      "name": "proxy_cr_litellmSettings_omits_global_forward_flag",
      "ok": true,
      "detail": "keys=['cache', 'cache_params', 'callbacks']"
    },
    {
      "name": "fallback_maps_parse",
      "ok": true,
      "detail": "avail={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}"
    },
    {
      "name": "claude_code_subscription_not_a_fallback_primary",
      "ok": true,
      "detail": "in_avail=False in_ctx=False"
    },
    {
      "name": "claude_code_subscription_not_a_fallback_target",
      "ok": true,
      "detail": "avail_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']} ctx_targets={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}"
    },
    {
      "name": "virtualkey_cr_present",
      "ok": true,
      "detail": "keys=['claude-code-subscription', 'demo', 'ha-demo', 'opencode', 'repo-wiki', 'router-demo']"
    },
    {
      "name": "virtualkey_scoped_only_to_subscription_model",
      "ok": true,
      "detail": "models=['claude-code-subscription']"
    },
    {
      "name": "virtualkey_key_alias_matches_name",
      "ok": true,
      "detail": "keyAlias='claude-code-subscription'"
    },
    {
      "name": "virtualkey_has_no_maxBudget",
      "ok": true,
      "detail": "spec_keys=['keyAlias', 'models', 'proxyRef', 'rpmLimit', 'secretKey', 'secretName', 'tpmLimit']"
    },
    {
      "name": "virtualkey_rpm_tpm_match_captain_sizing",
      "ok": true,
      "detail": "rpm=10 tpm=250000"
    },
    {
      "name": "only_subscription_key_lacks_maxBudget",
      "ok": true,
      "detail": "no_budget_keys=['claude-code-subscription']"
    },
    {
      "name": "no_other_virtualkey_holds_subscription_model",
      "ok": true,
      "detail": "other_holders=[]"
    },
    {
      "name": "sibling_opencode_key_still_has_maxBudget_and_lower_rpm",
      "ok": true,
      "detail": "opencode={'maxBudget': '10.00', 'rpmLimit': 8, 'tpmLimit': 200000, 'models': ['auto']}"
    },
    {
      "name": "pushsecret_companion_present",
      "ok": true,
      "detail": "n=1"
    },
    {
      "name": "pushsecret_targets_litellm_consumer_item",
      "ok": true,
      "detail": "remote={'remoteKey': 'litellm-consumer-claude-code-subscription', 'property': 'key'} selector={'secret': {'name': 'litellm-key-claude-code-subscription'}}"
    },
    {
      "name": "externalsecret_file_still_single_shared_secret",
      "ok": true,
      "detail": "n=1"
    },
    {
      "name": "externalsecret_does_not_reference_subscription_model_or_placeholder",
      "ok": true,
      "detail": "es_name=litellm"
    },
    {
      "name": "externalsecret_still_pulls_shared_ai_keys_item",
      "ok": true,
      "detail": "extracts=['litellm', 'cloudnative-pg', 'ai-keys']"
    },
    {
      "name": "kubectl_kustomize_app_succeeds",
      "ok": true,
      "detail": "skipped: kubectl not in this environment (host run covers kustomize)"
    },
    {
      "name": "kustomize_emits_subscription_model_key_pushsecret",
      "ok": true,
      "detail": "models_has=True keys_has=True"
    },
    {
      "name": "kustomize_emitted_model_keeps_placeholder_and_zero_prices",
      "ok": true,
      "detail": "params={'model': 'anthropic/claude-sonnet-5', 'apiKey': 'sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'} extra={'input_cost_per_token': 0, 'output_cost_per_token': 0} (source CR; no kubectl)"
    },
    {
      "name": "kustomize_emitted_key_has_limits_no_budget",
      "ok": true,
      "detail": "spec={'proxyRef': 'litellm', 'keyAlias': 'claude-code-subscription', 'secretName': 'litellm-key-claude-code-subscription', 'secretKey': 'key', 'models': ['claude-code-subscription'], 'rpmLimit': 10, 'tpmLimit': 250000} (source CR; no kubectl)"
    },
    {
      "name": "runbook_doc_exists",
      "ok": true,
      "detail": "bytes=14340"
    },
    {
      "name": "runbook_documents_client_env_contract",
      "ok": true,
      "detail": "missing=[]"
    },
    {
      "name": "runbook_model_value_is_claude_code_subscription",
      "ok": true,
      "detail": "model assignment present"
    },
    {
      "name": "runbook_forbids_putting_virtual_key_in_authorization",
      "ok": true,
      "detail": "authorization guidance present"
    },
    {
      "name": "runbook_states_oauth_login_is_manual_per_person",
      "ok": true,
      "detail": "oauth login section present"
    },
    {
      "name": "runbook_states_headless_oauth_out_of_scope",
      "ok": true,
      "detail": "headless scope note present"
    },
    {
      "name": "runbook_records_forward_client_headers_deliberately_off",
      "ok": true,
      "detail": "flag decision recorded"
    },
    {
      "name": "app_readme_links_subscription_pass_through_section",
      "ok": true,
      "detail": "readme_bytes=17194"
    },
    {
      "name": "litellm_runtime_available_for_subscription_proofs",
      "ok": true,
      "detail": "import ok"
    },
    {
      "name": "runtime_use_custom_pricing_honours_explicit_zero",
      "ok": true,
      "detail": "top=True metadata=True messages=True empty=True none=True"
    },
    {
      "name": "runtime_completion_cost_zero_custom_vs_metered_sonnet",
      "ok": true,
      "detail": "metered=0.007 zeroed=0.0"
    },
    {
      "name": "runtime_router_preserves_explicit_zero_model_info",
      "ok": true,
      "detail": "model_info={'input_cost_per_token': 0.0, 'output_cost_per_token': 0.0, 'id': 'eb7a36f092e92c288725977bc63a71283880c9936fefc72f35df5d64b4837bff', 'db_model': False}"
    },
    {
      "name": "runtime_placeholder_detected_as_anthropic_oauth_key",
      "ok": true,
      "detail": "placeholder='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'"
    },
    {
      "name": "runtime_metered_api_key_not_detected_as_oauth",
      "ok": true,
      "detail": "metered prefix rejected"
    },
    {
      "name": "runtime_get_api_key_none_falls_back_to_env_anthropic_key",
      "ok": true,
      "detail": "got='sk-ant-api03-TEST-SHARED-METERED-KEY'"
    },
    {
      "name": "runtime_get_api_key_placeholder_does_not_fall_back_to_env",
      "ok": true,
      "detail": "got='sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN'"
    }
  ],
  "failed": [],
  "model_names": [
    "auto",
    "chat-ha",
    "claude-code-subscription",
    "claude-fable-5",
    "claude-opus-4-8",
    "claude-opus-5",
    "claude-sonnet-5",
    "deepseek-v4-flash",
    "deepseek-v4-flash-0731",
    "deepseek-v4-pro",
    "deepseek-v4-pro-0813",
    "gemini-3.1-pro",
    "gemini-3.5-flash-lite",
    "gemini-3.6-flash",
    "gemini-3.7-flash",
    "gemma-4-31b",
    "glm-5.2",
    "glm-5.3",
    "gpt-5.6-luna",
    "gpt-5.6-sol",
    "gpt-5.6-terra",
    "gpt-oss-20b",
    "grok-4.5",
    "grok-4.6",
    "kimi-k3",
    "minimax-m3",
    "muse-glimmer-30b",
    "nemotron-3.5-lightning",
    "qwen3.6-27b",
    "qwen3.6-35b-a3b",
    "qwen3.6-35b-a3b-classifier",
    "qwen3.8-2.4t-a95b",
    "qwen3.8-27b"
  ],
  "subscription_entry": {
    "model_name": "claude-code-subscription",
    "litellm_params": {
      "model": "anthropic/claude-sonnet-5",
      "api_key": "sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN"
    },
    "model_info": {
      "input_cost_per_token": 0,
      "output_cost_per_token": 0
    }
  },
  "general_settings": {
    "store_model_in_db": false
  },
  "router_settings_fallbacks": [
    {
      "chat-ha": [
        "claude-sonnet-5"
      ]
    },
    {
      "auto": [
        "claude-sonnet-5"
      ]
    }
  ],
  "router_settings_context_window_fallbacks": [
    {
      "chat-ha": [
        "claude-sonnet-5"
      ]
    },
    {
      "auto": [
        "claude-sonnet-5"
      ]
    }
  ]
}
Evidence: Fallback-chain regression transcript (55 passed)
APP_DIR=/Users/coder/.no-mistakes/worktrees/07f90326a876/01M12T2GKHMC18J7G3BP9S8EZK/kubernetes/apps/base/ai/litellm/app
rendered model_list=33 router_settings_keys=['context_window_fallbacks', 'fallbacks', 'redis_host', 'redis_port', 'routing_strategy']
[PASS] router_settings_present_on_proxy_cr — keys=['context_window_fallbacks', 'fallbacks', 'redis_host', 'redis_port', 'routing_strategy']
[PASS] routing_strategy_is_simple_shuffle — got='simple-shuffle'
[PASS] fallbacks_parse_as_primary_to_targets_map — avail={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] context_window_fallbacks_parse_as_map — ctx={'chat-ha': ['claude-sonnet-5'], 'auto': ['claude-sonnet-5']}
[PASS] axes_are_separate_keys — keys=['context_window_fallbacks', 'fallbacks', 'redis_host', 'redis_port', 'routing_strategy']
[PASS] availability_primaries_are_chat_ha_and_auto — primaries=['auto', 'chat-ha']
[PASS] context_window_primaries_are_chat_ha_and_auto — primaries=['auto', 'chat-ha']
[PASS] qwen_local_alias_has_no_availability_fallback — avail_keys=['auto', 'chat-ha']
[PASS] qwen_local_alias_has_no_context_window_fallback — ctx_keys=['auto', 'chat-ha']
[PASS] chat-ha_availability_target_is_sonnet_not_opus — got=['claude-sonnet-5']
[PASS] chat-ha_context_window_target_is_sonnet — got=['claude-sonnet-5']
[PASS] auto_availability_target_is_sonnet_not_opus — got=['claude-sonnet-5']
[PASS] auto_context_window_target_is_sonnet — got=['claude-sonnet-5']
[PASS] no_reverse_cloud_to_local_context_window_chain — cloud_primaries=[]
[PASS] chat_ha_model_cr_present — names=['auto', 'chat-ha', 'claude-code-subscription', 'claude-fable-5', 'claude-opus-4-8', 'claude-opus-5', 'claude-sonnet-5', 'deepseek-v4-flash', 'deepseek-v4-flash-0731', 'deepseek-v4-pro', 'deepseek-v4-pro-0813', 'gemini-3.1-pro', 'gemini-3.5-flash-lite', 'gemini-3.6-flash', 'gemini-3.7-flash', 'gemma-4-31b', 'glm-5.2', 'glm-5.3', 'gpt-5.6-luna', 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-oss-20b', 'grok-4.5', 'grok-4.6', 'kimi-k3', 'minimax-m3', 'muse-glimmer-30b', 'nemotron-3.5-lightning', 'qwen3.6-27b', 'qwen3.6-35b-a3b', 'qwen3.6-35b-a3b-classifier', 'qwen3.8-2.4t-a95b', 'qwen3.8-27b']
[PASS] chat_ha_shares_qwen_backend — ha={'model': 'openai/qwen3.6-35b-a3b', 'api_base': 'http://vllm-app.ai.svc.cluster.local:8000/v1', 'api_key': 'not-needed'} qwen={'model': 'openai/qwen3.6-35b-a3b', 'api_base': 'http://vllm-app.ai.svc.cluster.local:8000/v1', 'api_key': 'not-needed'}
[PASS] chat_ha_has_no_governance_accounting_prices — model_info=None
[PASS] qwen_still_has_governance_accounting_prices — model_info={'input_cost_per_token': 5e-05, 'output_cost_per_token': 0.0001}
[PASS] virtualkeys_include_demo_ha_demo_router_demo — names=['claude-code-subscription', 'demo', 'ha-demo', 'opencode', 'repo-wiki', 'router-demo']
[PASS] demo_is_terminal_local_only — demo.models=['qwen3.6-35b-a3b']
[PASS] ha_demo_holds_only_chat_ha — ha-demo.models=['chat-ha']
[PASS] ha_demo_does_not_hold_terminal_qwen_or_direct_cloud — ha_models=['chat-ha']
[PASS] ha_demo_maxBudget_is_positive_decimal_string — maxBudget='1.0' type=str
[PASS] no_local_only_key_holds_chat_ha — chat_ha_holders=['ha-demo'] leak=[]
[PASS] httproute_file_contains_exactly_one_route — n=1
[PASS] httproute_name_is_litellm_internal_not_proxy_name — name='litellm-internal'
[PASS] httproute_parents_only_envoy_internal_https — parents={('network', 'envoy-internal', 'https')}
[PASS] httproute_never_attaches_envoy_external — parents=[{'name': 'envoy-internal', 'namespace': 'network', 'sectionName': 'https'}]
[PASS] httproute_hostname_uses_flux_secret_domain_token — hostnames=['litellm.${SECRET_DOMAIN}']
[PASS] httproute_has_gatus_endpoint_annotation — gatus='group: ${GATUS_GROUP}\nconditions: ["[STATUS] == 200", "[BODY].status == healthy"]\nurl: "https://litellm.${SECRET_DOMAIN}/health/readiness"'
[PASS] gatus_checks_health_readiness_not_root — gatus='group: ${GATUS_GROUP}\nconditions: ["[STATUS] == 200", "[BODY].status == healthy"]\nurl: "https://litellm.${SECRET_DOMAIN}/health/readiness"'
[PASS] gatus_group_uses_flux_substitution — gatus='group: ${GATUS_GROUP}\nconditions: ["[STATUS] == 200", "[BODY].status == healthy"]\nurl: "https://litellm.${SECRET_DOMAIN}/health/readiness"'
[PASS] httproute_external_dns_target_is_internal — target='internal.${SECRET_DOMAIN}'
[PASS] litellmproxy_has_no_spec_route — spec_keys=['apiAccess', 'applyMode', 'envFrom', 'generalSettings', 'image', 'litellmSettings', 'livenessProbe', 'podAnnotations', 'readinessProbe', 'replicas', 'resources', 'routerSettings', 'service']
[PASS] flux_ks_defines_GATUS_GROUP_ai — substitute={'APP': 'litellm', 'GATUS_GROUP': 'ai'}
[PASS] flux_ks_declares_substituteFrom_cluster_secrets — from=[{'name': 'cluster-secrets', 'kind': 'Secret'}]
[PASS] kubectl_kustomize_app_succeeds — bytes=28420
[PASS] flux_style_envsubst_succeeds — no missing vars
[PASS] no_unresolved_flux_tokens_after_substitute — leftover=[]
[PASS] substituted_hostname_is_litellm_example_test — hostnames=['litellm.example.test']
[PASS] substituted_gatus_group_is_ai — gatus='group: ai\nconditions: ["[STATUS] == 200", "[BODY].status == healthy"]\nurl: "https://litellm.example.test/health/readiness"'
[PASS] prometheusrule_kind — kind=PrometheusRule
[PASS] all_six_phase5_fallback_alerts_present — missing=[] have=['LiteLLMCloudProviderAuthFailing', 'LiteLLMCloudProviderQuotaExhausted', 'LiteLLMContextWindowFallbackFiring', 'LiteLLMDeploymentStuckFailing', 'LiteLLMFallbackChainExhausted', 'LiteLLMSustainedFailover']
[PASS] chain_exhausted_uses_failed_fallbacks_counter — expr='sum by (requested_model, fallback_model) (rate(litellm_deployment_failed_fallbacks_total[10m])) > 0'
[PASS] sustained_failover_excludes_context_window_exceptions — expr='sum by (requested_model, fallback_model) (\n  rate(litellm_deployment_successful_fallbacks_total{exception_class!~".*ContextWindowExceeded.*"}[15m])\n) > 0'
[PASS] context_window_alert_matches_context_window_exceptions — expr='sum by (requested_model, fallback_model) (\n  rate(litellm_deployment_successful_fallbacks_total{exception_class=~".*ContextWindowExceeded.*"}[30m])\n) > 0'
[PASS] stuck_failing_joins_latched_state_with_live_failure_rate — expr='(litellm_deployment_state >= 1)\n  and on (model_id)\n(sum by (model_id) (rate(litellm_deployment_failure_responses_total[15m])) > 0)'
[PASS] auth_alert_scopes_to_anthropic_provider_not_governance_403 — expr='sum by (api_provider, exception_class, exception_status) (\n  rate(litellm_deployment_failure_responses_total{api_provider="anthropic", exception_status=~"401|403"}[15m])\n) > 0'
[PASS] quota_alert_scopes_to_anthropic_429 — expr='sum by (api_provider) (\n  rate(litellm_deployment_failure_responses_total{api_provider="anthropic", exception_status="429"}[15m])\n) > 0'
[PASS] phase5_alerts_reference_verified_fallback_metric_series — missing_floor=[] used=['litellm_deployment_failed_fallbacks_total', 'litellm_deployment_failure_responses_total', 'litellm_deployment_state', 'litellm_deployment_successful_fallbacks_total'] unknown=[]
[PASS] fallbacks_doc_exists — bytes=29007
[PASS] fallbacks_doc_covers_required_topics — missing=[]
[PASS] app_readme_states_internal_route_public_still_forbidden — readme_bytes=17194
[PASS] kustomize_includes_phase5_resources — missing=[]
[PASS] litellm_runtime_available_for_router_proof — litellm not installed here - re-run under ghcr.io/berriai/litellm-non_root:v1.98.0

=== SUMMARY ===
passed=55 failed=0 total=55
wrote /var/folders/yr/h20mxtv56yj1c1pt9tr1_kbc0000gn/T/no-mistakes-evidence/01M12T2GKHMC18J7G3BP9S8EZK/fallback-chain-regression.json
- Evidence: [New focused test script added for this contract](https://github.com/Aviator-Coding/home-ops/blob/a9767da8249ff7f80baff2001517d35254d5b799/scripts/ci/litellm-claude-code-subscription-test.py)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • python3 scripts/ci/litellm-claude-code-subscription-test.py (host: operator render, kustomize consumer, virtual key, ExternalSecret absence, runbook contract; 40/40)
  • podman run --entrypoint python3 ghcr.io/berriai/litellm-non_root:v1.98.0 scripts/ci/litellm-claude-code-subscription-test.py with REQUIRE_LITELLM_RUNTIME=1 (OAuth prefix, get_api_key env fallback trap, explicit $0 custom pricing vs metered sonnet cost 0.007→0.0, Router model_info zeros; 47/47)
  • python3 scripts/ci/litellm-fallback-chain-test.py (regression: fallback chains still only chat-ha/auto→claude-sonnet-5; 55/55)
  • kubectl kustomize kubernetes/apps/base/ai/litellm/app (emits LiteLLMModel/VirtualKey/PushSecret for claude-code-subscription)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Registers `claude-code-subscription`, the one model CR in this repo for
which the proxy holds no credential. A `claude` CLI logged in to a personal
Claude Max/Pro subscription sends its own OAuth token per request; LiteLLM
forwards it to Anthropic so tokens bill that person's flat-rate plan, and
the cluster gains per-request token/latency/virtual-key attribution for
traffic that previously bypassed the proxy entirely.

Deliberately WITHOUT `general_settings.forward_client_headers_to_llm_api`.
The upstream tutorial calls that flag required; on the pinned v1.98.0 image
it is both unnecessary and unsafely scoped:

- It never forwards `Authorization`. Its only effect is
  `_get_forwardable_headers`, an allow-list of `x-*` (minus `x-stainless*`)
  plus `anthropic-beta`.
- The OAuth token travels a separate, ungated path:
  `add_provider_specific_headers_to_request` is called unconditionally at
  litellm_pre_call_utils.py:1704, so the feature works with the flag off.
- The flag is genuinely proxy-global, so setting it would forward every
  client `x-*` header to every backend (xai, zai, openrouter, local
  llama.cpp) for no benefit here.

Live measurements on the running proxy, with the flag absent from the
rendered config, i.e. describing production as it already is:

- Virtual-key governance is not bypassable: the `demo` key is denied
  `claude-sonnet-5` with 403 identically with and without a client
  `Authorization` header, and its allowed model still returns 200
  identically either way.
- Pre-existing finding: a client `sk-ant-oat` token DOES override the
  shared deployment key on Anthropic-family models the caller is already
  entitled to. A fake token sent to `claude-sonnet-5` made Anthropic answer
  "OAuth access token is invalid.", proving our ANTHROPIC_API_KEY was never
  sent. Bounded to that prefix, to anthropic/bedrock/vertex_ai, and to
  callers authenticating via `x-litellm-api-key`; it cannot bypass
  entitlement. It also briefly cools down that model group for all
  consumers.

Two footguns closed, both verified in-pod rather than assumed:

- The CR carries a non-secret placeholder `apiKey`. Omitting it does not
  make the model credential-less: `get_api_key` falls back to
  `os.environ/ANTHROPIC_API_KEY`, which the pod holds, so a caller who
  forgot the OAuth header would silently bill the household's metered
  account. The placeholder forces a clean 401 and zero spend instead.
- Explicit $0 `info.extra` prices, a deliberate exception to this
  directory's no-hand-written-prices rule and the same rule underneath:
  prices must match the invoice, and this invoice is flat-rate, so the
  metered cost map is what would lie. Zero is honoured rather than read as
  unset (`use_custom_pricing_for_model` tests `is not None`). Consequence:
  maxBudget cannot constrain this model, so grant it only with
  rpmLimit/tpmLimit.

Additive: no existing model, virtual key, allow-list, fallback chain or
generalSettings value is modified, and the new model is deliberately absent
from every fallback chain.

Mechanism, full evidence tables and the client runbook (ANTHROPIC_BASE_URL /
ANTHROPIC_MODEL / ANTHROPIC_CUSTOM_HEADERS, getting a virtual key, and the
one-time interactive OAuth login):
docs/ai-system/litellm/claude-code-subscription.md
…in AGENTS.md

Adds item (9) to the litellm entry: a client-supplied sk-ant-oat
Authorization header replaces the deployment apiKey on Anthropic-family
models unconditionally, forward_client_headers_to_llm_api does not gate it
and is deliberately unset, and the two reusable traps that finding exposed
(a keyless model silently falling back to os.environ/ANTHROPIC_API_KEY, and
explicit $0 pricing being honoured but making maxBudget inert).
Captain decision 2026-08-27, the entitlement half of the pass-through model.
Registration is not entitlement, so without this the model was callable by
name but reachable by nobody.

Scoped to `claude-code-subscription` and nothing else: it names neither
`auto` nor the metered `claude-sonnet-5`/`claude-opus-5`, so it cannot
become a second door into budgeted cloud models.

The only key in app/virtualkeys/ with NO maxBudget, deliberately. Its one
model is priced at an explicit $0 because a flat-rate subscription must not
be priced off LiteLLM's metered cost map, so recorded spend is always $0.00
and any budget would be silently inert - a cap that can never trip reads as
protection that does not exist. rpmLimit/tpmLimit are therefore the entire
guardrail and neither should be dropped.

Sized as one interactive CLI user at the same order of magnitude as the
opencode workspace key (8 rpm / 200000 tpm), nudged to 10 / 250000 because
Claude Code's agentic loop spends requests per tool call rather than per
human turn. These are a runaway guardrail, not a throughput target: the real
ceiling is Anthropic's own rate limiting against the caller's personal
subscription, which this proxy neither sees nor can raise.

Pre-flight checks against the live cluster, both clear:
- Server-side dry-run accepted by the CRD and the operator's failurePolicy
  Fail webhook, for the key and its PushSecret.
- No `claude-code-subscription` alias exists in the proxy's Postgres, so the
  operator will mint cleanly rather than hitting the globally-unique-alias
  trap that stalled the O1 cutover.

flux-local: 227 passed.
@mortyops

mortyops Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor
--- kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm LiteLLMModel: ai/claude-code-subscription

+++ kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm LiteLLMModel: ai/claude-code-subscription

@@ -0,0 +1,21 @@

+---
+apiVersion: litellm.home-operations.com/v1alpha1
+kind: LiteLLMModel
+metadata:
+  labels:
+    app.kubernetes.io/name: litellm
+    kustomize.toolkit.fluxcd.io/name: litellm
+    kustomize.toolkit.fluxcd.io/namespace: ai
+  name: claude-code-subscription
+  namespace: ai
+spec:
+  info:
+    extra:
+      input_cost_per_token: 0
+      output_cost_per_token: 0
+  modelName: claude-code-subscription
+  params:
+    apiKey: sk-ant-oat-PLACEHOLDER-CLIENT-SENDS-ITS-OWN-TOKEN
+    model: anthropic/claude-sonnet-5
+  proxyRef: litellm
+
--- kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm LiteLLMVirtualKey: ai/claude-code-subscription

+++ kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm LiteLLMVirtualKey: ai/claude-code-subscription

@@ -0,0 +1,20 @@

+---
+apiVersion: litellm.home-operations.com/v1alpha1
+kind: LiteLLMVirtualKey
+metadata:
+  labels:
+    app.kubernetes.io/name: litellm
+    kustomize.toolkit.fluxcd.io/name: litellm
+    kustomize.toolkit.fluxcd.io/namespace: ai
+  name: claude-code-subscription
+  namespace: ai
+spec:
+  keyAlias: claude-code-subscription
+  models:
+  - claude-code-subscription
+  proxyRef: litellm
+  rpmLimit: 10
+  secretKey: key
+  secretName: litellm-key-claude-code-subscription
+  tpmLimit: 250000
+
--- kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm PushSecret: ai/litellm-key-claude-code-subscription

+++ kubernetes/apps/base/ai/litellm/app Kustomization: ai/litellm PushSecret: ai/litellm-key-claude-code-subscription

@@ -0,0 +1,25 @@

+---
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+  labels:
+    app.kubernetes.io/name: litellm
+    kustomize.toolkit.fluxcd.io/name: litellm
+    kustomize.toolkit.fluxcd.io/namespace: ai
+  name: litellm-key-claude-code-subscription
+  namespace: ai
+spec:
+  data:
+  - match:
+      remoteRef:
+        property: key
+        remoteKey: litellm-consumer-claude-code-subscription
+      secretKey: key
+  refreshInterval: 5m
+  secretStoreRefs:
+  - kind: ClusterSecretStore
+    name: onepassword
+  selector:
+    secret:
+      name: litellm-key-claude-code-subscription
+

@Aviator-Coding
Aviator-Coding merged commit 0068260 into main Aug 28, 2026
18 checks passed
@Aviator-Coding
Aviator-Coding deleted the fm/homeops-litellm-claude-code-max branch August 28, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant