Gateway
Geltungsbereich: namespaced · Workload: LiteLLM über den litellm-operator oder Wäg über den waeg-operator
Das KI-Gateway — die Eingangstür für Modell-Traffic, mit Modellen, Teams, Organisationen, Budgets und optionalem Langfuse-Trace-Export.
spec.type wählt die Implementierung aus. Der Standardwert ist litellm, daher beschreibt der Rest dieser Seite das LiteLLM-Gateway, sofern nicht anders angegeben; die Unterschiede finden Sie unter Wäg-Gateway (type: waeg).
Spec
| Feld | Typ | Beschreibung |
|---|---|---|
type | litellm | waeg | Gateway-Implementierung. Standard litellm. |
waeg | object | Ausschließlich Wäg-spezifische Verdrahtung (Topologie, ClickHouse-/Redis-Ebenen, OpenFGA). Erforderlich bei type: waeg |
secretsRef | LocalRef (optional) | SecretsManagement im selben Namespace, auf die gewartet wird. Weglassen, um selbst verwaltete, gewöhnliche Kubernetes-Secrets zu verwenden — siehe secretsRef |
database | object (erforderlich) | Wo LiteLLM seinen Zustand speichert |
instance | object | Image/Tag, Replicas, Ressourcen, Master-/Salt-Keys, SSO |
organization | object | LiteLLM-Organisation + Budgets |
teams[] | list | Teams mit teamspezifischen Budgets |
models[] | list | Der Modellkatalog |
guardrailRefs | []ObjectRef | Guardrail-CRs, die in dieses Gateway verdrahtet werden (lizenziert guardrail; als LiteLLMGuardrail über den Lizenz-Gate-Proxy jedes Guardrails emittiert) |
observabilityRef | ObjectRef | Ein Observability für den Trace-Export (auto-wired, sofern lizenziert) |
observability | object | Manueller Fallback für den Langfuse-Callback |
sso | object | OIDC/SSO-Login für die LiteLLM-Admin-UI (siehe SSO) |
enableEntraSSO | bool | Veraltet — verwenden Sie sso mit provider: azure-entra |
database
| Feld | Beschreibung |
|---|---|
mode | postgresCluster (Verweis auf ein PostgresCluster) oder external |
postgresClusterRef | Der zu verwendende Cluster (Modus postgresCluster) |
databaseName | Datenbank innerhalb des gemeinsam genutzten Clusters |
connectionSecretRef | DATABASE_URL-Secret (Modus external) |
models[]
| Feld | Beschreibung |
|---|---|
name / modelName / model | Anzeigename, LiteLLM-Modellname und Provider-Modell-ID |
refSecretKey | Schlüssel im Modell-Anmeldedaten-Secret, der den API-Key enthält |
credentials.apiBase | Provider-Endpunkt, für jeden Provider, der einen expliziten benötigt (Azure OpenAI / AI Foundry, selbst gehostet, ein Proxy) |
credentials.apiVersion | Provider-API-Version, wenn der Provider eine benötigt (z. B. Azure OpenAI / AI Foundry) |
rpm / tpm / timeout / maxTokens | Modellspezifische Limits (nur LiteLLM) |
provider / providerName / modelId / fallbacks / weight | Ausschließlich Wäg-Katalogfelder — siehe Wäg-Gateway |
Wenn ein Modell einen eigenen Endpunkt und/oder eine API-Version benötigt, setzen Sie diese unter credentials — der Operator reicht sie zusammen mit dem API-Key aus refSecretKey an das Gateway weiter. Lassen Sie apiBase den reinen Endpunkt und tragen Sie die Version in apiVersion ein (kein ?api-version= in der URL). Das ist providerunabhängig: Der Operator gibt aus, was Sie angeben, und behandelt keinen Provider gesondert.
instance.healthCheck
Steuert die Hintergrund-Health-Checks der Modelle in LiteLLM. Standardmäßig deaktiviert: GET /health prüft die Modelle bei Bedarf, statt eine Hintergrundschleife auszuführen. Hintergrund-Checks erzeugen periodischen Upstream-Verkehr und können bei manchen Providern Kosten verursachen oder Rate-Limits auslösen — aktivieren Sie sie also nur, wenn /health zwischengespeicherte Ergebnisse liefern soll.
| Feld | Typ | Beschreibung |
|---|---|---|
enabled | bool | Hintergrund-Health-Checks einschalten. Standard false (deaktiviert). |
intervalSeconds | int | Sekunden zwischen den Checks (LiteLLM-Standard 300). Gilt nur bei enabled: true. |
instance:
healthCheck:
enabled: true
intervalSeconds: 300Wird auf generalSettings.backgroundHealthChecks / healthCheckInterval der LiteLLMInstance abgebildet. Bei deaktiviertem Zustand setzt der Operator explizit backgroundHealthChecks: false.
Was es emittiert
Der Controller löst die Datenbank auf, stellt den litellm-operator sicher (wartet, bis dessen CRDs Established sind) und emittiert anschließend in folgender Reihenfolge:
LiteLLMInstance → LiteLLMOrganization → LiteLLMTeam(s)
→ LiteLLMCredential(s) → LiteLLMModel(s)(Alle in der API-Gruppe litellm.palena.ai/v1alpha1.)
Datenbank-Verdrahtung
postgresCluster— der Operator löst den referenzierten Cluster auf, bereitet dielitellm-Datenbank und -Rolle bereit, generiert das Passwort und injiziert das Anmeldedaten-Secret. Es ist kein manuelles Secret erforderlich.external— geben Sie eineconnectionSecretRefan, die eineDATABASE_URLenthält.
Langfuse-Trace-Export
- Mit dem Feature
auto-wiringund einerobservabilityRefverdrahtet der Operator den Langfuse-Callback von LiteLLM (Host + Public-/Secret-Keys) aus dem referenziertenObservability, sodass Traces automatisch exportiert werden. - Ohne dieses Feature (oder gegen ein Community-Langfuse, das keine Projektschlüssel ausstellen kann) erstellen Sie das Projekt + den Schlüssel in der Langfuse-UI und setzen
spec.observability.{host, callbackSecretRef}(SchlüsselpublicKey/secretKey).
Siehe Auto-Wiring für die beiden beteiligten Gates.
SSO / OIDC login
spec.sso aktiviert Single Sign-On für die LiteLLM-Admin-UI und wird in den Block LiteLLMInstance.spec.sso übersetzt. Die OAuth-Client-Anmeldedaten stammen aus einem Secret im selben Namespace (über SecretsManagement — niemals inline).
| Feld | Beschreibung |
|---|---|
issuerURL | OIDC-Issuer-/Discovery-Basis-URL |
clientSecretRef.name | Secret mit dem OAuth-Client (Schlüssel standardmäßig client-id / client-secret, überschreibbar mit clientIDKey / clientSecretKey) |
provider | generic-oidc (Standard), azure-entra, google oder okta |
tenantID | Directory-/Tenant-ID (für azure-entra) |
authorizationEndpoint / tokenEndpoint / userinfoEndpoint | Explizite Endpunkte — erforderlich für generic-oidc (LiteLLM führt keine Discovery durch) |
scopes | Angeforderte Scopes (Standard openid, profile, email) |
providerName | Anzeigebezeichnung |
spec:
sso:
provider: generic-oidc
issuerURL: https://idp.example.com
authorizationEndpoint: https://idp.example.com/authorize
tokenEndpoint: https://idp.example.com/token
userinfoEndpoint: https://idp.example.com/userinfo
clientSecretRef: { name: gateway-oidc }Lizenzierung
LiteLLM-SSO ist für bis zu 5 Benutzer kostenlos; vollständiges/unbegrenztes SSO erfordert eine LiteLLM-Enterprise-Lizenz. Der Navique-Operator schränkt das Feld selbst nicht ein.
Redirect-URL bei öffentlichem Zugriff
LiteLLM bildet die OAuth-redirect_uri aus PROXY_BASE_URL, die aus dem ingress des Gateways abgeleitet wird. Für SSO über öffentlichen Zugriff muss das Gateway unter seinem öffentlichen Host mit TLS bereitgestellt werden — spec.ingress.enabled: true, spec.ingress.host: <öffentlicher-host> und spec.ingress.tls: true — sodass der Callback zu https://<öffentlicher-host>/sso/callback auflöst. Registrieren Sie genau diese URL bei Ihrem IdP. Ohne ingress.tls: true fällt der Redirect auf eine reine http-/clusterinterne Adresse zurück und der OAuth-Callback schlägt fehl. (status.endpoint der zugrunde liegenden LiteLLMInstance zeigt immer die clusterinterne .svc-URL — das ist die Admin-API-Adresse des Operators selbst, nicht die Basis des SSO-Redirects.)
Das veraltete Boolean enableEntraSSO: true funktioniert weiterhin, solange sso nicht gesetzt ist — es synthetisiert eine azure-entra-Konfiguration, die das althergebrachte Secret entra-sso-credentials liest. Bevorzugen Sie sso mit provider: azure-entra.
JWT-API-Authentifizierung & RBAC
spec.instance.jwtAuth aktiviert die JWT-basierte API-Authentifizierung: LiteLLM prüft bei jeder Anfrage ein Bearer-JWT Ihres IdP und bildet dessen Claims auf Rollen/Teams ab (getrennt von sso, dem Browser-Login der Admin-UI). Kombinieren Sie es mit spec.instance.rolePermissions, um einzuschränken, welche Modelle eine Rolle aufrufen darf.
| Feld | Abbildung auf (litellm_jwtauth) |
|---|---|
jwtAuth.enabled | general_settings.enable_jwt_auth |
jwtAuth.publicKeyURL | JWKS-Endpunkt (JWT_PUBLIC_KEY_URL) — erforderlich: ohne ihn kann LiteLLM kein Token validieren |
jwtAuth.issuer | erwarteter Token-Aussteller iss (JWT_ISSUER) |
jwtAuth.audience | erwartete Token-Zielgruppe aud (JWT_AUDIENCE) |
jwtAuth.userRolesJWTField | user_roles_jwt_field — JWT-Claim mit der Rollenliste |
jwtAuth.userAllowedRoles | user_allowed_roles — Rollen, die auf internal_user abbilden |
jwtAuth.enforceRBAC | enforce_rbac — Aufrufer mit unzulässigen Rollen ablehnen |
jwtAuth.userRoleJWTField | user_role_jwt_field (einzelne Rolle) |
jwtAuth.userIDJWTField | user_id_jwt_field — Claim, der als Benutzer-ID dient (sub / oid / preferred_username) |
jwtAuth.userIDUpsert | user_id_upsert — legt den LiteLLM-Benutzer beim ersten Login automatisch an |
jwtAuth.teamIDsJWTField | team_ids_jwt_field |
jwtAuth.adminJWTScope | admin_jwt_scope |
rolePermissions.<rolle>.models / .routes | general_settings.role_permissions |
spec:
instance:
licenseSecretRef: { name: litellm-enterprise-license, key: license }
jwtAuth:
enabled: true
publicKeyURL: https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys # ERFORDERLICH (JWKS)
issuer: https://login.microsoftonline.com/<tenant>/v2.0
audience: <client-id>
userIDJWTField: sub # Claim, der den Benutzer identifiziert (sub / oid / preferred_username)
userIDUpsert: true # legt den LiteLLM-Benutzer beim ersten Login automatisch an
userRolesJWTField: roles
userAllowedRoles: ["basic_user"]
enforceRBAC: true
rolePermissions:
internal_user:
models: ["anthropic-claude"]Auf einem Wäg-Gateway schaltet derselbe jwtAuth-Block Wägs eigenes JWT-Modul für die Datenebene ein; die Wäg-spezifischen Einstellungen liegen unter spec.waeg.jwt — siehe JWT-Authentifizierung auf der Datenebene.
Nur Enterprise
enable_jwt_auth, enforce_rbac und role_permissions sind LiteLLM-Enterprise- Funktionen — setzen Sie instance.licenseSecretRef auf eine gültige LiteLLM- Enterprise-Lizenz, sonst bleiben sie wirkungslos. Ein gesetzter rolePermissions- Eintrag aktiviert general_settings.enforce_rbac, damit die Beschränkungen greifen.
Beispiel
apiVersion: core.navique.com/v1alpha1
kind: Gateway
metadata:
name: gateway
namespace: forge-gateway
spec:
secretsRef: { name: forge-secrets }
database:
mode: postgresCluster
postgresClusterRef: { name: forge-pg, namespace: forge-data }
databaseName: litellm
instance:
image: { repository: ghcr.io/berriai/litellm, tag: v1.86.1 }
replicas: 1
masterKey: { autoGenerate: true }
saltKey: { autoGenerate: true }
observabilityRef: { name: observability, namespace: forge-langfuse }
organization:
name: navique-ag
maxBudget: 2000
budgetDuration: 30d
rpmLimit: 1000
tpmLimit: 200000
teams:
- { name: data-engineering, maxBudgetMonthly: 1000, budgetDuration: 30d }
models:
- name: gpt-5.4
modelName: gpt-5.4
model: azure/gpt-5.4 # Provider-/Deployment-ID (hier: ein Azure-OpenAI-Deployment)
refSecretKey: OPENAI_API_KEY
rpm: 300
tpm: 80000
timeout: 120
credentials:
apiBase: "https://forge-foundry.cognitiveservices.azure.com"
apiVersion: "2024-10-21"Wäg-Gateway (type: waeg)
Wäg ist ein alternatives KI-Gateway, betrieben vom mitgelieferten waeg-operator (gateway.waeg.ai). Es ist ein anderes Produkt, kein LiteLLM-Klon, und diese CRD tut nicht so, als wäre es eines: alles Folgende ist ein echter Unterschied, und alles, was Wäg nicht kann, wird gemeldet — niemals stillschweigend verworfen.
Was sich ändert
| Aspekt | LiteLLM | Wäg |
|---|---|---|
| Speicher | ein Postgres (+ optional Redis) | standardmäßig zwei Ebenen (storageMode: split): Postgres und ClickHouse. storageMode: single hält die Analysedaten im Postgres der Steuerungsebene und braucht kein ClickHouse |
| Redis | optional | erforderlich, sobald die API mehr als ein Replica bedient (HA-Kontingente) |
| Autorisierung | Proxy-Rollen / virtuelle Keys | OpenFGA — extern oder ein Postgres-gestütztes, das der Operator ausrollt |
| Prozessmodell | ein Proxy-Deployment | AllInOne oder Split (api + optionaler worker) |
| Katalog | ein Modellobjekt | Provider (Verbindung + Credential) und Modell-Alias, der darauf verweist |
| Budgets | an den Org-/Team-Objekten | separate organisationsverankerte Budget-Richtlinien |
spec.waeg
| Feld | Beschreibung |
|---|---|
topology | AllInOne (Standard) oder Split (*-api plus optionales *-worker-Deployment) |
jobWorkers | In-Process-Worker für dauerhafte Jobs (nur AllInOne) |
worker | Das dedizierte Worker-Deployment (enabled, replicas, jobWorkers, resources) — erfordert topology: Split |
storageMode | Wo die Analysedaten liegen: split (Standard — ClickHouse) oder single (Postgres der Steuerungsebene, ganz ohne ClickHouse) — siehe Analyse-Speichermodus |
clickhouse | Die Analyse-Ebene — erforderlich bei storageMode: split, bei single ignoriert und nicht erforderlich: mode: ref auf ein ClickHouseCluster oder mode: external mit einem Verbindungs-Secret |
clickhouseDatabase | Die ClickHouse-Datenbank, in die im split-Modus geschrieben wird. Standard waeg; der Operator legt sie auf einem managed- oder adopt-Cluster an |
redis | Der HA-Kontingentspeicher: mode: ref auf eine RedisInstance oder external. Erforderlich bei mehr als einem API-Replica |
openfga | Externe apiUrl / apiUrlSecretRef — oder weglassen, dann rollt der Operator ein Postgres-gestütztes OpenFGA aus (image, replicas, resources, storeId, modelId) |
autoscaling | API-HPA auf CPU/Speicher (enabled, minReplicas, maxReplicas, Zielwerte). Skalierung nach Warteschlangentiefe benötigt KEDA und ist nicht verdrahtet |
artifacts | Volume für Jobs/Medien: emptyDir (Standard), pvc (ein bestehender Claim) oder none |
bootstrapAdmin | Legt den ersten Konsolen-Admin aus einem Secret an (secretRef, emailKey, passwordKey) |
dataEncryptionKeySecretRef | Der Schlüssel, mit dem Wäg Konfigurationsrevisionen versiegelt (Wägs Gegenstück zum Salt-Key von LiteLLM) |
configYAML | Überschreibt den waeg.yaml-Bootstrap-Seed. Nur nicht-geheime Werte — der Inhalt landet in einer ConfigMap |
brandingConfigMapRef | Ihr eigenes Enterprise-Theme-Pack (JSON), das der Operator nach Erreichen von Ready an die Admin-API sendet. Ersetzt das eingebaute Navique-Pack — lizenziertes Feature custom-branding; siehe Konsolen-Branding |
defaultBranding | Wendet das eingebaute Navique-Theme-Pack an, wenn das Gateway Enterprise-lizenziert ist und kein brandingConfigMapRef gesetzt ist. Standard true |
modelAccess | Die organisationsverankerte Modell-ACL (openByDefault, fallbackMode, grants[]). Erfordert spec.organization |
scim | SCIM-v2-Benutzerbereitstellung (enabled, tokenSecretRef, defaultRole, defaultOrgID, orgSource, roleMap, orgMap) — siehe SCIM-Bereitstellung |
jwt | Die Wäg-spezifischen JWT-Einstellungen der Datenebene (appClaim, appClaimFallbacks, tenantClaim, requireRegisteredApplication, allowMasterKey, allowVirtualKeys, insecureSkipVerify) — siehe JWT-Authentifizierung auf der Datenebene |
Postgres kommt weiterhin aus spec.database — dasselbe Feld, identisch aufgelöst. Der Operator fasst alle Verbindungszeichenfolgen in einem eigenen Secret <gateway>-waeg-storage zusammen und referenziert es per Schlüssel, sodass nie eine Zugangsdaten-URL in der CR landet.
Analyse-Speichermodus
Wäg hält seine Steuerungsebene (Konfiguration, Keys, Organisationen, Teams, Anwendungen) in Postgres und seine Analyse-Ebene (Request-Logs, Nutzung, Kosten) in einem Speicher, den spec.waeg.storageMode auswählt:
| Modus | Analysedaten liegen in | ClickHouse nötig |
|---|---|---|
split (Standard) | ClickHouse — spec.waeg.clickhouse muss darauf zeigen | ja |
single | demselben Postgres wie die Steuerungsebene (spec.database) | nein |
split ist der Standard dieses Operators: es ist das, was das Gateway schon immer emittiert hat, und es trägt bei hohem Volumen. Wägs eigener Upstream- Standard ist single und gilt dort als empfohlener Einstieg — genau dafür gibt es dieses Feld. Für jedes Gateway ein ClickHouse zu verlangen erzwang eine Abhängigkeit, die Upstream längst fallen gelassen hatte; für eine kleine oder evaluierende Installation verdoppelt sie die Datenspeicher, die Sie betreiben, dimensionieren und sichern müssen — ohne Gegenwert.
Mit single enthält die emittierte WaegInstance überhaupt keinen clickhouse-Block; spec.waeg.clickhouse wird ignoriert und ist nicht erforderlich.
Ein Moduswechsel migriert die Analysehistorie nicht
Die beiden Ebenen sind getrennte Speicher. Ein geänderter storageMode an einem laufenden Gateway zeigt auf den jeweils anderen — die bereits geschriebenen Analysedaten bleiben, wo sie sind, und sind in der Konsole nicht mehr sichtbar. Weder der Operator noch Wäg kopiert sie hinüber. Entscheiden Sie sich für einen Modus, bevor Sie Daten sammeln, die Ihnen wichtig sind — oder exportieren Sie sie vorher.
single — nur Postgres
apiVersion: core.navique.com/v1alpha1
kind: Gateway
metadata:
name: waeg-gateway
namespace: forge-gateway
spec:
type: waeg
secretsRef: { name: forge-secrets }
database:
mode: postgresCluster
postgresClusterRef: { name: forge-pg, namespace: forge-data }
databaseName: waeg
instance:
replicas: 1
masterKey: { autoGenerate: true }
waeg:
storageMode: single # Analysedaten landen in forge-pg, neben der Steuerungsebene
# kein clickhouse-Block — keiner wird gebraucht, und einer hier würde ignoriertsplit — Analysedaten in ClickHouse
spec:
type: waeg
secretsRef: { name: forge-secrets }
database:
mode: postgresCluster
postgresClusterRef: { name: forge-pg, namespace: forge-data }
databaseName: waeg
instance:
replicas: 2
masterKey: { autoGenerate: true }
waeg:
storageMode: split # der Standard; kann weggelassen werden
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
clickhouseDatabase: waeg # der Standard
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }Wäg legt nur seine Tabellen an, niemals seine Datenbank. Auf einem managed- oder adopt-ClickHouseCluster deklariert der Operator diese Datenbank unter spec.databases und legt sie für Sie an; auf einem external-Cluster müssen Sie sie selbst anlegen, sonst beendet sich das Gateway beim Start mit Database waeg does not exist.
Modelle: Provider + Alias
Wägs Katalog trennt die Upstream-Verbindung vom clientseitigen Alias, daher emittiert jeder Eintrag in spec.models sowohl einen WaegProvider als auch ein WaegModel. Modelle mit demselben Treiber teilen sich einen Provider.
| Feld | Beschreibung |
|---|---|
provider | Provider-Treiber (openai, azure, anthropic, …). Standard ist das Präfix von model (azure/gpt-4o → azure), sonst openai |
providerName | Benennt den Provider-Katalogeintrag, sodass mehrere Modelle eine Verbindung teilen können. Standard ist der Treiber |
modelId | Providerseitige Modell-ID. Standard ist model ohne Präfix (azure/gpt-4o → gpt-4o) |
fallbacks | Modell-Aliase, die bei einem Fehler versucht werden |
weight | Gewichtet dieses Deployment gegenüber anderen desselben Alias |
Was es emittiert
WaegInstance → WaegOrganization → WaegBudget (Org-Limits) → WaegTeam(s)
→ WaegProvider(s) → WaegModel(s) → WaegModelAccess(Alle in der API-Gruppe gateway.waeg.ai/v1alpha1.)
Enterprise-Lizenz
spec.instance.licenseSecretRef trägt die Wäg-Enterprise-Lizenz des Gateways. Sie erreicht die Instanz als WaegInstance.spec.secrets.licenseKey; der Secret-Schlüssel ist standardmäßig license.
Jedes Wäg-Enterprise-Modul hängt daran — SSO, SCIM, Audit, CMEK, FIPS und Branding gleichermaßen — und die Durchsetzung ist bedingungslos: ein Gateway-Image mit gelinktem EE-Binary, aber ohne Lizenz, antwortet mit 402 license_required. Der Enterprise-Build allein genügt also nicht; meldet ein Modul LicenseRequired, fehlt genau dieses Feld.
spec:
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }Die Lizenz wird referenziert, nie eingebettet: lassen Sie Ihr SecretsManagement-Backend das Secret materialisieren (ESO aus dem Key Vault oder ein SealedSecret) wie jedes andere Credential. In einem Stack wird sie einmalig als spec.gateway.licenseSecretRef deklariert und an das Gateway durchgereicht.
SSO und Trace-Export
Beides funktioniert — über andere Wege als bei LiteLLM.
SSO (spec.sso) wird über eine WaegEnterpriseConfig-CR angewendet, die Wägs Enterprise-Module über dessen Admin-API konfiguriert statt über Instanz-Umgebungsvariablen. Der Operator liest die Client-ID aus Ihrem Secret und setzt sie als einfaches Feld (eine Client-ID ist konstruktionsbedingt öffentlich — sie steht in der Authorize-URL im Browser), während das Client-Secret eine Secret-Referenz bleibt und als WAEG_EE_OIDC_CLIENT_SECRET auf den Pod projiziert wird. Das ist Absicht: die CR könnte das Secret auch in den versiegelten Config-Store des Gateways schreiben, womit es in dessen Config-Revisionen läge statt in Kubernetes.
Die Redirect-URI wird als <öffentlicher Origin>/waeg/ui/v1/ee/sso/callback gebildet — das Gateway lehnt jeden Wert ab, der diesen Pfad nicht enthält. SSO benötigt daher spec.ingress mit einem Host; fehlt er, warnt der Operator, statt einen Callback zu erzeugen, der nie aufgelöst werden kann. Registrieren Sie genau diese URL bei Ihrem IdP.
Erfordert den Enterprise-Build und eine Lizenz; die CR meldet pro Modul EnterpriseNotLinked bzw. LicenseRequired, falls eines davon fehlt.
Trace-Export (observabilityRef / observability) wird auf WaegInstance.spec.observability.langfuse abgebildet, wobei die Projekt-Keys als Secret-Referenz übergeben werden. Umgebungsvariablen sind der einzige deklarative Weg des Gateways — es liest seine Konfigurationsdatei nur beim ersten Start und danach gewinnt die Konsole — daher wird die Senke einmal beim Start gebaut und eine Key-Änderung rollt die Pods. Die Auflösungsreihenfolge ist wie bei LiteLLM: zuerst manuelle Keys, dann lizenziertes Auto-Wiring aus einer referenzierten Observability.
Erfordert ein aktuelles Gateway
Die WAEG_LANGFUSE_*-Variablen kamen nach Gateway 1.0.0-rc.7. Ein älteres Image meldet LangfuseRequiresNewerGateway, statt eine Einstellung anzunehmen, die nie wirksam würde.
SCIM-Bereitstellung
spec.waeg.scim aktiviert Wägs SCIM-v2-Endpunkte, sodass Ihr IdP Konsolenbenutzer direkt anlegt, aktualisiert und wieder entfernt, statt dass das jemand von Hand tut. SCIM ist unabhängig von spec.sso: es funktioniert auch auf einem Gateway, für das überhaupt kein interaktives Login konfiguriert ist — die übliche Form einer kopflosen Bereitstellungsintegration.
| Feld | Beschreibung |
|---|---|
enabled | Schaltet die Endpunkte ein. Standard true; ausgeschaltet antworten sie mit 404 |
tokenSecretRef | Erforderlich. Das Bearer-Token, das Ihr IdP vorlegt. Schlüssel standardmäßig scim-token |
defaultRole | Konsolenrolle für neu bereitgestellte Benutzer: viewer, operator oder admin |
defaultOrgID | Wäg-Organisation, in der bereitgestellte Benutzer landen. Standard ist die eigene Organisation des Gateways, sofern spec.organization gesetzt ist |
orgSource | Woher die Organisation eines bereitgestellten Benutzers kommt: waeg (das Gateway entscheidet), enterprise (aus der IdP-Nutzlast) oder none |
roleMap | IdP-Gruppenname → Wäg-Konsolenrolle. Wird als vollständige Ersetzung der Map gesendet |
orgMap | IdP-Wert → Wäg-Organisations-ID. Ebenfalls vollständige Ersetzung |
roleMap und orgMap werden bei jedem Apply vollständig ersetzt statt zusammengeführt. Das Gateway trägt also exakt das, was hier deklariert ist — ein entfernter Eintrag entfernt die Zuordnung.
defaultOrgID kann entfallen, wenn das Gateway eine organization hat: Benutzer landen dann dort statt organisationslos. Das ist wichtig, denn ein Benutzer ohne Organisation verfehlt jede organisationsverankerte Modell-Berechtigung.
Die Endpunkte liegen unter /waeg/admin/v1/ee/scim/v2:
/waeg/admin/v1/ee/scim/v2/Users
/waeg/admin/v1/ee/scim/v2/Groups
/waeg/admin/v1/ee/scim/v2/ServiceProviderConfigRichten Sie den SCIM-Konnektor Ihres IdP auf diese Basis-URL und hinterlegen Sie das Bearer-Token aus tokenSecretRef. Ohne Token antwortet jeder Aufruf mit 401.
Das Token ist eine Umgebungsvariable, kein gespeicherter Konfigurationswert
Der Operator projiziert das Token als WAEG_EE_SCIM_TOKEN auf die Gateway-Pods, statt es in den versiegelten Config-Store des Gateways zu schreiben. Dieser Store lässt sich nie wieder auslesen — eine dort abgelegte Kopie würde beim Rotieren des Secrets still veralten. Als Umgebungsvariable besteht die gesamte Rotation darin, das Secret zu erneuern und die Pods neu starten zu lassen.
Erfordert die Enterprise-Lizenz des Gateways — ohne sie antwortet das Modul mit 402 — und sonst nichts.
Nicht das Plattform-Feature sso-scim
Die AI-Core-Lizenz kennt ein eigenes Feature sso-scim. Dabei handelt es sich um eine separate, noch nicht implementierte Plattformfunktion, die mit dem hier beschriebenen Gateway-Modul nichts zu tun hat.
spec:
organization: { name: navique-ag }
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }
waeg:
scim:
enabled: true
tokenSecretRef: { name: waeg-scim-token, key: scim-token }
defaultRole: viewer
orgSource: waeg
roleMap:
"AI Platform Admins": admin
"AI Platform Users": viewerJWT-Authentifizierung auf der Datenebene
Wäg kann bei Datenebenen-Anfragen ein Bearer-JWT Ihres IdP validieren, sodass Aufrufer ein Token statt eines Schlüssels — oder zusätzlich dazu — vorlegen. Eingeschaltet wird das über den gemeinsamen Block spec.instance.jwtAuth (dasselbe Feld, das auch LiteLLM verwendet); feinjustiert wird es über das Wäg-spezifische spec.waeg.jwt.
spec.instance.jwtAuth | Wägs EE-Modul jwt |
|---|---|
enabled | enabled |
publicKeyURL | jwksUrl — der JWKS-Endpunkt, von dem die Signaturschlüssel geladen werden |
issuer | issuer — der erwartete iss |
audience | audience — die erwartete aud |
userIDJWTField | subjectClaim — welcher Claim den Aufrufer identifiziert |
spec.waeg.jwt | Beschreibung |
|---|---|
appClaim | Claim, der die aufrufende Anwendung identifiziert. Standard azp |
appClaimFallbacks | Claims, die der Reihe nach probiert werden, wenn appClaim fehlt. Vollständige Ersetzung der Liste — eine leere Liste löscht Wägs eigene Standards (appid, client_id) |
tenantClaim | Claim mit Organisation/Mandant (sonst fällt Wäg auf tenant_id, dann tid zurück) |
requireRegisteredApplication | Weist Token ab, deren Anwendung keine registrierte WaegApplication ist |
allowMasterKey | Lässt den Master-Key auf der Datenebene weiter funktionieren. Standard true |
allowVirtualKeys | Lässt virtuelle Keys auf der Datenebene weiter funktionieren. Standard true |
insecureSkipVerify | Akzeptiert ungeprüfte Token-Signaturen. Nur für die Entwicklung — Wäg verweigert das in einer Produktionsumgebung und in Kombination mit jedem Härtungsschalter |
Die Härtungsschalter können den Start des Gateways verhindern
allowMasterKey: false und allowVirtualKeys: false beschränken die Datenebene auf JWTs, und Wäg startet nicht, solange der Rest nicht stimmig ist: jwtAuth.enabled: true, eine publicKeyURL (JWKS), eine audience und insecureSkipVerify: false. Ein halb konfigurierter Härtungsschalter legt das Gateway lahm, statt sich abzustufen — setzen Sie daher beide Hälften im selben Apply.
allowVirtualKeys: false legt außerdem jeden Anwendungsschlüssel still, auch den der ChatUI. Ändern Sie das bewusst.
Die LiteLLM-spezifischen Claim-Zuordnungen werden nicht angewendet.userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField und adminJWTScope haben kein Wäg-Gegenstück: Wäg validiert das Token, leitet daraus aber weder Rollen noch Teams ab — die Autorisierung kommt aus OpenFGA und registrierten Anwendungen. Diese Felder werden über die Bedingung FeaturesSupported und in status.unsupportedFeatures gemeldet statt stillschweigend verworfen. Verwenden Sie stattdessen spec.waeg.jwt.requireRegisteredApplication und spec.waeg.modelAccess.
Erfordert die Enterprise-Lizenz des Gateways (Berechtigung jwt_api); ohne sie antwortet das Modul mit 402.
spec:
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }
jwtAuth:
enabled: true
publicKeyURL: https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys
issuer: https://login.microsoftonline.com/<tenant>/v2.0
audience: <client-id>
userIDJWTField: sub # -> subjectClaim von Wäg
waeg:
jwt:
appClaim: azp
tenantClaim: tid
requireRegisteredApplication: true
# allowMasterKey: false # nur zusammen mit den vier Einstellungen obenKonsolen-Branding
Eine Enterprise-lizenzierte Wäg-Konsole trägt standardmäßig das Navique-Theme-Pack. Der Operator schreibt das eingebaute Pack in eine ConfigMap, die ihm gehört — <gateway-name>-branding, Schlüssel theme-pack.json — und verweist die Instanz darauf; der waeg-operator sendet es anschließend an die Branding-API.
| Situation | Ergebnis |
|---|---|
waeg.brandingConfigMapRef ist gesetzt, Lizenz enthält custom-branding | Ihr Pack gewinnt. Der Operator legt nichts Eigenes an; CustomBranding=True |
waeg.brandingConfigMapRef ist gesetzt, Lizenz enthält custom-branding nicht | Das Navique-Pack wird angewendet, und CustomBranding=False (CustomBrandingUnlicensed) nennt den Grund. Der Verweis bleibt bestehen, sodass Ihr Pack greift, sobald die Lizenz es erlaubt |
waeg.defaultBranding: false | Abwahl — die Konsole behält Wägs eigenes Erscheinungsbild |
Kein instance.licenseSecretRef | Es wird überhaupt nichts angelegt. Wäg verweigert Branding ohne Lizenz, eine ConfigMap würde hier also nur ein Aussehen ankündigen, das die Konsole nie zeigen kann |
| Sonst (Standard) | Das Navique-Pack wird angewendet |
Die ConfigMap wird bei jedem Reconcile neu geschrieben. Das Pack ist Teil des Operators, daher zieht ein Operator-Upgrade die Konsole mit, statt das zuerst installierte Pack für immer festzuschreiben — womit auch Handänderungen sinnlos sind, der nächste Reconcile setzt sie zurück. Für eigenes Styling (White-Label, lizenziertes Feature custom-branding) veröffentlichen Sie eine ConfigMap und verweisen mit brandingConfigMapRef darauf — oder nutzen Sie Gateway-Branding auf der Seite des Gateways in der Management-Konsole, das das Pack validiert, eine Vorschau zeigt und es anbindet (am Stack des Gateways, sofern ein Stack es verwaltet). Ein Stack reicht spec.gateway.waeg.brandingConfigMapRef / defaultBranding durch.
spec:
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }
waeg:
defaultBranding: true # Standard; false behält Wägs Erscheinungsbild
# brandingConfigMapRef: { name: my-theme-pack, key: theme-pack.json }ChatUI verbindet sich als Anwendung
Eine ChatUI mit gatewayRef auf ein Wäg-Gateway wird automatisch verdrahtet — allerdings über ein anderes Objekt als bei LiteLLM.
Wäg kennt keinen eigenständigen virtuellen Schlüssel: ein Schlüssel gehört zu einer Anwendung — einem organisationsverankerten Tenancy-Objekt mit eigenem Modellzugriff, eigenen Budgets und eigenem Audit-Trail. Der Operator registriert die ChatUI daher als WaegApplication und erzeugt einen WaegVirtualKey dazu:
WaegApplication (Identität der Chat-UI) → WaegVirtualKey (ihr Credential)Der praktische Unterschied ist die Zuordnung: Verbrauch, Rate Limits und Audit-Einträge laufen auf „die Chat-UI" statt auf einen anonymen Schlüssel, und die Anwendung erscheint mit deklariertem Zweck in Wägs EU-AI-Act-Deployer-Inventar.
Das erfordert spec.organization am Gateway, denn Anwendungen sind organisationsverankert. Fehlt sie, wird die ChatUI mit genau dieser Begründung abgelehnt, statt auf einen Schlüssel zu warten, der nie erzeugt werden kann — setzen Sie stattdessen spec.gateway.{url, apiKeySecretRef}, um einen selbst erzeugten Schlüssel zu verwenden.
Die Credential-Rotation funktioniert wie bei LiteLLM: ein erhöhter Rotationsindex erzeugt einen neuen Schlüssel in einem frischen Secret, statt in place zu überschreiben. Die Anwendung bleibt über Rotationen hinweg stabil — sie ist die Identität, nicht das Credential.
Verschlüsselung im Transit
Sobald die Plattform-CA ein Zertifikat ausgestellt hat, aktiviert der Operator WaegInstance.spec.tls, sodass das Gateway HTTPS im Pod ausliefert — der Verkehr ist bis zum Pod verschlüsselt, nicht nur bis zum Edge. Das Gateway hat einen einzigen Listener, daher schaltet das alles gemeinsam um, und der waeg-operator zieht nach: Probes, appProtocol des Service, status.endpoint, der ServiceMonitor und die KEDA-Scale-URL.
Das CA-Bundle der Plattform geht mit. Die eigenen Admin-API-Aufrufe des Operators laufen über diese Verbindung; ohne die CA scheiterten sie an der x509-Prüfung gegen die clusterinterne CA und rissen Branding und jede Produkt-CR mit.
Erfordert Gateway 1.0.0-rc.9 oder neuer
In-Pod-TLS funktioniert erst ab Gateway 1.0.0-rc.9. Frühere Images stürzten beim Start ab, sobald spec.tls aktiviert war (rustls fand zwei eingebundene Crypto-Provider und keinen installiert). Der waeg-operator aktiviert es auf älteren Images nicht und meldet TLSUnsupportedGateway, statt eine Crash-Loop zu liefern — ein gepinntes älteres instance.image bleibt also bei Klartext, statt kaputtzugehen. waeg-operator 1.3.0 setzt rc.9 als Standard, der Standardpfad ist damit in Ordnung.
Guardrails
spec.guardrailRefs funktioniert an einem Wäg-Gateway, die Form unterscheidet sich aber. LiteLLM erwartet ein Objekt pro Ausführungsmodus, Wäg drei:
WaegGuardrailItem (eines pro Guardrail) → WaegGuardrailChain (Reihenfolge)
→ WaegGuardrailBinding (wendet die Chain an)Der Operator gibt alle drei mit scope: platform aus — eine Organisation ist nicht erforderlich. Das Binding nutzt mode: floor, läuft also immer: ein später in der Wäg-Konsole ergänztes Org- oder Team-Binding kann ein von der Plattform gesetztes Guardrail nicht abschalten.
Drei Item-Einstellungen sind entscheidend; der Operator wählt sie für Sie:
| Einstellung | Warum |
|---|---|
role | transform-Engines schreiben Inhalte um. Als policy-Item behandelt Wäg die Umschreibung als Redact-Entscheidung, und die Ersetzung mitten im Stream passiert stillschweigend nicht. Katalog-Engines deklarieren ihre eigene Rolle |
onError | Aus unreachableFallback übernommen (Standard fail_closed). Wäg ist standardmäßig fail-open, sodass bei einer Engine, die bewusst mit 502 antwortet, sonst der ursprüngliche Prompt an den Provider ginge |
mode | Wäg kennt eine during_call-Phase, die LiteLLM fehlt. Eine Transform-Engine braucht sie, sonst bleiben gestreamte Antworten unverändert. Der mitgelieferte pseudonymizer läuft unter Wäg in allen drei Phasen |
Externe Guardrails brauchen spec.waeg.path. Wäg sendet an eine vollständig angegebene URL, während LiteLLM /beta/litellm_basic_guardrail_api anhängt — der Operator kann also nicht erraten, wo ein selbst betriebener Endpunkt das Wäg-Protokoll bedient. Ohne den Pfad wird das Guardrail übersprungen und ein Event sagt das, statt dass der Operator einen Pfad rät und jeder Scan fehlschlägt, während das Gateway gesund aussieht. Katalog-Engines liefern ihren Pfad selbst (der pseudonymizer bedient /v1/waeg/check).
spec.waeg an einem Guardrail trägt zusätzlich optional role, timeoutMs, forwardIdentity und forwardSessionId. Die beiden letzten sind für Engines mit Sitzungszustand wichtig: die Namenszuordnungen des Pseudonymizers bleiben nur dann über mehrere Turns konsistent, wenn Wäg eine Identität weiterreicht — sonst fällt er auf die Request-ID zurück, die eine einzelne Anfrage zusammenhält, aber kein Gespräch.
Was nicht angewendet wird
Einige wenige Felder dieser CRD werden auf ein Wäg-Gateway weiterhin nicht angewendet. Eines davon zu setzen bricht nichts — es wird in status.unsupportedFeatures aufgeführt, die Bedingung FeaturesSupported wird False mit dem Grund UnsupportedByBackend, und ein Event wird ausgelöst.
| Feld | Warum |
|---|---|
instance.saltKey | Wäg hat keinen Salt-Key — verwenden Sie waeg.dataEncryptionKeySecretRef |
instance.storePromptsInLogs | Keine entsprechende Laufzeiteinstellung |
instance.healthCheck | Wäg hat feste /healthz- und /readyz-Probes und keine konfigurierbare Hintergrundschleife |
instance.jwtAuth — nur die Claim-Zuordnungen | Die JWT-Authentifizierung selbst wird angewendet (siehe JWT-Authentifizierung auf der Datenebene). Abgelehnt werden nur userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField und adminJWTScope: Wäg autorisiert über OpenFGA und registrierte Anwendungen, statt Rollen aus dem Token abzuleiten |
instance.rolePermissions | Wäg kennt keine LiteLLM-role_permissions — verwenden Sie waeg.modelAccess für organisationsverankerte Modell-ACLs |
models[].rpm / tpm / timeout / maxTokens | Das Wäg-Modellobjekt kennt keine Rate- oder Token-Limits, und das Gateway hat überhaupt kein Timeout pro Modell. Ein modellspezifisches Limit ist ein WaegBudget mit scope: model (ein organisationsweiter Topf für den Alias), das dieser Operator nicht ausgibt |
organization.budgetDuration | Wäg-Budget-Richtlinien haben kein Zeitraumfeld |
Außerhalb der Gateway-Spec sind die Identitätsressourcen auf Wäg vollständig unterstützt: Identity, Organization und Team emittieren WaegUser, WaegOrganization, WaegTeam sowie die org- und team-bezogenen WaegBudget-Richtlinien. Richten Sie sie mit type: gateway auf dieses Gateway aus, dann folgen sie dessen spec.type — siehe Backend-Auswahl.
Zwei Identitätsfelder werden auf Wäg nicht angewendet: teamRefs und budget einer Identity. Wäg führt die Mitgliedschaft am Team (tragen Sie die Adresse in members des Teams ein), und ein Limit pro Benutzer ist ein WaegBudget mit scope: user. Eine Wäg-Identity erhält außerdem ein erzeugtes Secret mit dem Konsolen-Passwort und meldet erst dann Ready, wenn der WaegUser Synced ist — siehe Wäg-Konten.
Vorab abgelehnt
Statt eine Ressource zu emittieren, die sich festfahren würde, lehnt der Operator Folgendes mit Ready=False und dem Grund InvalidTarget ab:
type: waegohnespec.waegoder ohnespec.waeg.clickhousewaeg.worker.enabledbeitopology: AllInOne- mehr als ein API-Replica ohne
waeg.redis waeg.modelAccessohnespec.organization
Beispiel
apiVersion: core.navique.com/v1alpha1
kind: Gateway
metadata:
name: waeg-gateway
namespace: forge-gateway
spec:
type: waeg
secretsRef: { name: forge-secrets }
database:
mode: postgresCluster
postgresClusterRef: { name: forge-pg, namespace: forge-data }
databaseName: waeg # niemals ein Schema mit einem LiteLLM-Gateway teilen
instance:
replicas: 2
masterKey: { autoGenerate: true }
licenseSecretRef: { name: waeg-enterprise-license, key: license } # schaltet jedes EE-Modul frei
jwtAuth:
enabled: true
publicKeyURL: https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys
issuer: https://login.microsoftonline.com/<tenant>/v2.0
audience: <client-id>
userIDJWTField: sub
waeg:
topology: Split
worker: { enabled: true, replicas: 2, jobWorkers: 4 }
storageMode: split # der Standard; `single` verzichtet ganz auf ClickHouse
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }
# openfga weggelassen -> der Operator rollt ein Postgres-gestütztes OpenFGA aus
# defaultBranding ist standardmäßig true -> das Navique-Theme-Pack wird angewendet
scim:
enabled: true
tokenSecretRef: { name: waeg-scim-token, key: scim-token }
defaultRole: viewer
roleMap: { "AI Platform Admins": admin }
jwt:
appClaim: azp
requireRegisteredApplication: true
modelAccess:
openByDefault: false
fallbackMode: deny
grants:
- models: [premium, economy]
organization: { name: navique-ag, maxBudget: 2000, rpmLimit: 1000 }
teams:
- { name: data-engineering }
models:
- name: gpt-5-4
modelName: premium
model: azure/gpt-5.4 # -> Provider "azure", modelId "gpt-5.4"
refSecretKey: OPENAI_API_KEY
credentials: { apiBase: "https://forge-foundry.openai.azure.com/openai/v1" }
fallbacks: [economy]Prüfen, was übersprungen wurde:
kubectl -n forge-gateway get gateway waeg-gateway \
-o jsonpath='{.status.conditions[?(@.type=="FeaturesSupported")].message}'In einem Stack
Stack.spec.gateway.type: waeg wählt Wäg für einen ganzen Stack. Der Stack erfordert dann datastores.clickhouse.enabled (die Admission lehnt ihn sonst ab), verdrahtet sein eigenes ClickHouse und Redis automatisch als zweite Ebene und nennt die logische Datenbank des Gateways waeg, damit sie nie auf einem LiteLLM-Schema landet. Stack.spec.gateway.waeg reicht die Topologie-, Worker-, OpenFGA- und Autoscaling-Einstellungen sowie scim und jwt durch; die Enterprise-Lizenz wird einmalig als Stack.spec.gateway.licenseSecretRef deklariert, und Stack.spec.gateway.jwtAuth schaltet die JWT-Authentifizierung der Datenebene ein.
Status
status.url stellt den Gateway-Endpunkt bereit (öffentlich, wenn Ingress aktiviert ist, ansonsten clusterintern), zusätzlich zur Bereitschaft der Instanz, den Modell-/Team-Anzahlen und der Bereitschaft der Datenbank. status.unsupportedFeatures listet jedes konfigurierte Feld auf, das der gewählte Gateway-Typ nicht umsetzen kann (bei type: litellm immer leer); die Bedingung FeaturesSupported spiegelt dies wider.
Lizenzhinweise
Teams, Organisationen und Budgets sind Teil des Features multi-tenancy; mehr als ein Gateway erfordert ein gateways-Limit oberhalb des Community-Standards von 1. Siehe Editions & Licensing.