Gateway
Ambito: namespaced · Workload: LiteLLM tramite il litellm-operator, oppure Wäg tramite il waeg-operator
Il gateway AI — il punto d'ingresso del traffico verso i modelli, con modelli, team, organization, budget ed export opzionale delle trace verso Langfuse.
spec.type sceglie l'implementazione. Il valore predefinito è litellm, quindi il resto di questa pagina descrive il gateway LiteLLM salvo diversa indicazione; vedi Gateway Wäg (type: waeg) per le differenze.
Spec
| Campo | Tipo | Descrizione |
|---|---|---|
type | litellm | waeg | Implementazione del gateway. Predefinito litellm. |
waeg | object | Wiring specifico di Wäg (topologia, piani ClickHouse/Redis, OpenFGA). Obbligatorio con type: waeg |
secretsRef | LocalRef (facoltativo) | SecretsManagement nello stesso namespace da attendere. Omettilo per usare normali Secret Kubernetes gestiti da te — vedi secretsRef |
database | object (obbligatorio) | Dove LiteLLM conserva il proprio stato |
instance | object | Immagine/tag, repliche, risorse, chiavi master/salt, SSO |
organization | object | Organization LiteLLM + budget |
teams[] | list | Team con budget per team |
models[] | list | Il catalogo dei modelli |
guardrailRefs | []ObjectRef | CR Guardrail da collegare a questo gateway (soggetto alla licenza guardrail; emessi come LiteLLMGuardrail tramite il proxy license-gate di ciascun guardrail) |
observabilityRef | ObjectRef | Una risorsa Observability per l'export delle trace (auto-wiring se previsto dalla licenza) |
observability | object | Fallback manuale per la callback Langfuse |
sso | object | Login OIDC/SSO per la LiteLLM Admin UI (vedi SSO) |
enableEntraSSO | bool | Deprecato — usa sso con provider: azure-entra |
database
| Campo | Descrizione |
|---|---|
mode | postgresCluster (riferimento a un PostgresCluster) oppure external |
postgresClusterRef | Il cluster da usare (modalità postgresCluster) |
databaseName | Database all'interno del cluster condiviso |
connectionSecretRef | Secret con DATABASE_URL (modalità external) |
models[]
| Campo | Descrizione |
|---|---|
name / modelName / model | Nome visualizzato, nome del modello in LiteLLM e id del modello presso il provider |
refSecretKey | Chiave, nel Secret delle credenziali dei modelli, che contiene la API key |
credentials.apiBase | Endpoint del provider, per qualsiasi provider che ne richieda uno esplicito (Azure OpenAI / AI Foundry, self-hosted, un proxy) |
credentials.apiVersion | Versione dell'API del provider, quando il provider la richiede (ad es. Azure OpenAI / AI Foundry) |
rpm / tpm / timeout / maxTokens | Limiti per modello (solo LiteLLM) |
provider / providerName / modelId / fallbacks / weight | Campi di catalogo specifici di Wäg — vedi Gateway Wäg |
Quando un modello richiede un endpoint personalizzato e/o una versione dell'API, impostali in credentials — l'operatore li passa al gateway insieme alla API key presa da refSecretKey. Mantieni in apiBase il solo endpoint e metti la versione in apiVersion (non inserire ?api-version= nell'URL). Il meccanismo è indipendente dal provider: l'operatore emette ciò che fornisci e non tratta mai un provider come caso speciale.
instance.healthCheck
Regola i controlli di salute in background dei modelli di LiteLLM. Disattivati per impostazione predefinita: GET /health interroga i modelli su richiesta invece di eseguire un ciclo in background. I controlli in background generano traffico periodico verso l'upstream e, con alcuni provider, possono comportare costi o raggiungere i rate limit; attivali quindi solo se vuoi che /health restituisca risultati in cache.
| Campo | Tipo | Descrizione |
|---|---|---|
enabled | bool | Attiva i controlli di salute in background. Predefinito false (disattivati). |
intervalSeconds | int | Secondi tra un controllo e l'altro (predefinito LiteLLM 300). Si applica solo con enabled: true. |
instance:
healthCheck:
enabled: true
intervalSeconds: 300Corrisponde a generalSettings.backgroundHealthChecks / healthCheckInterval del LiteLLMInstance. Quando sono disattivati, l'operatore imposta esplicitamente backgroundHealthChecks: false.
Cosa emette
Il controller risolve il database, garantisce la presenza del litellm-operator (attendendo che i suoi CRD siano Established), quindi emette, in ordine:
LiteLLMInstance → LiteLLMOrganization → LiteLLMTeam(s)
→ LiteLLMCredential(s) → LiteLLMModel(s)(Tutti nel gruppo API litellm.palena.ai/v1alpha1.)
Collegamento del database
postgresCluster— l'operatore risolve il cluster referenziato, crea il database e il ruololitellm, genera la password e inietta il Secret delle credenziali. Nessun secret manuale necessario.external— fornisci unconnectionSecretRefche contenga unDATABASE_URL.
Export delle trace verso Langfuse
- Con la funzionalità
auto-wiringe unobservabilityRef, l'operatore collega la callback Langfuse di LiteLLM (host + chiavi pubblica/segreta) a partire dall'Observabilityreferenziata, così le trace vengono esportate automaticamente. - Senza di essa (o con un Langfuse community che non può generare chiavi di progetto), crea il progetto e la chiave nella UI di Langfuse e imposta
spec.observability.{host, callbackSecretRef}(chiavipublicKey/secretKey).
Vedi Auto-Wiring per i due gate coinvolti.
Login SSO / OIDC
spec.sso abilita il single sign-on per la Admin UI di LiteLLM, tradotto nel blocco LiteLLMInstance.spec.sso. Le credenziali del client OAuth provengono da un Secret nello stesso namespace (tramite SecretsManagement — mai inline).
| Campo | Descrizione |
|---|---|
issuerURL | Issuer OIDC / URL base di discovery |
clientSecretRef.name | Secret che contiene il client OAuth (chiavi predefinite client-id / client-secret, sovrascrivibili con clientIDKey / clientSecretKey) |
provider | generic-oidc (predefinito), azure-entra, google oppure okta |
tenantID | ID della directory/del tenant (per azure-entra) |
authorizationEndpoint / tokenEndpoint / userinfoEndpoint | Endpoint espliciti — obbligatori per generic-oidc (LiteLLM non esegue la discovery) |
scopes | Scope richiesti (predefiniti openid, profile, email) |
providerName | Etichetta visualizzata |
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 }Licenze
L'SSO di LiteLLM è gratuito fino a 5 utenti; l'SSO completo/illimitato richiede una licenza LiteLLM Enterprise. L'operatore Navique non applica alcun gating sul campo stesso.
URL di redirect con accesso pubblico
LiteLLM costruisce il redirect_uri OAuth a partire da PROXY_BASE_URL, derivato dall'ingress del Gateway. Per l'SSO con accesso pubblico, esponi il Gateway sul suo host pubblico con TLS — spec.ingress.enabled: true, spec.ingress.host: <public-host> e spec.ingress.tls: true — in modo che la callback si risolva in https://<public-host>/sso/callback. Registra **esattamente quell'**URL presso il tuo IdP. Senza ingress.tls: true il redirect ripiega su un indirizzo in http semplice/interno al cluster e la callback OAuth fallisce. (status.endpoint sul LiteLLMInstance sottostante riporta sempre l'URL .svc interno al cluster — è l'indirizzo della admin API usato dall'operatore stesso, non la base del redirect SSO.)
Il booleano deprecato enableEntraSSO: true funziona ancora quando sso non è impostato — genera una configurazione azure-entra che legge il Secret legacy entra-sso-credentials. Preferisci sso con provider: azure-entra.
Autenticazione API tramite JWT e RBAC
spec.instance.jwtAuth attiva l'autenticazione API basata su JWT: LiteLLM convalida un JWT bearer del tuo IdP a ogni richiesta e ne mappa i claim su ruoli/team (a differenza di sso, che è il login via browser per la Admin UI). Combinalo con spec.instance.rolePermissions per limitare quali modelli può chiamare ciascun ruolo.
| Campo | Corrisponde a (litellm_jwtauth) |
|---|---|
jwtAuth.enabled | general_settings.enable_jwt_auth |
jwtAuth.publicKeyURL | Endpoint JWKS (JWT_PUBLIC_KEY_URL) — obbligatorio: senza di esso LiteLLM non può convalidare alcun token |
jwtAuth.issuer | issuer iss atteso del token (JWT_ISSUER) |
jwtAuth.audience | audience aud attesa del token (JWT_AUDIENCE) |
jwtAuth.userRolesJWTField | user_roles_jwt_field — claim JWT che contiene l'elenco dei ruoli |
jwtAuth.userAllowedRoles | user_allowed_roles — ruoli mappati su un internal_user |
jwtAuth.enforceRBAC | enforce_rbac — nega i chiamanti i cui ruoli non sono ammessi |
jwtAuth.userRoleJWTField | user_role_jwt_field (ruolo singolo) |
jwtAuth.userIDJWTField | user_id_jwt_field — claim usato come id utente (sub / oid / preferred_username) |
jwtAuth.userIDUpsert | user_id_upsert — crea automaticamente l'utente LiteLLM al primo login |
jwtAuth.teamIDsJWTField | team_ids_jwt_field |
jwtAuth.adminJWTScope | admin_jwt_scope |
rolePermissions.<role>.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 # REQUIRED (JWKS)
issuer: https://login.microsoftonline.com/<tenant>/v2.0
audience: <client-id>
userIDJWTField: sub # claim identifying the user (sub / oid / preferred_username)
userIDUpsert: true # auto-create the LiteLLM user on first login
userRolesJWTField: roles
userAllowedRoles: ["basic_user"]
enforceRBAC: true
rolePermissions:
internal_user:
models: ["anthropic-claude"]Su un gateway Wäg lo stesso blocco jwtAuth attiva il modulo JWT del data plane di Wäg, con le opzioni specifiche di Wäg in spec.waeg.jwt — vedi Autenticazione JWT del data plane.
Solo Enterprise
enable_jwt_auth, enforce_rbac e role_permissions sono funzionalità LiteLLM Enterprise — imposta instance.licenseSecretRef su una licenza LiteLLM Enterprise valida, altrimenti non hanno alcun effetto. Impostare una qualsiasi voce di rolePermissions attiva general_settings.enforce_rbac, così le restrizioni vengono effettivamente applicate.
Esempio
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 (here: an 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"Gateway Wäg (type: waeg)
Wäg è un gateway AI alternativo, gestito dal waeg-operator incluso (gateway.waeg.ai). È un prodotto diverso, non un clone di LiteLLM, e questo CRD non finge il contrario: tutto ciò che segue è una differenza reale, e qualsiasi cosa Wäg non sappia fare viene segnalata, mai scartata silenziosamente.
Cosa cambia
| Aspetto | LiteLLM | Wäg |
|---|---|---|
| Storage | un Postgres (+ Redis facoltativo) | doppio piano per impostazione predefinita (storageMode: split): Postgres e ClickHouse. storageMode: single mantiene i dati analitici nel Postgres del control plane e non richiede ClickHouse |
| Redis | facoltativo | obbligatorio non appena l'API gira con più di una replica (quote HA) |
| Autorizzazione | ruoli del proxy / virtual key | OpenFGA — esterno, oppure un'istanza basata su Postgres distribuita dall'operatore |
| Modello di processo | un solo Deployment proxy | AllInOne, oppure Split (api + worker facoltativo) |
| Catalogo | un unico oggetto modello | provider (connessione + credenziale) e alias di modello che vi punta |
| Budget | sugli oggetti organization/team | policy di budget con radice nell'organization, separate |
spec.waeg
| Campo | Descrizione |
|---|---|
topology | AllInOne (predefinito) oppure Split (*-api più un Deployment *-worker facoltativo) |
jobWorkers | Worker in-process per i job durevoli (solo AllInOne) |
worker | Il Deployment worker dedicato (enabled, replicas, jobWorkers, resources) — richiede topology: Split |
storageMode | Dove risiedono i dati analitici: split (predefinito — ClickHouse) oppure single (il Postgres del control plane, nessun ClickHouse) — vedi Modalità di storage analitico |
clickhouse | Il piano analitico — obbligatorio per storageMode: split, ignorato e non richiesto per single: mode: ref verso un ClickHouseCluster, oppure mode: external con un Secret di connessione |
clickhouseDatabase | Il database ClickHouse in cui vengono scritti i dati analitici in modalità split. Predefinito waeg; l'operatore lo crea su un cluster managed o adopt |
redis | Lo store delle quote HA: mode: ref verso un RedisInstance, oppure external. Obbligatorio con più di una replica API |
openfga | apiUrl / apiUrlSecretRef esterni, oppure omettilo e l'operatore distribuisce un OpenFGA basato su Postgres (image, replicas, resources, storeId, modelId) |
autoscaling | HPA dell'API su CPU/memoria (enabled, minReplicas, maxReplicas, target). Lo scaling sulla profondità della coda richiede KEDA e non è collegato |
artifacts | Volume per job/media: emptyDir (predefinito), pvc (un claim esistente) oppure none |
bootstrapAdmin | Crea il primo admin della console da un unico Secret (secretRef, emailKey, passwordKey) |
dataEncryptionKeySecretRef | La chiave con cui Wäg sigilla le revisioni di configurazione (l'equivalente Wäg della salt key di LiteLLM) |
configYAML | Sovrascrive il seed di bootstrap waeg.yaml. Solo dati non segreti — viene renderizzato in una ConfigMap |
brandingConfigMapRef | Il tuo theme pack Enterprise (JSON), che l'operatore invia via POST alla Admin API una volta Ready. Sostituisce il pack Navique integrato — funzionalità soggetta a licenza custom-branding; vedi Branding della console |
defaultBranding | Applica il theme pack Navique integrato quando il gateway ha una licenza Enterprise e nessun brandingConfigMapRef è impostato. Predefinito true |
modelAccess | L'ACL dei modelli con radice nell'organization (openByDefault, fallbackMode, grants[]). Richiede spec.organization |
scim | Provisioning utenti SCIM v2 (enabled, tokenSecretRef, defaultRole, defaultOrgID, orgSource, roleMap, orgMap) — vedi Provisioning SCIM |
jwt | Le opzioni JWT del data plane specifiche di Wäg (appClaim, appClaimFallbacks, tenantClaim, requireRegisteredApplication, allowMasterKey, allowVirtualKeys, insecureSkipVerify) — vedi Autenticazione JWT del data plane |
Postgres proviene sempre da spec.database — lo stesso campo, risolto allo stesso modo. L'operatore assembla ogni stringa di connessione in un unico Secret <gateway>-waeg-storage di sua proprietà e vi fa riferimento per chiave, così nessun URL contenente credenziali finisce mai nel CR.
Modalità di storage analitico
Wäg mantiene il suo control plane (configurazione, chiavi, organization, team, applicazioni) in Postgres e il suo piano analitico (log delle richieste, utilizzo, spesa) in uno store selezionato da spec.waeg.storageMode:
| Modalità | Dati analitici in | ClickHouse necessario |
|---|---|---|
split (predefinito) | ClickHouse — spec.waeg.clickhouse deve puntarvi | sì |
single | lo stesso Postgres del control plane (spec.database) | no |
split è il valore predefinito di questo operatore: è ciò che il Gateway ha sempre emesso, ed è ciò che regge i volumi elevati. Il valore predefinito di Wäg upstream è invece single, ed è il suo punto di partenza consigliato — ed è proprio per questo che esiste questo campo. Richiedere un ClickHouse per ogni gateway imponeva una dipendenza che upstream aveva già eliminato e, per un deployment piccolo o di valutazione, raddoppia i datastore da gestire, dimensionare e sottoporre a backup senza alcun beneficio.
Con single il WaegInstance emesso non contiene alcun blocco clickhouse; spec.waeg.clickhouse viene ignorato e non è richiesto.
Cambiare modalità non migra lo storico analitico
I due piani sono store separati. Cambiare storageMode su un gateway in esecuzione lo fa puntare all'altro — i dati analitici già scritti restano dove erano e non sono più visibili nella console. Né l'operatore né Wäg li copiano. Scegli una modalità prima di raccogliere dati a cui tieni, oppure esportali prima.
single — solo 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 # analytics land in forge-pg, next to the control plane
# no clickhouse block — none is needed, and one here would be ignoredsplit — dati analitici 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 # the default; may be omitted
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
clickhouseDatabase: waeg # the default
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }Wäg crea solo le proprie tabelle, mai il proprio database. Su un ClickHouseCluster managed o adopt l'operatore dichiara quel database in spec.databases e lo crea per te; su un cluster external devi crearlo tu, altrimenti il gateway termina all'avvio con Database waeg does not exist.
Modelli: provider + alias
Il catalogo di Wäg separa la connessione upstream dall'alias esposto ai client, quindi ogni voce di spec.models emette un WaegProvider e un WaegModel. I modelli che condividono un driver condividono un unico provider.
| Campo | Descrizione |
|---|---|
provider | Driver del provider (openai, azure, anthropic, …). Predefinito: il prefisso di model (azure/gpt-4o → azure), altrimenti openai |
providerName | Dà il nome alla voce di catalogo del provider, così più modelli possono condividere una connessione. Predefinito: il driver |
modelId | Id del modello lato provider. Predefinito: model senza prefisso (azure/gpt-4o → gpt-4o) |
fallbacks | Alias di modello da provare quando questo fallisce |
weight | Bilancia questo deployment rispetto ad altri sullo stesso alias |
Cosa emette
WaegInstance → WaegOrganization → WaegBudget (org caps) → WaegTeam(s)
→ WaegProvider(s) → WaegModel(s) → WaegModelAccess(Tutti nel gruppo API gateway.waeg.ai/v1alpha1.)
Licenza Enterprise
spec.instance.licenseSecretRef contiene la licenza Wäg Enterprise del gateway. Arriva all'istanza come WaegInstance.spec.secrets.licenseKey; la chiave del Secret è per impostazione predefinita license.
Ogni modulo Wäg Enterprise dipende da essa — SSO, SCIM, audit, CMEK, FIPS e branding allo stesso modo — e l'applicazione è incondizionata: un'immagine del gateway con EE collegato ma senza licenza risponde 402 license_required. Eseguire la build Enterprise non basta di per sé: se un modulo riporta LicenseRequired, è questo campo a mancare.
spec:
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }La licenza è referenziata, mai inline: lascia che il backend SecretsManagement materializzi il Secret (ESO da Key Vault, oppure un SealedSecret) come per ogni altra credenziale. In uno Stack viene dichiarata una sola volta come spec.gateway.licenseSecretRef e passata al Gateway.
SSO ed export delle trace
Entrambi funzionano, attraverso percorsi diversi da quelli di LiteLLM.
L'SSO (spec.sso) viene applicato tramite un CR WaegEnterpriseConfig, che configura i moduli Enterprise di Wäg attraverso la sua admin API anziché tramite le variabili d'ambiente dell'istanza. L'operatore legge il client id dal tuo Secret e lo imposta come campo in chiaro (un client id è pubblico per costruzione — viaggia nell'URL di autorizzazione del browser), mentre il client secret resta un riferimento a un Secret, proiettato sul pod come WAEG_EE_OIDC_CLIENT_SECRET. È una scelta deliberata: il CR potrebbe anche scrivere il secret nello store di configurazione sigillato del gateway, il che lo metterebbe nelle revisioni di configurazione del gateway invece di lasciarlo in Kubernetes.
L'URI di redirect è derivato come <public origin>/waeg/ui/v1/ee/sso/callback — il gateway rifiuta qualsiasi valore che non contenga quel percorso. L'SSO richiede quindi spec.ingress con un host; in sua assenza l'operatore emette un avviso anziché una callback che non potrebbe mai risolversi. Registra esattamente quell'URL presso il tuo IdP.
Richiede la build Enterprise del gateway e una licenza; il CR riporta EnterpriseNotLinked o LicenseRequired per ciascun modulo se manca l'una o l'altra.
L'export delle trace (observabilityRef / observability) corrisponde a WaegInstance.spec.observability.langfuse, con le chiavi di progetto fornite come riferimento a un Secret. Le variabili d'ambiente sono l'unico percorso dichiarativo del gateway — che popola il proprio file di configurazione solo al primo avvio, dopodiché prevale la console — quindi il sink viene costruito una sola volta all'avvio e un cambio di chiave riavvia i pod. L'ordine di risoluzione è lo stesso di LiteLLM: prima le chiavi manuali, poi l'auto-wiring soggetto a licenza da un'Observability referenziata.
Richiede un gateway recente
Le variabili WAEG_LANGFUSE_* sono arrivate dopo il gateway 1.0.0-rc.7. Un'immagine più vecchia riporta LangfuseRequiresNewerGateway invece di accettare un'impostazione che non avrebbe mai effetto.
Provisioning SCIM
spec.waeg.scim attiva gli endpoint SCIM v2 di Wäg, così il tuo IdP crea, aggiorna e disattiva direttamente gli utenti della console invece di farlo fare a qualcuno a mano. SCIM è indipendente da spec.sso: funziona anche su un gateway senza alcun login interattivo configurato, che è la configurazione tipica di un'integrazione di provisioning headless.
| Campo | Descrizione |
|---|---|
enabled | Attiva gli endpoint. Predefinito true; quando disattivati rispondono 404 |
tokenSecretRef | Obbligatorio. Il bearer token presentato dal tuo IdP. Chiave predefinita scim-token |
defaultRole | Ruolo della console assegnato a un utente appena creato: viewer, operator oppure admin |
defaultOrgID | Organization Wäg in cui finiscono gli utenti creati. Predefinita: l'organization del Gateway stesso quando spec.organization è impostato |
orgSource | Da dove proviene l'organization di un utente creato: waeg (decide il gateway), enterprise (il payload dell'IdP) oppure none |
roleMap | Nome del gruppo nell'IdP → ruolo della console Wäg. Inviata come sostituzione dell'intera mappa |
orgMap | Valore dell'IdP → id dell'organization Wäg. Sostituzione dell'intera mappa |
roleMap e orgMap vengono sostituite per intero a ogni applicazione, non unite, così il gateway contiene sempre esattamente ciò che è dichiarato qui — eliminare una voce elimina la mappatura.
Lasciare defaultOrgID non impostato va bene quando il Gateway ha un'organization: gli utenti finiscono lì anziché senza organization, il che è importante perché un utente senza organization non riceve nessuno dei grant di accesso ai modelli con radice nell'organization.
Gli endpoint si trovano sotto /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/ServiceProviderConfigPunta il connettore SCIM del tuo IdP a quell'URL base con il bearer token di tokenSecretRef. Senza il token ogni chiamata risponde 401.
Il token è una variabile d'ambiente, non un valore di configurazione memorizzato
L'operatore proietta il token sui pod del gateway come WAEG_EE_SCIM_TOKEN invece di scriverlo nello store di configurazione sigillato del gateway. Quello store non può mai essere riletto, quindi una copia scritta lì diventerebbe silenziosamente obsoleta nel momento in cui ruoti il Secret. Come variabile d'ambiente, ruotare il Secret e lasciare che i pod si riavviino è l'intera procedura di rotazione.
Richiede la licenza Enterprise del gateway — senza di essa il modulo risponde 402 — e nient'altro.
Non è la funzionalità sso-scim della piattaforma
La licenza di AI Core ha una propria funzionalità sso-scim. Si tratta di una capacità di piattaforma separata, non ancora implementata, e non ha alcuna relazione con il modulo del gateway descritto qui.
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": viewerAutenticazione JWT del data plane
Wäg può convalidare un JWT bearer del tuo IdP sulle richieste del data plane, così i chiamanti presentano un token invece di — o insieme a — una chiave. Si attiva con il blocco condiviso spec.instance.jwtAuth (lo stesso campo usato da LiteLLM) e si regola con spec.waeg.jwt, specifico di Wäg.
spec.instance.jwtAuth | Modulo EE jwt di Wäg |
|---|---|
enabled | enabled |
publicKeyURL | jwksUrl — l'endpoint JWKS da cui vengono recuperate le chiavi di firma dei token |
issuer | issuer — l'iss atteso |
audience | audience — l'aud attesa |
userIDJWTField | subjectClaim — quale claim identifica il chiamante |
spec.waeg.jwt | Descrizione |
|---|---|
appClaim | Claim che identifica l'applicazione chiamante. Predefinito azp |
appClaimFallbacks | Claim provati in ordine quando appClaim è assente. Sostituzione dell'intero elenco — un elenco vuoto cancella i valori predefiniti di Wäg (appid, client_id) |
tenantClaim | Claim che contiene l'organization/il tenant (altrimenti Wäg ripiega su tenant_id, poi tid) |
requireRegisteredApplication | Rifiuta i token la cui applicazione non è un WaegApplication registrato |
allowMasterKey | Mantiene funzionante la master key sul data plane. Predefinito true |
allowVirtualKeys | Mantiene funzionanti le virtual key sul data plane. Predefinito true |
insecureSkipVerify | Accetta firme dei token non verificate. Solo per lo sviluppo — Wäg lo rifiuta in un ambiente di produzione e insieme a qualsiasi flag di hardening |
Gli interruttori di hardening possono impedire l'avvio del gateway
allowMasterKey: false e allowVirtualKeys: false limitano il data plane ai soli JWT, e Wäg si rifiuta di avviarsi se il resto non è coerente: jwtAuth.enabled: true, un publicKeyURL (JWKS), un'audience e insecureSkipVerify: false. Un flag di hardening configurato a metà mette fuori servizio il gateway invece di degradarlo — imposta entrambe le parti nella stessa applicazione.
allowVirtualKeys: false blocca anche ogni chiave di applicazione, inclusa quella della ChatUI. Modificalo consapevolmente.
Le mappature dei claim specifiche di LiteLLM non vengono applicate.userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField e adminJWTScope non hanno un equivalente in Wäg: Wäg convalida il token ma non ne ricava mai ruoli o team — l'autorizzazione proviene da OpenFGA e dalle applicazioni registrate. Impostarli viene segnalato nella condizione FeaturesSupported e in status.unsupportedFeatures invece di essere scartato silenziosamente. Usa invece spec.waeg.jwt.requireRegisteredApplication e spec.waeg.modelAccess.
Richiede la licenza Enterprise del gateway (entitlement jwt_api); senza di essa il modulo risponde 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 # -> Wäg's subjectClaim
waeg:
jwt:
appClaim: azp
tenantClaim: tid
requireRegisteredApplication: true
# allowMasterKey: false # only together with the four settings aboveBranding della console
Una console Wäg con licenza Enterprise adotta per impostazione predefinita il theme pack Navique. L'operatore materializza il pack integrato in una ConfigMap di sua proprietà — <gateway-name>-branding, chiave theme-pack.json — e vi fa puntare l'istanza; il waeg-operator la invia poi via POST alla API di branding.
| Situazione | Risultato |
|---|---|
waeg.brandingConfigMapRef è impostato, la licenza include custom-branding | Prevale il tuo pack. L'operatore non crea nulla di proprio; CustomBranding=True |
waeg.brandingConfigMapRef è impostato, la licenza non include custom-branding | Viene applicato il pack Navique e CustomBranding=False (CustomBrandingUnlicensed) ne indica il motivo. Il riferimento resta, così il tuo pack si applica non appena la licenza lo consente |
waeg.defaultBranding: false | Opt-out — la console mantiene l'aspetto di Wäg |
Nessun instance.licenseSecretRef | Non viene creato nulla. Wäg rifiuta il branding senza licenza, quindi una ConfigMap qui pubblicizzerebbe soltanto uno stile che la console non potrà mai mostrare |
| Altrimenti (il caso predefinito) | Viene applicato il pack Navique |
La ConfigMap viene riscritta a ogni riconciliazione. Il pack è incluso nell'operatore, quindi un aggiornamento dell'operatore fa avanzare la console invece di bloccarla sul pack installato per primo — il che significa anche che modificarla a mano è inutile: la riconciliazione successiva annulla la modifica. Per usare uno stile tuo (white-label, funzionalità soggetta a licenza custom-branding), pubblica una ConfigMap e fai puntare brandingConfigMapRef ad essa — oppure usa Branding del gateway nella pagina del gateway della console di gestione, che convalida il pack, ne mostra un'anteprima e lo collega (sullo Stack del gateway, se è uno Stack a gestirlo). Uno Stack inoltra spec.gateway.waeg.brandingConfigMapRef / defaultBranding.
spec:
instance:
licenseSecretRef: { name: waeg-enterprise-license, key: license }
waeg:
defaultBranding: true # the default; false keeps Wäg's own livery
# brandingConfigMapRef: { name: my-theme-pack, key: theme-pack.json }La ChatUI si connette come applicazione
Una ChatUI con gatewayRef che punta a un gateway Wäg viene collegata automaticamente, ma attraverso un oggetto diverso da quello usato con LiteLLM.
Wäg non ha virtual key autonome: una chiave appartiene a un'applicazione — un oggetto di tenancy con radice nell'organization, con propri accessi ai modelli, budget e audit trail. L'operatore registra quindi la ChatUI come WaegApplication e genera un WaegVirtualKey associato:
WaegApplication (the chat UI's identity) → WaegVirtualKey (its credential)La differenza pratica è l'attribuzione: spesa, rate limit e voci di audit vengono imputati alla "chat UI" invece che a una chiave anonima, e l'applicazione compare nell'inventario dei deployer previsto dall'EU AI Act di Wäg con una finalità dichiarata.
Ciò richiede spec.organization sul Gateway, perché le applicazioni hanno radice nell'organization. In sua assenza la ChatUI viene rifiutata con questa motivazione invece di restare in attesa di una chiave che non potrà mai essere generata — imposta invece spec.gateway.{url, apiKeySecretRef} per usare una chiave generata da te.
La rotazione delle credenziali funziona come con LiteLLM: incrementare l'indice di rotazione genera una nuova chiave in un Secret nuovo invece di sovrascriverla. L'applicazione resta stabile attraverso le rotazioni — è l'identità, non la credenziale.
Crittografia in transito
Quando la CA di piattaforma ha emesso un certificato, l'operatore attiva WaegInstance.spec.tls in modo che il gateway serva HTTPS all'interno del pod — il traffico è cifrato fino al pod, non solo fino all'edge. Il gateway ha un unico listener, quindi questo cambia tutto insieme e il waeg-operator si adegua: probe, appProtocol del Service, status.endpoint, il ServiceMonitor e l'URL di scaling di KEDA.
Insieme viene fornito il bundle della CA di piattaforma. Le chiamate alla Admin API effettuate dall'operatore stesso passano su quella connessione, quindi senza la CA fallirebbero la verifica x509 rispetto alla CA interna al cluster, trascinando con sé il branding e ogni CR di prodotto.
Richiede il gateway 1.0.0-rc.9 o successivo
Il TLS all'interno del pod funziona solo a partire dal gateway 1.0.0-rc.9. Le immagini precedenti andavano in panic all'avvio ogni volta che spec.tls era abilitato (rustls trovava due crypto provider collegati e nessuno installato). Il waeg-operator si rifiuta di abilitarlo su un'immagine più vecchia e riporta TLSUnsupportedGateway invece di consegnarti un crash loop, quindi fissare un instance.image più vecchio lascia il gateway in chiaro invece di romperlo. waeg-operator 1.3.0 usa rc.9 come predefinito, quindi il percorso predefinito non presenta problemi.
Guardrail
spec.guardrailRefs funziona su un gateway Wäg, ma la struttura è diversa. LiteLLM prevede un oggetto per ciascuna modalità di esecuzione; Wäg ne prevede tre:
WaegGuardrailItem (one per Guardrail) → WaegGuardrailChain (orders them)
→ WaegGuardrailBinding (applies the chain)L'operatore li emette tutti e tre con scope: platform, quindi non è richiesta alcuna organization. Il binding usa mode: floor, cioè viene eseguito sempre — un binding a livello di organization o di team aggiunto in seguito nella console Wäg non può disattivare un guardrail collegato dalla piattaforma.
Tre impostazioni degli item sono determinanti, e l'operatore le sceglie per te:
| Impostazione | Motivo |
|---|---|
role | I motori transform riscrivono il contenuto. Se configurato come item policy, Wäg tratta la riscrittura come una decisione di redazione e la sostituzione durante lo streaming silenziosamente non avviene. I motori del catalogo dichiarano il proprio ruolo |
onError | Ricavato da unreachableFallback (predefinito fail_closed). Wäg usa per impostazione predefinita il fail-open, quindi con un motore che risponde deliberatamente 502 il prompt originale verrebbe altrimenti inoltrato al provider |
mode | Wäg ha una fase during_call che LiteLLM non ha. Un motore di trasformazione ne ha bisogno, altrimenti le risposte in streaming non vengono riscritte. Il pseudonymizer incluso esegue tutte e tre le fasi su Wäg |
I guardrail esterni richiedono spec.waeg.path. Wäg invia la POST a un URL indicato per intero, mentre LiteLLM aggiunge /beta/litellm_basic_guardrail_api — quindi l'operatore non può indovinare dove un endpoint gestito dall'utente espone il protocollo Wäg. In sua assenza il guardrail viene saltato e un Event lo segnala, invece di lasciare che l'operatore indovini un percorso facendo fallire ogni scansione mentre il gateway appare sano. I motori del catalogo forniscono il proprio percorso (il pseudonymizer espone /v1/waeg/check).
spec.waeg su un Guardrail contiene anche i campi facoltativi role, timeoutMs, forwardIdentity e forwardSessionId. Gli ultimi due contano per i motori che mantengono uno stato per sessione: le mappature dei nomi del pseudonymizer restano coerenti tra i turni solo quando Wäg inoltra un'identità, altrimenti ripiega sull'id della richiesta, che tiene insieme una singola richiesta ma non una conversazione.
Cosa non viene applicato
Alcuni campi di questo CRD non vengono ancora applicati a un gateway Wäg. Impostarne uno non rompe nulla — viene elencato in status.unsupportedFeatures, la condizione FeaturesSupported passa a False con motivo UnsupportedByBackend e viene generato un Event.
| Campo | Motivo |
|---|---|
instance.saltKey | Wäg non ha una salt key — usa waeg.dataEncryptionKeySecretRef |
instance.storePromptsInLogs | Nessuna impostazione di runtime equivalente |
instance.healthCheck | Wäg espone probe fisse /healthz e /readyz e nessun ciclo in background configurabile |
instance.jwtAuth — solo le mappature dei claim | L'autenticazione JWT in sé viene applicata (vedi Autenticazione JWT del data plane). Vengono rifiutati solo userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField e adminJWTScope: Wäg autorizza tramite OpenFGA e le applicazioni registrate anziché ricavando i ruoli dal token |
instance.rolePermissions | Wäg non ha i role_permissions di LiteLLM — usa waeg.modelAccess per le ACL dei modelli con radice nell'organization |
models[].rpm / tpm / timeout / maxTokens | L'oggetto modello di Wäg non prevede limiti di richieste o di token, e il gateway non ha alcun timeout di richiesta per modello. Un limite per modello è un WaegBudget con scope: model (un pool a livello di organization per l'alias), che questo operatore non emette |
organization.budgetDuration | Le policy di budget di Wäg non hanno un campo per il periodo |
Al di fuori della spec del Gateway, le risorse di identità sono pienamente supportate su Wäg: Identity, Organization e Team emettono WaegUser, WaegOrganization, WaegTeam e le policy WaegBudget a livello di organization e di team. Falle puntare a questo Gateway con type: gateway e seguiranno il suo spec.type — vedi Scegliere il backend.
Due campi di identità non vengono applicati su Wäg: teamRefs e budget di un'Identity. Wäg mantiene l'appartenenza sul team (elenca l'indirizzo nei members del Team), e un limite per utente è un WaegBudget con scope: user. Un'Identity Wäg riceve inoltre un Secret con una password della console generata e riporta Ready solo quando il WaegUser è Synced — vedi Account Wäg.
Rifiutato a monte
Invece di emettere una risorsa che resterebbe bloccata, l'operatore rifiuta questi casi con Ready=False, motivo InvalidTarget:
type: waegsenzaspec.waego senzaspec.waeg.clickhousewaeg.worker.enabledcontopology: AllInOne- più di una replica API senza
waeg.redis waeg.modelAccesssenzaspec.organization
Esempio
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 # never share a schema with a LiteLLM gateway
instance:
replicas: 2
masterKey: { autoGenerate: true }
licenseSecretRef: { name: waeg-enterprise-license, key: license } # unlocks every EE module
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 # the default; `single` drops ClickHouse entirely
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }
# openfga omitted -> the operator deploys a Postgres-backed OpenFGA
# defaultBranding defaults to true -> the Navique theme pack is applied
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]Verifica cosa è stato saltato:
kubectl -n forge-gateway get gateway waeg-gateway \
-o jsonpath='{.status.conditions[?(@.type=="FeaturesSupported")].message}'In uno Stack
Stack.spec.gateway.type: waeg lo seleziona per un intero Stack. Lo Stack allora richiede datastores.clickhouse.enabled (altrimenti l'admission lo rifiuta), collega automaticamente il proprio ClickHouse e il proprio Redis come secondo piano e assegna al database logico del gateway il nome waeg, così non finisce mai su uno schema LiteLLM. Stack.spec.gateway.waeg inoltra le opzioni di topologia/worker/OpenFGA/autoscaling, oltre a scim e jwt; la licenza Enterprise viene dichiarata una sola volta come Stack.spec.gateway.licenseSecretRef, e Stack.spec.gateway.jwtAuth attiva l'autenticazione JWT del data plane.
Status
status.url espone l'endpoint del gateway (pubblico quando l'ingress è abilitato, altrimenti interno al cluster), oltre alla readiness dell'istanza, al numero di modelli/team e alla readiness del database. status.unsupportedFeatures elenca ogni campo configurato che il tipo di gateway selezionato non può rispettare (sempre vuoto per type: litellm); la condizione FeaturesSupported lo rispecchia.
Note sulla licenza
Team, organization e budget fanno parte della funzionalità multi-tenancy; più di un Gateway richiede un limite gateways superiore al valore predefinito Community di 1. Vedi Edizioni e licenze.