litellm - 💡(How to fix) Fix [Feature]: Key-level tag routing enforcement (allowed_tags or enforced_tags on virtual keys) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#22966Fetched 2026-04-08 00:39:11
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
2
Participants
Timeline (top)
labeled ×3renamed ×1

Code Example

model_list:
  - model_name: gpt-4
    litellm_params:
      model: azure/gpt-4
      api_base: https://my-eu-endpoint.openai.azure.com/
      api_key: os.environ/AZURE_EU_KEY
      tags: ["region:eu"]

  - model_name: gpt-4
    litellm_params:
      model: azure/gpt-4
      api_base: https://my-us-endpoint.openai.azure.com/
      api_key: os.environ/AZURE_US_KEY
      tags: ["region:us"]

router_settings:
  enable_tag_filtering: True

---

curl -X POST http://0.0.0.0:4000/key/generate \
  -H "Authorization: Bearer sk-1234" \
  -H "Content-Type: application/json" \
  -d '{"allowed_tags": ["region:eu"]}'
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

The Feature

Add an allowed_tags field to virtual keys that enforces tag-based deployment routing at the key level, independently of what tags the caller sends in the request body.

When allowed_tags is set on a key, requests using that key are validated against that whitelist before routing. If the caller sends tags not present in allowed_tags, the request is rejected with a 401. If the caller sends no tags, the allowed_tags are injected as the default. This ensures a key scoped to ["region:eu"] can never - accidentally or deliberately - route to a non-EU deployment.

An alternative simpler design would be enforced_tags, where the key always forces specific tags regardless of what the caller sends. allowed_tags is more flexible and covers a broader set of use cases, which is why I explored it here - but I'm open to discussing which approach makes more sense.

Note that this is also distinct from the existing metadata.tags field on keys, which is used solely for spend tracking. allowed_tags would operate at the routing layer, not the logging layer.

Example of config:

model_list:
  - model_name: gpt-4
    litellm_params:
      model: azure/gpt-4
      api_base: https://my-eu-endpoint.openai.azure.com/
      api_key: os.environ/AZURE_EU_KEY
      tags: ["region:eu"]

  - model_name: gpt-4
    litellm_params:
      model: azure/gpt-4
      api_base: https://my-us-endpoint.openai.azure.com/
      api_key: os.environ/AZURE_US_KEY
      tags: ["region:us"]

router_settings:
  enable_tag_filtering: True

And key generation:

curl -X POST http://0.0.0.0:4000/key/generate \
  -H "Authorization: Bearer sk-1234" \
  -H "Content-Type: application/json" \
  -d '{"allowed_tags": ["region:eu"]}'

Any request using this key that tries to route to region:us will be rejected. A request that sends no tags will automatically be routed to region:eu.

Motivation, pitch

As you can see from my examples above, he main use case is data residency enforcement - for example, ensuring a given API key can only route to EU-hosted endpoints for GDPR compliance, without relying on the caller to correctly pass the right tags on every request.

Currently, tag-based routing is caller-controlled: the client passes tags in the request body or via x-litellm-tags, and the router uses them. This means there is no way to enforce routing constraints at the key level in the open-source version - a caller can pass any tag (or none) and bypass any intended regional restriction.

The reject_clientside_metadata_tags setting partially addresses this but only blocks client tags, it doesn't enforce key-level routing.

The team-based tag routing that solves this is enterprise-only. allowed_tags on keys would be a lighter-weight, open-source-friendly alternative for self-hosted deployments with compliance requirements.

Implementation notes (happy to open a PR)

I took a quick look at the codebase and I think the change is contained to ~3 files with ~50-80 lines added (not counting added tests ofc), following existing patterns:

  • litellm/proxy/_types.py: add allowed_tags: Optional[List[str]] = None to LiteLLM_VerificationToken and GenerateKeyRequest
  • litellm/proxy/litellm_pre_call_utils.py: validate caller tags against valid_token.allowed_tags, reject with 401 if they fall outside the whitelist, inject allowed_tags if caller sends none.
  • schema.prisma: add allowedTags String[] column to LiteLLMVerificationToken (for DB-backed deployments)
  • ui/litellm-dashboard/src/components/: add an allowed_tags multi-select input to the key creation/edit form, following the same pattern as the existing guardrails field (populated from available deployment tags)

I'm willing to implement and submit a PR if the team is open to it - happy to discuss the approach first !

What part of LiteLLM is this about?

Proxy

LiteLLM is hiring a founding backend engineer, are you interested in joining us and shipping to all our users?

No

Twitter / LinkedIn details

https://www.linkedin.com/in/milo-parigi/

extent analysis

Fix Plan

To implement the allowed_tags feature, follow these steps:

  • Update litellm/proxy/_types.py:
    • Add allowed_tags: Optional[List[str]] = None to LiteLLM_VerificationToken and GenerateKeyRequest
    • Example:

from typing import Optional, List

class LiteLLM_VerificationToken: # ... allowed_tags: Optional[List[str]] = None

class GenerateKeyRequest: # ... allowed_tags: Optional[List[str]] = None


* **Update `litellm/proxy/litellm_pre_call_utils.py`**:
  * Validate caller tags against `valid_token.allowed_tags`
  * Reject with 401 if tags fall outside the whitelist
  * Inject `allowed_tags` if caller sends none
  * Example:
  ```python
def validate_tags(valid_token, caller_tags):
    if valid_token.allowed_tags and not set(caller_tags).issubset(set(valid_token.allowed_tags)):
        raise Exception("Invalid tags")
    if not caller_tags and valid_token.allowed_tags:
        return valid_token.allowed_tags
    return caller_tags
  • Update schema.prisma:
    • Add allowedTags String[] column to LiteLLMVerificationToken
    • Example:

model LiteLLMVerificationToken {

...

allowedTags String[] }


* **Update `ui/litellm-dashboard/src/components/`**:
  * Add an `allowed_tags` multi-select input to the key creation/edit form
  * Populate from available deployment tags

### Verification
To verify the fix, test the following scenarios:

* Create a key with `allowed_tags` set to `["region:eu"]`
* Send a request with the key and tags `["region:us"]` - it should be rejected with a 401
* Send a request with the key and no tags - it should be routed to `region:eu`

### Extra Tips
* Make sure to update the documentation to reflect the new `allowed_tags` feature
* Consider adding tests to cover the new validation logic
* Review the code changes to ensure they follow the existing patterns and coding standards

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING