Skip to content

Gateway ​

Portée : namespaced · Charge de travail : LiteLLM via le litellm-operator, ou Wäg via le waeg-operator

La passerelle IA — la porte d'entrée du trafic de modèles, avec modèles, équipes, organisations, budgets, et export optionnel de traces vers Langfuse.

spec.type choisit l'implémentation. Sa valeur par défaut est litellm, donc le reste de cette page décrit la passerelle LiteLLM sauf mention contraire ; voir Passerelle Wäg (type: waeg) pour les différences.

Spec ​

ChampTypeDescription
typelitellm | waegImplémentation de la passerelle. Défaut litellm.
waegobjectCâblage propre à Wäg (topologie, plans ClickHouse/Redis, OpenFGA). Requis lorsque type: waeg
secretsRefLocalRef (optionnel)SecretsManagement du même namespace à attendre. Omettez-le pour utiliser de simples Secrets Kubernetes que vous gérez vous-même — voir secretsRef
databaseobject (requis)Où LiteLLM stocke son état
instanceobjectImage/tag, réplicas, ressources, clés master/salt, SSO
organizationobjectOrganisation LiteLLM + budgets
teams[]listÉquipes avec budgets par équipe
models[]listLe catalogue de modèles
guardrailRefs[]ObjectRefCRs Guardrail à câbler dans ce gateway (sous licence guardrail ; émis comme LiteLLMGuardrail via le proxy de contrôle de licence de chaque garde-fou)
observabilityRefObjectRefUn Observability pour l'export de traces (câblé automatiquement si sous licence)
observabilityobjectFallback manuel du callback Langfuse
ssoobjectConnexion OIDC/SSO pour l'Admin UI de LiteLLM (voir SSO)
enableEntraSSOboolDéprécié — utilisez sso avec provider: azure-entra

database ​

ChampDescription
modepostgresCluster (référence un PostgresCluster) ou external
postgresClusterRefLe cluster à utiliser (mode postgresCluster)
databaseNameBase de données au sein du cluster partagé
connectionSecretRefSecret DATABASE_URL (mode external)

models[] ​

ChampDescription
name / modelName / modelNom d'affichage, nom de modèle LiteLLM, et identifiant de modèle du fournisseur
refSecretKeyClé dans le Secret d'identifiants de modèle contenant la clé d'API
credentials.apiBaseEndpoint du fournisseur, pour tout fournisseur qui en exige un (Azure OpenAI / AI Foundry, auto-hébergé, un proxy)
credentials.apiVersionVersion d'API du fournisseur, lorsqu'il en exige une (par ex. Azure OpenAI / AI Foundry)
rpm / tpm / timeout / maxTokensLimites par modèle (LiteLLM uniquement)
provider / providerName / modelId / fallbacks / weightChamps de catalogue propres à Wäg — voir Passerelle Wäg

Lorsqu'un modèle nécessite un endpoint et/ou une version d'API spécifiques, définissez-les sous credentials — l'opérateur les transmet au gateway avec la clé d'API issue de refSecretKey. Gardez apiBase comme endpoint nu et mettez la version dans apiVersion (n'intégrez pas ?api-version= dans l'URL). C'est indépendant du fournisseur : l'opérateur émet ce que vous fournissez et ne traite aucun fournisseur de manière particulière.

instance.healthCheck ​

Règle les vérifications de santé (health checks) des modèles en arrière-plan de LiteLLM. Désactivé par défaut : GET /health sonde les modèles à la demande au lieu d'exécuter une boucle en arrière-plan. Les vérifications en arrière-plan génèrent un trafic amont périodique et peuvent entraîner des coûts ou atteindre les limites de débit chez certains fournisseurs — ne l'activez donc que si vous voulez que /health renvoie des résultats mis en cache.

ChampTypeDescription
enabledboolActive les vérifications de santé en arrière-plan. Par défaut false (désactivé).
intervalSecondsintSecondes entre les vérifications (défaut LiteLLM 300). Ne s'applique que si enabled: true.
yaml
instance:
  healthCheck:
    enabled: true
    intervalSeconds: 300

Correspond à generalSettings.backgroundHealthChecks / healthCheckInterval de la LiteLLMInstance. Lorsqu'elles sont désactivées, l'opérateur définit explicitement backgroundHealthChecks: false.

Ce qu'il émet ​

Le contrôleur résout la base de données, garantit la présence du litellm-operator (en attendant que ses CRD soient Established), puis émet, dans l'ordre :

LiteLLMInstance → LiteLLMOrganization → LiteLLMTeam(s)
               → LiteLLMCredential(s) → LiteLLMModel(s)

