Identity, Organization et Team
Portée : namespaced · Sous licence (fonctionnalité identity-management + quotas organizations / teams)
Ces resources gèrent les identités à travers les composants de la plateforme — la passerelle (LiteLLM ou Wäg), Langfuse et LibreChat — sous forme d'objets Kubernetes, de sorte que les accès et la tenance soient déclaratifs et auditables plutôt que configurés à la main dans chaque outil.
| Resource | Gère |
|---|---|
User | Une personne / un siège — la clé naturelle (email) qui relie entre eux les comptes de cette personne |
Identity | Un compte par backend pour une personne, sur la passerelle, Observability (Langfuse) ou LibreChat |
Organization | Une frontière de tenant / facturation de premier niveau |
Team | Un groupe au sein d'une organisation, avec des budgets et un accès aux modèles |
Personnes vs comptes
Un User est une personne (un siège sous licence). Un Identity est l'un des comptes backend de cette personne. Le quota users de la licence compte les sièges User ; les comptes Identity sont illimités, et chacun doit pointer vers un User via userRef. Une personne disposant d'une clé de passerelle, d'une appartenance Langfuse et d'une connexion LibreChat correspond à un siège, trois objets Identity.
Identity
Un Identity provisionne un compte unique pour une personne sur un backend. Son spec.type sélectionne le composant dans lequel vit le compte :
type | Émet |
|---|---|
gateway | Recommandé. Suit le spec.type du Gateway référencé — voir Choix du backend |
litellm | Resources d'utilisateur LiteLLM (LiteLLMUser), vérifiées contre le Gateway référencé |
waeg | Utilisateur de console Wäg (WaegUser), vérifié contre le Gateway référencé — voir Comptes Wäg |
observability | Appartenance à l'organisation Langfuse de l'Observability (nécessite une licence Langfuse Enterprise) |
librechat | Configuration d'utilisateur / rôle LibreChat |
Spec
| Champ | Type | Description |
|---|---|---|
type | enum (requis) | gateway / litellm / waeg / observability / librechat |
userRef | { name } (requis) | Le User propriétaire dans le même namespace |
email | string (optionnel) | Email du compte ; par défaut le spec.email du User propriétaire |
displayName | string | Nom lisible par un humain |
userId | string | Identifiant utilisateur externe stable pour le backend |
role | string | Rôle backend. Sur Wäg, seuls admin ou platform_admin exactement accordent le privilège d'administration de plateforme à l'échelle du cluster — rien d'autre n'est déduit |
budget | object | Budget par compte (lorsque le backend le supporte ; non appliqué sur Wäg) |
teamRefs | list | Équipes auxquelles ce compte appartient (LiteLLM ; non appliqué sur Wäg) |
gatewayRef | ObjectRef | Le Gateway sur lequel vit ce compte. Requis pour type: gateway / litellm / waeg, et il doit se trouver dans le même namespace — voir Règles d'admission |
observabilityRef | ObjectRef | Un Observability (pour type: observability) |
chatUIRef | ObjectRef | Un ChatUI (pour type: librechat) |
passwordSecretRef | SecretKeyRef | Secret de mot de passe (pour type: librechat) |
Choix du backend : type: gateway
Identity, Organization et Team acceptent tous type: gateway, et c'est la valeur à utiliser pour les nouvelles resources. Une resource d'identité adossée à une passerelle cible un Gateway — et ce Gateway sait déjà quelle implémentation il exécute. Avec type: gateway, l'opérateur lit le backend dans le spec.type du Gateway référencé, la seule source qui ne peut pas diverger.
spec:
type: gateway # résolu depuis le Gateway ci-dessous
gatewayRef: { name: forge-gateway }Les valeurs nommées d'après le produit, litellm et waeg, fonctionnent toujours et ne sont pas vouées à disparaître. Ce qui change, c'est qu'elles sont désormais vérifiées contre le Gateway au lieu d'être crues sur parole. Déclarer un type qui contredit le Gateway est refusé avec Ready=False :
type=litellm does not match Gateway "forge-gateway", which is type=waeg —
set type: gateway to follow the Gateway, or point gatewayRef at a litellm gatewayLe message nomme le type déclaré, le Gateway, le type réel du Gateway, et les deux issues : suivre le Gateway avec type: gateway, ou faire pointer gatewayRef vers une passerelle du type déclaré.
Pourquoi cette vérification existe
Rien ne comparait auparavant les deux valeurs. Une Organization / une Team / un Identity déclarant type: litellm face à une passerelle Wäg était accepté, et l'opérateur émettait une LiteLLMOrganization / LiteLLMTeam / LiteLLMUser dont l'instanceRef désignait une instance Wäg — un objet qui ne se lie à rien et ne se matérialise dans aucune passerelle.
Sur un cluster où les deux opérateurs sont installés, cet apply réussit : la CRD existe, l'objet est stocké, et rien ne révèle jamais l'erreur. Le compte n'apparaît tout simplement jamais dans la passerelle. L'incohérence est désormais un refus au lieu d'un objet silencieusement orphelin.
Règles d'admission
Deux règles sont appliquées par le serveur d'API sur Identity, Team etOrganization, de sorte qu'une mauvaise référence est refusée dès le kubectl apply plutôt que des heures plus tard dans une condition de statut.
1. Un type adossé à une passerelle requiert gatewayRef.
type gateway/litellm/waeg requires gatewayRefgatewayRef était jusqu'ici optionnel sur chacune de ces resources. Une Team de type: waeg sans gatewayRef était admise et n'échouait qu'à la réconciliation avec « a gateway-backed identity resource requires gatewayRef » — la même information, des heures plus tard.
2. gatewayRef.namespace doit être vide.
gatewayRef.namespace must be empty: the Gateway must live in the same namespace as this resourceLe litellm-operator comme le waeg-operator résolvent l'instanceRef amont au sein du namespace local, une référence gatewayRef inter-namespaces ne pouvait donc jamais se lier à quoi que ce soit. Placez le Gateway dans le même namespace que les resources d'identité qui le ciblent, et ne renseignez que { name: … }.
Comptes LibreChat
LibreChat n'expose aucune API pour créer des comptes. Un Identity de type: librechat est donc provisionné en exécutant le script create-user fourni avec LibreChat sous la forme d'un Job Kubernetes éphémère. Le Job est cloné depuis le conteneur LibreChat en cours d'exécution (même image et même environnement), il atteint donc automatiquement la même base MongoDB.
- Le
ChatUIréférencé (spec.chatUIRef) doit être Ready et se trouver dans le même namespace que l'Identity. - Le provisionnement est idempotent et en création seule (create-only) : un compte existant est considéré comme un succès, et les modifications ultérieures de l'
Identityne changent pas un compte déjà créé. - Mot de passe : définissez
spec.passwordSecretRef(clépasswordpar défaut) pour utiliser un mot de passe connu. Si vous l'omettez, l'opérateur en génère un et le stocke dans un Secret dédié nommé<identity>-librechat-password(clépassword). Récupérez-le aveckubectl get secret <identity>-librechat-password -o jsonpath='{.data.password}' | base64 -dpuis effectuez une rotation après la première connexion. Le mot de passe n'est jamais transmis en ligne de commande.
Comptes Wäg
Identity, Team et Organization sont pleinement supportés sur une passerelle Wäg. Ils émettent WaegUser, WaegTeam, WaegOrganization ainsi que les politiques WaegBudget au niveau de l'organisation et de l'équipe (toutes dans gateway.waeg.ai/v1alpha1).
Mot de passe de console. Wäg exige un mot de passe pour créer un utilisateur de console. Pour un Identity adossé à Wäg, l'opérateur en génère un dans un Secret dédié nommé <nom-de-l-identity>-waeg-password, clé password, et y fait pointer le WaegUser via spec.passwordSecretRef. Récupérez-le pour la première connexion de la personne avec :
kubectl get secret <identity>-waeg-password \
-o jsonpath='{.data.password}' | base64 -dLe Secret est généré une seule fois et jamais renouvelé automatiquement — en effectuer la rotation fait tourner le mot de passe de la console. Rien d'autre n'est à renseigner ; il n'existe aucun champ permettant de fournir votre propre mot de passe pour un compte Wäg.
Ready signifie que le compte existe. Une resource d'identité Wäg ne signale Ready qu'une fois que le WaegUser émis signale lui-même Synced=True. Appliquer la CR amont était auparavant considéré comme un succès, ce qui masquait précisément la défaillance ci-dessus : sans Secret de mot de passe, le WaegUser restait indéfiniment à Synced=False — « passwordSecretRef is required to create a new console user », tandis que l'Identity s'affichait au vert et que le compte de console n'existait pas. Une Identity au vert signifie désormais que la passerelle a accepté le compte.
Membres d'équipe. Wäg désigne un membre d'équipe par un WaegUser, jamais par son email. Pour un membre qui possède aussi une Identity sur le même Gateway, l'équipe pointe vers le WaegUser de cette Identity — un seul utilisateur de console par personne. Seule une adresse sans Identity reçoit un WaegUser d'appartenance distinct (nommé <team>-<email-slug>), supprimé dès que l'adresse quitte l'équipe. Cette suppression n'efface jamais le compte Wäg de la personne : Wäg ne supprime un utilisateur de l'annuaire que si la CR supprimée l'a créé, ce qu'un WaegUser d'appartenance (sans mot de passe) ne fait jamais.
Non appliqué sur Wäg — chaque cas est signalé par un Event (UnsupportedByBackend), jamais abandonné en silence :
| Champ | Pourquoi |
|---|---|
Identity.spec.teamRefs | Wäg garde l'appartenance sur l'équipe, pas sur l'utilisateur. Indiquez plutôt l'adresse de la personne dans le spec.members de la Team |
Identity.spec.budget | Une limite Wäg par utilisateur est un WaegBudget de scope: user, que cet opérateur n'émet pas |
Organization.spec.members | WaegOrganization ne modélise aucun membre — l'appartenance à une organisation est un endpoint de l'Admin API sans CR. Les members d'une Team, eux, sont appliqués |
spec.budget.budgetDuration (Organization / Team) | Les politiques de budget Wäg n'ont pas de champ de période |
spec.budget.models (Organization / Team) | Les listes de modèles autorisés relèvent de l'ACL WaegModelAccess distincte, pas d'un champ de budget |
Organization et Team
| Resource | Gère |
|---|---|
Organization | Une frontière de tenant / facturation de premier niveau |
Team | Un groupe au sein d'une organisation, avec des budgets et un accès aux modèles |
Chacune porte un type qui sélectionne l'identité de quel composant elle pilote :
type | Émet |
|---|---|
gateway | Recommandé. Suit le spec.type du Gateway référencé |
litellm | LiteLLMOrganization / LiteLLMTeam |
waeg | WaegOrganization / WaegTeam, plus la politique WaegBudget correspondante |
observability | Resources d'org / projet / appartenance Langfuse (les équipes sont à l'état d'ébauche) |
Les deux acceptent un gatewayRef — requis pour gateway / litellm / waeg, et uniquement dans le même namespace (Règles d'admission). Organization expose également un observabilityRef pour pointer vers la charge de travail Observability lorsque type: observability.
Une Team référence son organisation parente via spec.organizationRef ({ name }, même namespace). Sur LiteLLM le parent est optionnel ; sur Wäg il est requis — les équipes Wäg sont toujours rattachées à une organisation. Une Team de type: waeg qui en manque est rejetée à l'admission (spec.organizationRef: Required value) ; une Team de type: gateway sur un gateway Wäg est refusée lors de la réconciliation, car l'admission ne voit pas le type du Gateway. Le type de l'organisation parente est résolu de la même façon : une Organization de type: gateway sur un gateway Wäg est un parent valide pour une équipe Wäg ; une Organization de l'autre produit est signalée comme incompatible.
Exemple
Un Gateway, une organisation, une équipe, une personne — et aucune des resources d'identité ne nomme le produit de passerelle. Basculer le Gateway entre litellm et waeg laisse les quatre inchangées.
apiVersion: core.navique.com/v1alpha1
kind: Organization
metadata:
name: acme
namespace: forge
spec:
type: gateway # résolu depuis le Gateway
gatewayRef: { name: forge-gateway } # même namespace — pas de champ `namespace:`
displayName: ACME Corp
budget:
maxBudget: 500
---
apiVersion: core.navique.com/v1alpha1
kind: Team
metadata:
name: platform
namespace: forge
spec:
type: gateway
gatewayRef: { name: forge-gateway }
organizationRef: { name: acme } # requis sur Wäg, optionnel sur LiteLLM
displayName: Platform Team
members:
- email: alice@example.com # sur Wäg, l'appartenance se déclare ici
role: admin
---
apiVersion: core.navique.com/v1alpha1
kind: User
metadata:
name: alice
namespace: forge
spec:
email: alice@example.com
displayName: Alice Example
---
apiVersion: core.navique.com/v1alpha1
kind: Identity
metadata:
name: alice-gateway
namespace: forge
spec:
type: gateway
userRef: { name: alice } # le siège sous licence, même namespace
gatewayRef: { name: forge-gateway }
role: admin
# email omis → hérite de alice@example.com depuis le UserSur une passerelle de type: waeg, cela produit également le mot de passe de console généré dans alice-gateway-waeg-password — voir Comptes Wäg.
Relation avec le Gateway
Un Gateway peut déclarer une organization et des teams[] inline. Les resources d'identité sont le mécanisme plus large, transversal aux composants : elles vous permettent de gérer les organisations, les équipes, les personnes (User) et leurs comptes backend (Identity) une seule fois et de les voir reflétés de manière cohérente à travers la passerelle, Langfuse et LibreChat, y compris à travers plusieurs gateways.
Licences
La gestion des identités fait partie de la valeur multi-tenancy et requiert la fonctionnalité identity-management ainsi que les quotas d'instances organizations / teams pertinents dans la License. Le quota users compte les sièges User — et non les comptes Identity, qui sont illimités. Dans l'édition Community, utilisez les organization / teams[] inline sur un seul Gateway.
Voir Éditions et licences.