Identity, Organization e Team
Ambito: namespaced · Con licenza (funzionalità identity-management + limiti organizations / teams)
Queste risorse gestiscono le identità tra i componenti della piattaforma — il gateway (LiteLLM o Wäg), Langfuse e LibreChat — come oggetti Kubernetes, così che accessi e tenancy siano dichiarativi e verificabili anziché configurati a mano in ciascuno strumento.
| Risorsa | Gestisce |
|---|---|
User | Una persona / postazione — la chiave naturale (email) che collega tra loro gli account di quella persona |
Identity | Un account per backend di una persona, sul gateway, su Observability (Langfuse) o su LibreChat |
Organization | Un tenant di primo livello / confine di fatturazione |
Team | Un gruppo all'interno di un'organizzazione, con budget e accesso ai modelli |
Persone e account
Uno User è una persona (una postazione licenziata). Una Identity è uno degli account backend di quella persona. Il limite users della licenza conta le postazioni User; gli account Identity non sono soggetti a limiti, e ciascuno deve puntare a uno User tramite userRef. Una persona con una chiave del gateway, un'adesione a Langfuse e un login LibreChat è una postazione, tre oggetti Identity.
Identity
Una Identity crea un singolo account per una persona su un backend. Il suo spec.type seleziona il componente in cui risiede l'account:
type | Emette |
|---|---|
gateway | Consigliato. Segue il spec.type del Gateway referenziato — vedi Scelta del backend |
litellm | Risorse utente LiteLLM (LiteLLMUser), verificate rispetto al Gateway referenziato |
waeg | Utente della console Wäg (WaegUser), verificato rispetto al Gateway referenziato — vedi Account Wäg |
observability | Adesione all'organizzazione Langfuse dell'Observability (richiede una licenza Langfuse Enterprise) |
librechat | Configurazione utente / ruolo di LibreChat |
Spec
| Campo | Tipo | Descrizione |
|---|---|---|
type | enum (obbligatorio) | gateway / litellm / waeg / observability / librechat |
userRef | { name } (obbligatorio) | Lo User proprietario nello stesso namespace |
email | string (opzionale) | Email dell'account; per impostazione predefinita è lo spec.email dello User proprietario |
displayName | string | Nome leggibile |
userId | string | ID utente esterno stabile per il backend |
role | string | Ruolo nel backend. Su Wäg, esattamente admin o platform_admin concede il privilegio di platform-admin a livello di cluster — nient'altro viene dedotto |
budget | object | Budget per account (dove il backend lo supporta; non applicato su Wäg) |
teamRefs | list | Team a cui appartiene questo account (LiteLLM; non applicato su Wäg) |
gatewayRef | ObjectRef | Il Gateway su cui risiede questo account. Obbligatorio per type: gateway / litellm / waeg, e deve trovarsi nello stesso namespace — vedi Regole di ammissione |
observabilityRef | ObjectRef | Un Observability (per type: observability) |
chatUIRef | ObjectRef | Una ChatUI (per type: librechat) |
passwordSecretRef | SecretKeyRef | Secret della password (per type: librechat) |
Scelta del backend: type: gateway
Identity, Organization e Team accettano tutte type: gateway, ed è il valore da usare per le nuove risorse. Una risorsa di identità basata sul gateway punta a un Gateway — e quel Gateway sa già quale implementazione esegue. Con type: gateway l'operatore legge il backend dal spec.type del Gateway referenziato, l'unica fonte che non può divergere.
spec:
type: gateway # resolved from the Gateway below
gatewayRef: { name: forge-gateway }I valori con il nome del prodotto litellm e waeg funzionano ancora e non ne è prevista la rimozione. Ciò che è cambiato è che ora vengono verificati rispetto al Gateway invece di essere accettati sulla fiducia. Dichiarare un tipo che contraddice il Gateway viene rifiutato con 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 gatewayIl messaggio indica il tipo dichiarato, il Gateway, il tipo effettivo del Gateway e entrambe le vie d'uscita: seguire il Gateway con type: gateway, oppure puntare gatewayRef a un gateway del tipo dichiarato.
Perché esiste questo controllo
In passato nulla confrontava i due valori. Una Organization / Team / Identity che dichiarava type: litellm verso un gateway Wäg veniva accettata, e l'operatore emetteva un LiteLLMOrganization / LiteLLMTeam / LiteLLMUser il cui instanceRef indicava un'istanza Wäg — un oggetto che non si lega a nulla e non si materializza in alcun gateway.
Su un cluster in cui sono installati entrambi gli operatori, quell'apply va a buon fine: il CRD esiste, l'oggetto viene memorizzato e nulla fa mai emergere l'errore. L'account semplicemente non compare mai nel gateway. La discrepanza ora produce un rifiuto invece di un oggetto silenziosamente orfano.
Regole di ammissione
Due regole vengono applicate dall'API server su Identity, Team eOrganization, così che un riferimento errato venga rifiutato già al kubectl apply invece che ore dopo in una status condition.
1. Un tipo basato sul gateway richiede gatewayRef.
type gateway/litellm/waeg requires gatewayRefIn passato gatewayRef era opzionale su ognuna di queste risorse. Un Team con type: waeg e senza gatewayRef veniva ammesso e falliva solo al reconcile con "a gateway-backed identity resource requires gatewayRef" — la stessa informazione, ore dopo.
2. gatewayRef.namespace deve essere vuoto.
gatewayRef.namespace must be empty: the Gateway must live in the same namespace as this resourceSia il litellm-operator sia il waeg-operator risolvono l'instanceRef upstream localmente al namespace, quindi un gatewayRef cross-namespace non potrebbe mai legarsi a nulla. Colloca il Gateway nello stesso namespace delle risorse di identità che lo referenziano, e imposta solo { name: … }.
Account LibreChat
LibreChat non dispone di un'API per creare account, quindi una Identity con type: librechat viene creata eseguendo lo script create-user incluso in LibreChat come Job Kubernetes di breve durata. Il Job è clonato dal container LibreChat in esecuzione (stessa immagine e stesso ambiente), quindi raggiunge automaticamente lo stesso MongoDB.
- La
ChatUIreferenziata (spec.chatUIRef) deve essere Ready e trovarsi nello stesso namespace dellaIdentity. - Il provisioning è idempotente e solo in creazione: un account esistente viene considerato un successo, e le modifiche successive alla
Identitynon alterano un account già esistente. - Password: imposta
spec.passwordSecretRef(chiavepasswordper impostazione predefinita) per usare una password nota. Se lo ometti, l'operatore ne genera una e la memorizza in un Secret di sua proprietà chiamato<identity>-librechat-password(chiavepassword). Recuperala conkubectl get secret <identity>-librechat-password -o jsonpath='{.data.password}' | base64 -de ruotala dopo il primo login. La password non viene mai passata sulla riga di comando.
Account Wäg
Identity, Team e Organization sono pienamente supportate su un gateway Wäg. Emettono WaegUser, WaegTeam, WaegOrganization e le policy WaegBudget con scope di organizzazione e di team (tutte in gateway.waeg.ai/v1alpha1).
Password della console. Wäg richiede una password per creare un utente della console. Per una Identity basata su Wäg l'operatore ne genera una in un Secret di sua proprietà chiamato <identity-name>-waeg-password, chiave password, e vi fa puntare il WaegUser tramite spec.passwordSecretRef. Rileggila per il primo login della persona con:
kubectl get secret <identity>-waeg-password \
-o jsonpath='{.data.password}' | base64 -dIl Secret viene generato una sola volta e mai ruotato automaticamente — ruotare il Secret ruota la password della console. Non serve impostare nient'altro; non esiste alcun campo per fornire una password propria per un account Wäg.
Ready significa che l'account esiste. Una risorsa di identità Wäg riporta Ready solo quando il WaegUser emesso riporta a sua volta Synced=True. In passato l'applicazione del CR upstream veniva considerata un successo, il che nascondeva esattamente il problema descritto sopra: senza Secret della password il WaegUser restava permanentemente in Synced=False — "passwordSecretRef is required to create a new console user", mentre la Identity appariva verde e l'account della console non esisteva. Una Identity verde ora significa che il gateway ha accettato l'account.
Membri del team. Wäg identifica un membro del team tramite WaegUser, mai tramite email. Per un membro che ha anche una Identity sullo stesso gateway, il team punta al WaegUser di quella Identity — un solo utente della console per persona. Solo un indirizzo senza Identity ottiene un WaegUser di adesione separato (chiamato <team>-<email-slug>), che viene nuovamente eliminato quando l'indirizzo lascia il team. Rimuoverlo non elimina mai l'account Wäg della persona: Wäg elimina un utente della directory solo quando è stato creato dal CR che viene eliminato, cosa che un utente di adesione (che non ha password) non fa mai.
Non applicati su Wäg — ciascuno viene segnalato come Event (UnsupportedByBackend), mai ignorato silenziosamente:
| Campo | Motivo |
|---|---|
Identity.spec.teamRefs | Wäg mantiene l'adesione sul team, non sull'utente. Aggiungi invece l'indirizzo della persona a spec.members del Team |
Identity.spec.budget | Un limite per utente in Wäg è un WaegBudget con scope: user, che questo operatore non emette |
Organization.spec.members | WaegOrganization non modella membri — l'adesione all'organizzazione è un endpoint dell'Admin API senza CR. I members di un Team vengono applicati |
spec.budget.budgetDuration (Organization / Team) | Le policy di budget di Wäg non hanno un campo per il periodo |
spec.budget.models (Organization / Team) | Le allow-list dei modelli sono l'ACL separata WaegModelAccess, non un campo del budget |
Organization e Team
| Risorsa | Gestisce |
|---|---|
Organization | Un tenant di primo livello / confine di fatturazione |
Team | Un gruppo all'interno di un'organizzazione, con budget e accesso ai modelli |
Ciascuna porta un type che seleziona l'identità di quale componente gestisce:
type | Emette |
|---|---|
gateway | Consigliato. Segue il spec.type del Gateway referenziato |
litellm | LiteLLMOrganization / LiteLLMTeam |
waeg | WaegOrganization / WaegTeam, più la policy WaegBudget corrispondente |
observability | Risorse Langfuse di organizzazione / progetto / adesione (i team sono predisposti come scaffold) |
Entrambe accettano un gatewayRef — obbligatorio per gateway / litellm / waeg, e solo nello stesso namespace (Regole di ammissione). Organization espone inoltre un observabilityRef per puntare al workload Observability quando type: observability.
Un Team fa riferimento al proprio genitore tramite spec.organizationRef ({ name }, stesso namespace). Su LiteLLM il genitore è opzionale; su Wäg è obbligatorio — i team Wäg sono sempre radicati in un'organizzazione. Un Team type: waeg che ne è privo viene rifiutato in fase di ammissione (spec.organizationRef: Required value); un Team type: gateway su un gateway Wäg viene invece rifiutato al reconcile, poiché l'ammissione non può vedere il tipo del Gateway. Il tipo del genitore viene risolto allo stesso modo, quindi una Organization di type: gateway su un gateway Wäg è un genitore valido per un team Wäg; una Organization sull'altro prodotto viene segnalata come discrepanza.
Esempio
Un Gateway, un'organizzazione, un team, una persona — e nessuna delle risorse di identità nomina il prodotto del gateway. Passare il Gateway tra litellm e waeg lascia invariate tutte e quattro.
apiVersion: core.navique.com/v1alpha1
kind: Organization
metadata:
name: acme
namespace: forge
spec:
type: gateway # resolved from the Gateway
gatewayRef: { name: forge-gateway } # same namespace — no `namespace:` field
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 } # required on Wäg, optional on LiteLLM
displayName: Platform Team
members:
- email: alice@example.com # on Wäg, membership lives here
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 } # the licensed seat, same namespace
gatewayRef: { name: forge-gateway }
role: admin
# email omitted → inherits alice@example.com from the UserSu un gateway type: waeg questo produce anche la password della console generata in alice-gateway-waeg-password — vedi Account Wäg.
Relazione con il Gateway
Un Gateway può dichiarare inline una organization e dei teams[]. Le risorse di identità sono il meccanismo più ampio e trasversale ai componenti: consentono di gestire organizzazioni, team, persone (User) e i loro account backend (Identity) una sola volta e di vederli riflessi in modo coerente su gateway, Langfuse e LibreChat, anche su più gateway.
Licenze
La gestione delle identità fa parte della proposta multi-tenancy e richiede la funzionalità identity-management più i relativi limiti di istanze organizations / teams nella License. Il limite users conta le postazioni User — non gli account Identity, che non sono soggetti a limiti. Nella Community edition, usa organization / teams[] inline su un singolo Gateway.
Vedi Edizioni e licenze.