(Tous dans le groupe d'API litellm.palena.ai/v1alpha1.)

Câblage de la base de données ​

  • postgresCluster — l'opérateur résout le cluster référencé, provisionne la base de données et le rôle litellm, génère le mot de passe, et injecte le Secret d'identifiants. Aucun secret manuel n'est nécessaire.
  • external — fournissez un connectionSecretRef contenant un DATABASE_URL.

Export de traces Langfuse ​

  • Avec la fonctionnalité auto-wiring et un observabilityRef, l'opérateur câble le callback Langfuse de LiteLLM (hôte + clés publique/secrète) à partir de l'Observability référencé pour que les traces s'exportent automatiquement.
  • Sans cela (ou avec un Langfuse community qui ne peut pas générer de clés de projet), créez le projet + la clé dans l'interface Langfuse et définissez spec.observability.{host, callbackSecretRef} (clés publicKey / secretKey).

Consultez Auto-Wiring pour les deux conditions impliquées.

SSO / OIDC login ​

spec.sso active l'authentification unique pour l'Admin UI de LiteLLM, traduite vers le bloc LiteLLMInstance.spec.sso. Les identifiants du client OAuth proviennent d'un Secret du même namespace (via SecretsManagement — jamais en ligne).

ChampDescription
issuerURLURL de base de l'émetteur OIDC / de découverte
clientSecretRef.nameSecret contenant le client OAuth (les clés par défaut sont client-id / client-secret, modifiables via clientIDKey / clientSecretKey)
providergeneric-oidc (par défaut), azure-entra, google, ou okta
tenantIDID de répertoire/locataire (pour azure-entra)
authorizationEndpoint / tokenEndpoint / userinfoEndpointEndpoints explicites — requis pour generic-oidc (LiteLLM n'effectue pas de découverte)
scopesScopes demandés (par défaut openid, profile, email)
providerNameLibellé d'affichage
yaml
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 }

Licence

Le SSO LiteLLM est gratuit jusqu'à 5 utilisateurs ; un SSO complet/illimité nécessite une licence LiteLLM Enterprise. L'opérateur Navique ne restreint pas le champ lui-même.

URL de redirection en accès public

LiteLLM construit la redirect_uri OAuth à partir de PROXY_BASE_URL, dérivée de l'ingress du Gateway. Pour le SSO en accès public, exposez le Gateway sur son hôte public avec TLS — spec.ingress.enabled: true, spec.ingress.host: <hôte-public> et spec.ingress.tls: true — afin que le callback se résolve en https://<hôte-public>/sso/callback. Enregistrez exactement cette URL auprès de votre IdP. Sans ingress.tls: true, la redirection retombe sur une adresse http/interne au cluster et le callback OAuth échoue. (status.endpoint de la LiteLLMInstance sous-jacente indique toujours l'URL .svc interne au cluster — c'est l'adresse de l'API d'administration de l'opérateur lui-même, pas la base de la redirection SSO.)

Le booléen déprécié enableEntraSSO: true fonctionne toujours lorsque sso n'est pas défini — il synthétise une configuration azure-entra lisant l'ancien Secret entra-sso-credentials. Préférez sso avec provider: azure-entra.

Authentification d'API par JWT & RBAC ​

spec.instance.jwtAuth active l'authentification d'API par JWT : LiteLLM valide un JWT porteur de votre IdP à chaque requête et mappe ses claims sur des rôles/équipes (distinct de sso, la connexion navigateur de l'interface d'administration). Associez-le à spec.instance.rolePermissions pour restreindre les modèles qu'un rôle peut appeler.

ChampCorrespond à (litellm_jwtauth)
jwtAuth.enabledgeneral_settings.enable_jwt_auth
jwtAuth.publicKeyURLpoint de terminaison JWKS (JWT_PUBLIC_KEY_URL) — requis : sans lui, LiteLLM ne peut valider aucun token
jwtAuth.issuerémetteur de token attendu iss (JWT_ISSUER)
jwtAuth.audienceaudience de token attendue aud (JWT_AUDIENCE)
jwtAuth.userRolesJWTFielduser_roles_jwt_field — claim JWT contenant la liste des rôles
jwtAuth.userAllowedRolesuser_allowed_roles — rôles mappés sur un internal_user
jwtAuth.enforceRBACenforce_rbac — refuser les appelants aux rôles non autorisés
jwtAuth.userRoleJWTFielduser_role_jwt_field (rôle unique)
jwtAuth.userIDJWTFielduser_id_jwt_field — claim servant d'identifiant utilisateur (sub / oid / preferred_username)
jwtAuth.userIDUpsertuser_id_upsert — crée automatiquement l'utilisateur LiteLLM à la première connexion
jwtAuth.teamIDsJWTFieldteam_ids_jwt_field
jwtAuth.adminJWTScopeadmin_jwt_scope
rolePermissions.<rôle>.models / .routesgeneral_settings.role_permissions
yaml
spec:
  instance:
    licenseSecretRef: { name: litellm-enterprise-license, key: license }
    jwtAuth:
      enabled: true
      publicKeyURL: https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys  # REQUIS (JWKS)
      issuer: https://login.microsoftonline.com/<tenant>/v2.0
      audience: <client-id>
      userIDJWTField: sub          # claim identifiant l'utilisateur (sub / oid / preferred_username)
      userIDUpsert: true           # crée l'utilisateur LiteLLM à la première connexion
      userRolesJWTField: roles
      userAllowedRoles: ["basic_user"]
      enforceRBAC: true
    rolePermissions:
      internal_user:
        models: ["anthropic-claude"]

Sur une passerelle Wäg, ce même bloc jwtAuth active le module JWT de plan de données propre à Wäg, dont les réglages spécifiques vivent sous spec.waeg.jwt — voir Authentification JWT du plan de données.

Enterprise uniquement

enable_jwt_auth, enforce_rbac et role_permissions sont des fonctionnalités LiteLLM Enterprise — renseignez instance.licenseSecretRef avec une licence LiteLLM Enterprise valide, sinon elles restent sans effet. Définir une entrée rolePermissions active general_settings.enforce_rbac pour que les restrictions s'appliquent.

Exemple ​

yaml
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            # id fournisseur/déploiement (ici : un déploiement Azure OpenAI)
      refSecretKey: OPENAI_API_KEY
      rpm: 300
      tpm: 80000
      timeout: 120
      credentials:
        apiBase: "https://forge-foundry.cognitiveservices.azure.com"
        apiVersion: "2024-10-21"

Passerelle Wäg (type: waeg) ​

Wäg est une passerelle IA alternative, pilotée par le waeg-operator embarqué (gateway.waeg.ai). C'est un produit différent, pas un clone de LiteLLM, et cette CRD ne prétend pas le contraire : tout ce qui suit est une différence réelle, et tout ce que Wäg ne sait pas faire est signalé, jamais abandonné en silence.

Ce qui change ​

SujetLiteLLMWäg
Stockageun Postgres (+ Redis optionnel)deux plans par défaut (storageMode: split) : Postgres et ClickHouse. storageMode: single garde les analyses dans le Postgres du plan de contrôle et ne requiert aucun ClickHouse
Redisoptionnelrequis dès que l'API sert plus d'un réplica (quotas HA)
Autorisationrôles du proxy / clés virtuellesOpenFGA — externe, ou adossé à Postgres et déployé par l'opérateur
Modèle de processusun Deployment de proxyAllInOne, ou Split (api + worker optionnel)
Catalogueun objet modèleprovider (connexion + credential) et alias de modèle qui le référence
Budgetsportés par les objets org/équipepolitiques de budget enracinées dans l'organisation, séparées

spec.waeg ​

ChampDescription
topologyAllInOne (défaut) ou Split (*-api plus un Deployment *-worker optionnel)
jobWorkersWorkers de tâches durables in-process (AllInOne uniquement)
workerLe Deployment de workers dédié (enabled, replicas, jobWorkers, resources) — exige topology: Split
storageModeOù vivent les analyses : split (défaut — ClickHouse) ou single (le Postgres du plan de contrôle, sans aucun ClickHouse) — voir Mode de stockage analytique
clickhouseLe plan analytique — requis pour storageMode: split, ignoré et non requis pour single : mode: ref vers un ClickHouseCluster, ou mode: external avec un Secret de connexion
clickhouseDatabaseLa base ClickHouse dans laquelle les analyses sont écrites en mode split. Défaut waeg ; l'opérateur la crée sur un cluster managed ou adopt
redisLe magasin de quotas HA : mode: ref vers une RedisInstance, ou external. Requis au-delà d'un réplica d'API
openfgaapiUrl / apiUrlSecretRef externes, ou omettez-les et l'opérateur déploie un OpenFGA adossé à Postgres (image, replicas, resources, storeId, modelId)
autoscalingHPA de l'API sur CPU/mémoire (enabled, minReplicas, maxReplicas, cibles). La mise à l'échelle sur la profondeur de file nécessite KEDA et n'est pas câblée
artifactsVolume pour les tâches/médias : emptyDir (défaut), pvc (un claim existant) ou none
bootstrapAdminCrée le premier administrateur de la console depuis un Secret (secretRef, emailKey, passwordKey)
dataEncryptionKeySecretRefLa clé avec laquelle Wäg scelle les révisions de configuration (l'équivalent de la clé salt de LiteLLM)
configYAMLRemplace l'amorce waeg.yaml. Valeurs non secrètes uniquement — le contenu atterrit dans une ConfigMap
brandingConfigMapRefVotre propre thème Enterprise (JSON) que l'opérateur envoie à l'API d'administration une fois Ready. Il remplace le thème Navique intégré — fonctionnalité sous licence custom-branding ; voir Habillage de la console
defaultBrandingApplique le thème Navique intégré lorsque la passerelle possède une licence Enterprise et qu'aucun brandingConfigMapRef n'est défini. Défaut true
modelAccessL'ACL de modèles enracinée dans l'organisation (openByDefault, fallbackMode, grants[]). Exige spec.organization
scimApprovisionnement des utilisateurs en SCIM v2 (enabled, tokenSecretRef, defaultRole, defaultOrgID, orgSource, roleMap, orgMap) — voir Approvisionnement SCIM
jwtLes réglages JWT du plan de données propres à Wäg (appClaim, appClaimFallbacks, tenantClaim, requireRegisteredApplication, allowMasterKey, allowVirtualKeys, insecureSkipVerify) — voir Authentification JWT du plan de données

Postgres provient toujours de spec.database — le même champ, résolu de la même façon. L'opérateur rassemble toutes les chaînes de connexion dans un unique Secret <gateway>-waeg-storage qu'il possède, et le référence par clé : aucune URL porteuse d'identifiants n'atterrit jamais dans la CR.

Mode de stockage analytique ​

Wäg conserve son plan de contrôle (configuration, clés, organisations, équipes, applications) dans Postgres, et son plan analytique (journaux de requêtes, usage, dépenses) dans un magasin choisi par spec.waeg.storageMode :

ModeLes analyses vivent dansClickHouse nécessaire
split (défaut)ClickHouse — spec.waeg.clickhouse doit le désigneroui
singlele même Postgres que le plan de contrôle (spec.database)non

split est le défaut de cet opérateur : c'est ce que la Gateway a toujours émis, et cela tient la charge. Le défaut de Wäg lui-même est single, et c'est son point de départ recommandé — c'est précisément la raison d'être de ce champ. Exiger un ClickHouse pour chaque passerelle imposait une dépendance que l'amont avait déjà abandonnée ; pour un déploiement modeste ou d'évaluation, elle double le nombre de datastores à exploiter, dimensionner et sauvegarder sans rien apporter.

Avec single, la WaegInstance émise ne porte aucun bloc clickhouse ; spec.waeg.clickhouse est ignoré et n'est pas requis.

Changer de mode ne migre pas l'historique analytique

Les deux plans sont des magasins distincts. Modifier storageMode sur une passerelle en service la pointe vers l'autre : les analyses déjà écrites restent où elles sont et disparaissent de la console. Ni l'opérateur ni Wäg ne les recopie. Choisissez un mode avant de collecter des données qui comptent, ou exportez-les d'abord.

single — Postgres uniquement ​

yaml
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         # les analyses atterrissent dans forge-pg, à côté du plan de contrôle
    # aucun bloc clickhouse — inutile, et un bloc placé ici serait ignoré

split — analyses dans ClickHouse ​

yaml
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          # le défaut ; peut être omis
    clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
    clickhouseDatabase: waeg    # le défaut
    redis:      { mode: ref, ref: { name: forge-redis, namespace: forge-data } }

Wäg ne crée que ses tables, jamais sa base de données. Sur un ClickHouseCluster en mode managed ou adopt, l'opérateur déclare cette base sous spec.databases et la crée pour vous ; sur un cluster external, vous devez la créer vous-même, sans quoi la passerelle s'arrête au démarrage avec Database waeg does not exist.

Modèles : provider + alias ​

Le catalogue de Wäg sépare la connexion amont de l'alias exposé aux clients ; chaque entrée de spec.models émet donc un WaegProvider et un WaegModel. Les modèles partageant un pilote partagent un provider.

ChampDescription
providerPilote du provider (openai, azure, anthropic, …). Par défaut le préfixe de model (azure/gpt-4o → azure), sinon openai
providerNameNomme l'entrée de catalogue du provider, pour que plusieurs modèles partagent une connexion. Par défaut le pilote
modelIdIdentifiant du modèle côté provider. Par défaut model sans son préfixe (azure/gpt-4o → gpt-4o)
fallbacksAlias de modèles à essayer en cas d'échec
weightPondère ce déploiement face aux autres du même alias

Ce qu'elle émet ​

WaegInstance → WaegOrganization → WaegBudget (limites org) → WaegTeam(s)
             → WaegProvider(s)  → WaegModel(s)             → WaegModelAccess

(Le tout dans le groupe d'API gateway.waeg.ai/v1alpha1.)

Licence Enterprise ​

spec.instance.licenseSecretRef porte la licence Wäg Enterprise de la passerelle. Elle atteint l'instance sous la forme WaegInstance.spec.secrets.licenseKey ; la clé du Secret vaut license par défaut.

Chaque module Wäg Enterprise en dépend — SSO, SCIM, audit, CMEK, FIPS et habillage — et l'application de la règle est inconditionnelle : une image de passerelle liée à l'EE mais sans licence répond 402 license_required. Exécuter la build Enterprise ne suffit donc pas à elle seule ; si un module signale LicenseRequired, c'est précisément ce champ qui manque.

yaml
spec:
  instance:
    licenseSecretRef: { name: waeg-enterprise-license, key: license }

La licence est référencée, jamais insérée en clair : laissez votre backend SecretsManagement matérialiser le Secret (ESO depuis Key Vault, ou un SealedSecret) comme n'importe quel autre identifiant. Dans un Stack, elle se déclare une seule fois via spec.gateway.licenseSecretRef et est transmise à la passerelle.

SSO et export de traces ​

Les deux fonctionnent, par des chemins différents de ceux de LiteLLM.

Le SSO (spec.sso) est appliqué via une CR WaegEnterpriseConfig, qui configure les modules Enterprise de Wäg par son API d'administration plutôt que par l'environnement de l'instance. L'opérateur lit le client id dans votre Secret et le pose comme champ simple (un client id est public par construction — il circule dans l'URL d'autorisation du navigateur), tandis que le client secret reste une référence de Secret, projetée sur le pod en WAEG_EE_OIDC_CLIENT_SECRET. C'est délibéré : la CR pourrait aussi écrire le secret dans le magasin de configuration scellé de la passerelle, ce qui le placerait dans ses révisions de configuration au lieu de le laisser dans Kubernetes.

L'URI de redirection est dérivée en <origine publique>/waeg/ui/v1/ee/sso/callback — la passerelle rejette toute valeur ne contenant pas ce chemin. Le SSO exige donc spec.ingress avec un hôte ; sans lui l'opérateur avertit plutôt que d'émettre un callback qui ne pourra jamais aboutir. Enregistrez exactement cette URL auprès de votre IdP.

Nécessite le build Enterprise et une licence ; la CR rapporte EnterpriseNotLinked ou LicenseRequired par module si l'un manque.

L'export de traces (observabilityRef / observability) est mappé sur WaegInstance.spec.observability.langfuse, les clés de projet étant fournies par référence de Secret. L'environnement est le seul chemin déclaratif de la passerelle — elle amorce son fichier de configuration au premier démarrage seulement, la console l'emportant ensuite — donc le sink est construit une fois au démarrage et un changement de clé fait rouler les pods. L'ordre de résolution est celui de LiteLLM : clés manuelles d'abord, puis câblage automatique sous licence depuis une Observability référencée.

Nécessite une passerelle récente

Les variables WAEG_LANGFUSE_* sont arrivées après la passerelle 1.0.0-rc.7. Une image plus ancienne rapporte LangfuseRequiresNewerGateway au lieu d'accepter un réglage qui ne prendrait jamais effet.

Approvisionnement SCIM ​

spec.waeg.scim active les points d'accès SCIM v2 de Wäg : votre IdP crée, met à jour et désactive directement les utilisateurs de la console, au lieu que quelqu'un le fasse à la main. SCIM est indépendant de spec.sso : il fonctionne sur une passerelle où aucune connexion interactive n'est configurée — la forme habituelle d'une intégration d'approvisionnement sans interface.

ChampDescription
enabledActive les points d'accès. Défaut true ; désactivés, ils répondent 404
tokenSecretRefRequis. Le jeton porteur que présente votre IdP. Clé scim-token par défaut
defaultRoleRôle console attribué à un utilisateur nouvellement approvisionné : viewer, operator ou admin
defaultOrgIDOrganisation Wäg dans laquelle atterrissent les utilisateurs approvisionnés. Par défaut, l'organisation de la passerelle elle-même lorsque spec.organization est défini
orgSourceD'où provient l'organisation d'un utilisateur approvisionné : waeg (la passerelle décide), enterprise (la charge utile de l'IdP) ou none
roleMapNom de groupe IdP → rôle console Wäg. Envoyé en remplacement intégral de la map
orgMapValeur IdP → identifiant d'organisation Wäg. Remplacement intégral également

roleMap et orgMap sont remplacées en totalité à chaque apply plutôt que fusionnées : la passerelle contient donc exactement ce qui est déclaré ici — supprimer une entrée supprime la correspondance.

Laisser defaultOrgID vide convient dès lors que la passerelle a une organization : les utilisateurs y atterrissent au lieu d'être sans organisation, ce qui compte car un utilisateur sans organisation manque toutes les autorisations de modèles enracinées dans l'organisation.

Les points d'accès se trouvent sous /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/ServiceProviderConfig

Pointez le connecteur SCIM de votre IdP sur cette URL de base, avec le jeton porteur issu de tokenSecretRef. Sans ce jeton, chaque appel répond 401.

Le jeton est une variable d'environnement, pas une valeur de configuration stockée

L'opérateur projette le jeton sur les pods de la passerelle sous le nom WAEG_EE_SCIM_TOKEN plutôt que de l'écrire dans le magasin de configuration scellé de la passerelle. Ce magasin ne peut jamais être relu : une copie écrite là deviendrait silencieusement obsolète dès la rotation du Secret. En variable d'environnement, faire tourner le Secret et laisser les pods redémarrer constitue toute la procédure de rotation.

Exige la licence Enterprise de la passerelle — sans elle le module répond 402 — et rien d'autre.

Pas la fonctionnalité sso-scim de la plateforme

La licence AI Core possède sa propre fonctionnalité sso-scim. Il s'agit d'une capacité de plateforme distincte, pas encore implémentée, sans rapport avec le module de passerelle décrit ici.

yaml
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": viewer

Authentification JWT du plan de données ​

Wäg peut valider un JWT porteur émis par votre IdP sur les requêtes du plan de données : les appelants présentent alors un jeton au lieu d'une clé — ou en plus d'elle. L'activation passe par le bloc partagé spec.instance.jwtAuth (le même champ que pour LiteLLM) ; le réglage fin se fait via spec.waeg.jwt, propre à Wäg.

spec.instance.jwtAuthModule EE jwt de Wäg
enabledenabled
publicKeyURLjwksUrl — le point d'accès JWKS d'où proviennent les clés de signature
issuerissuer — l'iss attendu
audienceaudience — l'aud attendue
userIDJWTFieldsubjectClaim — le claim qui identifie l'appelant
spec.waeg.jwtDescription
appClaimClaim identifiant l'application appelante. Défaut azp
appClaimFallbacksClaims essayés dans l'ordre lorsque appClaim est absent. Remplacement intégral de la liste — une liste vide efface les valeurs par défaut de Wäg (appid, client_id)
tenantClaimClaim portant l'organisation / le tenant (sinon Wäg se rabat sur tenant_id, puis tid)
requireRegisteredApplicationRejette les jetons dont l'application n'est pas une WaegApplication enregistrée
allowMasterKeyLaisse la clé maîtresse fonctionner sur le plan de données. Défaut true
allowVirtualKeysLaisse les clés virtuelles fonctionner sur le plan de données. Défaut true
insecureSkipVerifyAccepte les signatures non vérifiées. Développement uniquement — Wäg le refuse en environnement de production et en présence de tout commutateur de durcissement

Les commutateurs de durcissement peuvent empêcher la passerelle de démarrer

allowMasterKey: false et allowVirtualKeys: false restreignent le plan de données aux seuls JWT, et Wäg refuse de démarrer tant que le reste n'est pas cohérent : jwtAuth.enabled: true, une publicKeyURL (JWKS), une audience et insecureSkipVerify: false. Un commutateur de durcissement à moitié configuré met la passerelle hors service au lieu de se dégrader — définissez donc les deux moitiés dans le même apply.

allowVirtualKeys: false coupe aussi toutes les clés d'application, y compris celle de la ChatUI. Ne le changez qu'en connaissance de cause.

Les correspondances de claims propres à LiteLLM ne sont pas appliquées.userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField et adminJWTScope n'ont pas d'équivalent chez Wäg : Wäg valide le jeton mais n'en extrait jamais de rôles ni d'équipes — l'autorisation vient d'OpenFGA et des applications enregistrées. Les définir est signalé sur la condition FeaturesSupported et dans status.unsupportedFeatures plutôt qu'abandonné en silence. Utilisez plutôt spec.waeg.jwt.requireRegisteredApplication et spec.waeg.modelAccess.

Exige la licence Enterprise de la passerelle (droit jwt_api) ; sans elle, le module répond 402.

yaml
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 de Wäg
  waeg:
    jwt:
      appClaim: azp
      tenantClaim: tid
      requireRegisteredApplication: true
      # allowMasterKey: false    # uniquement avec les quatre réglages ci-dessus

Habillage de la console ​

Une console Wäg sous licence Enterprise porte par défaut le thème Navique. L'opérateur matérialise le thème intégré dans une ConfigMap qu'il possède — <nom-de-la-passerelle>-branding, clé theme-pack.json — et y pointe l'instance ; le waeg-operator l'envoie ensuite à l'API d'habillage.

SituationRésultat
waeg.brandingConfigMapRef est défini, la licence inclut custom-brandingVotre thème l'emporte. L'opérateur ne crée rien de son côté ; CustomBranding=True
waeg.brandingConfigMapRef est défini, la licence n'inclut pas custom-brandingLe thème Navique est appliqué et CustomBranding=False (CustomBrandingUnlicensed) en indique la raison. La référence est conservée : votre thème s'applique dès que la licence le permet
waeg.defaultBranding: falseRetrait — la console conserve la livrée propre à Wäg
Pas de instance.licenseSecretRefRien n'est créé du tout. Wäg refuse l'habillage sans licence : une ConfigMap ne ferait qu'annoncer un style que la console ne pourra jamais afficher
Sinon (le défaut)Le thème Navique est appliqué

La ConfigMap est réécrite à chaque réconciliation. Le thème est livré avec l'opérateur : une mise à jour de l'opérateur fait donc avancer la console au lieu de figer le thème installé en premier — ce qui rend aussi toute retouche manuelle inutile, la réconciliation suivante l'annule. Pour votre propre style (marque blanche, fonctionnalité sous licence custom-branding), publiez une ConfigMap et pointez brandingConfigMapRef dessus — ou utilisez l'habillage de la passerelle sur la page de la passerelle dans la console de gestion, qui valide le thème, en affiche un aperçu et le raccorde (sur le Stack de la passerelle lorsqu'un Stack la gère). Un Stack transmet spec.gateway.waeg.brandingConfigMapRef / defaultBranding.

yaml
spec:
  instance:
    licenseSecretRef: { name: waeg-enterprise-license, key: license }
  waeg:
    defaultBranding: true          # le défaut ; false conserve la livrée de Wäg
    # brandingConfigMapRef: { name: my-theme-pack, key: theme-pack.json }

ChatUI se connecte en tant qu'application ​

Une ChatUI dont le gatewayRef pointe vers une passerelle Wäg est câblée automatiquement, mais via un objet différent de celui qu'utilise LiteLLM.

Wäg n'a pas de clé virtuelle autonome : une clé appartient à une application — un objet de tenancy enraciné dans l'organisation, avec son propre accès aux modèles, ses budgets et sa piste d'audit. L'opérateur enregistre donc la ChatUI comme WaegApplication et génère un WaegVirtualKey associé :

WaegApplication (identité de la chat UI)  →  WaegVirtualKey (son credential)

La différence pratique tient à l'attribution : consommation, limites de débit et entrées d'audit sont imputées à « la chat UI » plutôt qu'à une clé anonyme, et l'application figure dans l'inventaire déployeur EU AI Act de Wäg avec une finalité déclarée.

Cela exige spec.organization sur le Gateway, car les applications sont enracinées dans l'organisation. Sans elle, la ChatUI est refusée avec ce motif plutôt que laissée en attente d'une clé qui ne pourra jamais être générée — définissez plutôt spec.gateway.{url, apiKeySecretRef} pour utiliser une clé que vous avez générée vous-même.

La rotation des credentials fonctionne comme pour LiteLLM : incrémenter l'index de rotation génère une nouvelle clé dans un Secret neuf plutôt que d'écraser sur place. L'application reste stable d'une rotation à l'autre — c'est l'identité, pas le credential.

Chiffrement en transit ​

Dès que la CA de la plateforme a émis un certificat, l'opérateur active WaegInstance.spec.tls : la passerelle sert alors du HTTPS dans le pod — le trafic est chiffré jusqu'au pod, pas seulement jusqu'à l'edge. La passerelle n'a qu'un seul listener, donc tout bascule ensemble et le waeg-operator suit : sondes, appProtocol du Service, status.endpoint, le ServiceMonitor et l'URL de scale KEDA.

Le bundle CA de la plateforme l'accompagne. Les appels d'API d'administration de l'opérateur lui-même empruntent cette connexion : sans la CA ils échoueraient à la vérification x509 contre la CA interne au cluster, emportant le branding et toutes les CR produit.

Nécessite la passerelle 1.0.0-rc.9 ou plus récente

Le TLS dans le pod ne fonctionne qu'à partir de la passerelle 1.0.0-rc.9. Les images antérieures paniquaient au démarrage dès que spec.tls était activé (rustls trouvait deux fournisseurs cryptographiques liés et aucun installé). Le waeg-operator refuse de l'activer sur une image plus ancienne et rapporte TLSUnsupportedGateway plutôt que de livrer une boucle de crash : épingler un instance.image plus ancien laisse donc la passerelle en clair au lieu de la casser. waeg-operator 1.3.0 utilise rc.9 par défaut, le chemin par défaut est donc sain.

Garde-fous ​

spec.guardrailRefs fonctionne sur une passerelle Wäg, mais la forme diffère. LiteLLM attend un objet par mode d'exécution ; Wäg en attend trois :

WaegGuardrailItem (un par Guardrail)  →  WaegGuardrailChain (les ordonne)
                                      →  WaegGuardrailBinding (applique la chaîne)

L'opérateur les émet tous les trois en scope: platform : aucune organisation n'est requise. Le binding utilise mode: floor, il s'applique donc toujours — un binding org ou équipe ajouté plus tard dans la console Wäg ne peut pas désactiver un garde-fou posé par la plateforme.

Trois réglages de l'item sont déterminants ; l'opérateur les choisit pour vous :

RéglagePourquoi
roleLes moteurs transform réécrivent le contenu. Configuré en policy, Wäg traite la réécriture comme une décision de masquage et la substitution en cours de flux n'a silencieusement pas lieu. Les moteurs du catalogue déclarent leur propre rôle
onErrorRepris de unreachableFallback (défaut fail_closed). Wäg est fail-open par défaut : un moteur qui répond délibérément 502 verrait sinon le prompt d'origine transmis au fournisseur
modeWäg a une phase during_call absente de LiteLLM. Un moteur transform en a besoin, sinon les réponses en flux ne sont pas réécrites. Le pseudonymizer embarqué tourne sur les trois phases avec Wäg

Les garde-fous externes exigent spec.waeg.path. Wäg envoie à une URL donnée en entier, là où LiteLLM ajoute /beta/litellm_basic_guardrail_api — l'opérateur ne peut donc pas deviner où un endpoint que vous gérez sert le protocole Wäg. Sans ce chemin, le garde-fou est ignoré et un Event l'indique, plutôt que l'opérateur devine un chemin et fasse échouer chaque analyse pendant que la passerelle paraît saine. Les moteurs du catalogue fournissent le leur (le pseudonymizer sert /v1/waeg/check).

spec.waeg sur un Guardrail porte aussi role, timeoutMs, forwardIdentity et forwardSessionId en option. Les deux derniers comptent pour les moteurs à état de session : les correspondances de noms du pseudonymizer ne restent cohérentes d'un tour à l'autre que si Wäg transmet une identité — sinon il retombe sur l'identifiant de requête, qui relie une seule requête mais pas une conversation.

Ce qui n'est pas appliqué ​

Quelques champs de cette CRD ne sont toujours pas appliqués à une passerelle Wäg. En définir un ne casse rien : il est listé dans status.unsupportedFeatures, la condition FeaturesSupported passe à False avec le motif UnsupportedByBackend, et un Event est émis.

ChampPourquoi
instance.saltKeyWäg n'a pas de clé salt — utilisez waeg.dataEncryptionKeySecretRef
instance.storePromptsInLogsAucun réglage d'exécution équivalent
instance.healthCheckWäg expose des sondes /healthz et /readyz fixes, sans boucle de fond configurable
instance.jwtAuth — les correspondances de claims uniquementL'authentification JWT elle-même est appliquée (voir Authentification JWT du plan de données). Seuls userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField et adminJWTScope sont refusés : Wäg autorise via OpenFGA et les applications enregistrées plutôt qu'en extrayant des rôles du jeton
instance.rolePermissionsWäg n'a pas de role_permissions façon LiteLLM — utilisez waeg.modelAccess pour des ACL de modèles enracinées dans l'organisation
models[].rpm / tpm / timeout / maxTokensL'objet modèle de Wäg ne porte ni limite de débit ni limite de tokens, et la passerelle n'a aucun timeout par modèle. Une limite par modèle est un WaegBudget avec scope: model (un pool à l'échelle de l'organisation pour l'alias), que cet opérateur n'émet pas
organization.budgetDurationLes politiques de budget Wäg n'ont pas de champ de période

Hors spec du Gateway, les resources d'identité sont pleinement supportées sur Wäg : Identity, Organization et Team émettent WaegUser, WaegOrganization, WaegTeam ainsi que les politiques WaegBudget au niveau de l'organisation et de l'équipe. Pointez-les vers ce Gateway avec type: gateway et elles suivront son spec.type — voir Choix du backend.

Deux champs d'identité ne sont pas appliqués sur Wäg : les teamRefs et le budget d'une Identity. Wäg garde l'appartenance sur l'équipe (indiquez l'adresse dans les members de la Team), et une limite par utilisateur est un WaegBudget avec scope: user. Une Identity Wäg reçoit également un Secret de mot de passe de console généré et ne signale Ready qu'une fois le WaegUserSynced — voir Comptes Wäg.

Rejeté en amont ​

Plutôt que d'émettre une ressource qui se bloquerait, l'opérateur refuse ceci avec Ready=False et le motif InvalidTarget :

  • type: waeg sans spec.waeg ou sans spec.waeg.clickhouse
  • waeg.worker.enabled avec topology: AllInOne
  • plus d'un réplica d'API sans waeg.redis
  • waeg.modelAccess sans spec.organization

Exemple ​

yaml
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          # ne jamais partager un schéma avec une passerelle LiteLLM
  instance:
    replicas: 2
    masterKey: { autoGenerate: true }
    licenseSecretRef: { name: waeg-enterprise-license, key: license }   # débloque chaque module EE
    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          # le défaut ; `single` se passe totalement de ClickHouse
    clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
    redis:      { mode: ref, ref: { name: forge-redis, namespace: forge-data } }
    # openfga omis -> l'opérateur déploie un OpenFGA adossé à Postgres
    # defaultBranding vaut true par défaut -> le thème Navique est appliqué
    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]

Vérifier ce qui a été ignoré :

bash
kubectl -n forge-gateway get gateway waeg-gateway \
  -o jsonpath='{.status.conditions[?(@.type=="FeaturesSupported")].message}'

Dans un Stack ​

Stack.spec.gateway.type: waeg la sélectionne pour tout un Stack. Le Stack exige alors datastores.clickhouse.enabled (l'admission le rejette sinon), câble automatiquement ses propres ClickHouse et Redis comme second plan, et nomme la base logique de la passerelle waeg pour qu'elle n'atterrisse jamais sur un schéma LiteLLM. Stack.spec.gateway.waeg transmet les réglages de topologie/worker/OpenFGA/autoscaling, ainsi que scim et jwt ; la licence Enterprise se déclare une seule fois via Stack.spec.gateway.licenseSecretRef, et Stack.spec.gateway.jwtAuth active l'authentification JWT du plan de données.

Status ​

status.url expose l'endpoint de la passerelle (public lorsque l'ingress est activé, sinon interne au cluster), plus la disponibilité de l'instance, les compteurs de modèles/équipes, et la disponibilité de la base de données. status.unsupportedFeatures liste tout champ configuré que le type de passerelle choisi ne peut pas honorer (toujours vide pour type: litellm) ; la condition FeaturesSupported le reflète.

Notes de licence ​

Les équipes, organisations et budgets font partie de la fonctionnalité multi-tenancy ; plus d'un Gateway requiert une limite gateways supérieure à la valeur par défaut Community de 1. Consultez Éditions et licences.

Cœur open source sous AGPL-3.0. Les composants Enterprise sont propriétaires et soumis à licence.