Skip to content

Stack ​

Geltungsbereich: namespaced · Optionale Klammer-Ressource · Lizenziert (Feature managed-deployment + eine stacks-Obergrenze)

Stack ist eine optionale Klammer-Ressource, die ein kuratiertes Bündel der granularen Ressourcen erstellt und besitzt, deren Referenzen automatisch verdrahtet und den Fortschritt aggregiert — eine Bereitstellungserfahrung im Stil eines ARM-Deployments aus einem Guss.

Sie ersetzt die komponentenspezifischen Ressourcen nicht. Die granularen CRDs bleiben eine erstklassige Möglichkeit, die Plattform von Hand zusammenzusetzen; Stack setzt lediglich obenauf für Teams, die ein einziges Objekt zur Verwaltung einer gesamten Umgebung wünschen.

Wann Sie es verwenden sollten ​

  • Verwenden Sie Stack, wenn Sie eine vollständige Umgebung (Secrets + Datenspeicher + Gateway + Observability + UI) als eine Einheit bereitstellen und verfolgen möchten, wobei der Operator die Querverweise für Sie verdrahtet und den Status an einer einzigen Stelle zusammenführt.
  • Verwenden Sie die granularen Ressourcen, wenn Sie feingranulare Kontrolle wünschen, Ressourcen über Namespaces hinweg von Hand zusammensetzen oder Datenspeicher zwischen mehreren unabhängig verwalteten Workloads gemeinsam nutzen.

Was es tut ​

  • Erstellt + besitzt die gebündelten Komponenten (der Operator setzt Owner-Referenzen, sodass das gesamte Bündel zusammen mit dem Stack per Garbage-Collection entfernt wird).
  • Verdrahtet automatisch die Referenzen zwischen ihnen (Gateway→Postgres, Gateway→Observability, ChatUI→Gateway, Workloads→Datenspeicher) — dies setzt die Fähigkeit auto-wiring voraus.
  • Aggregiert den Fortschritt in status.components[] und status.endpoints, sodass ein einziges kubectl get stack die Bereitschaft und die URLs der gesamten Umgebung anzeigt.

Drift-Richtlinie ​

Standardmäßig erzwingt der Stack die kuratierte Spezifikation kontinuierlich auf seinen Kindern: Jede manuelle Änderung, die Sie an einer erzeugten Komponenten-CR vornehmen (etwa das Erhöhen der Speichergröße eines Datenspeichers oder der Replica-Anzahl eines Workloads), wird beim nächsten Reconcile zurückgesetzt. spec.driftPolicy erlaubt es, dieses Verhalten zu lockern:

driftPolicyVerhalten
Enforce (Standard)Die kuratierte Spezifikation wird bei jedem Reconcile erneut durchgesetzt — manuelle Änderungen an Kindern werden zurückgesetzt (vollständige Drift-Korrektur).
AdoptDie kuratierte Spezifikation wird bei der Erstellung sowie immer dann angewendet, wenn sich die Spezifikation des Stack selbst ändert (ein „Redeploy“ im ARM-Stil). Zwischen Stack-Änderungen bleiben manuelle Änderungen pro Komponente erhalten.

Adopt bildet das Verhalten eines Azure-Resource-Manager-Deployments nach: Der Stack legt die Ressourcen an und verdrahtet sie, anschließend können Sie jede Komponente direkt anpassen. Eine Änderung am Stack selbst setzt jedes Kind erneut durch und überschreibt Ihre Drift — behandeln Sie eine Stack-Änderung daher wie ein Redeploy.

Die Owner-Referenz bleibt unter beiden Richtlinien bestehen, sodass das Löschen des Stack stets per Kaskade alle erzeugten Komponenten entfernt (und abbaut).

yaml
apiVersion: core.navique.com/v1alpha1
kind: Stack
metadata:
  name: forge
spec:
  driftPolicy: Adopt   # Komponenten nach der Erstellung individuell anpassbar machen
  # ...

Dimensionierung der Datenspeicher ​

Jeder verwaltete Datenspeicher unter spec.datastores akzeptiert eine optionale Kurzform resources, die CPU und Arbeitsspeicher des zugrunde liegenden Containers dimensioniert. Sie wird für die Datenspeicher Postgres, ClickHouse, Redis und MongoDB berücksichtigt (nur im Modus managed) und in das erzeugte Kind PostgresCluster / ClickHouseCluster / RedisInstance / MongoCluster als dessen managed.resources übernommen.

FeldAbbildung auf
cpuCPU-Anforderung (request)
memoryArbeitsspeicher-Anforderung (request)
memoryLimitArbeitsspeicher-Limit

Die Kurzform hat bewusst kein CPU-Limit (CPU-Limits drosseln, statt zu schützen). Wenn Sie eines benötigen, nutzen Sie die eigenständige Datenspeicher-CRD direkt, die ein vollständiges managed.resources akzeptiert.

yaml
apiVersion: core.navique.com/v1alpha1
kind: Stack
metadata:
  name: forge
spec:
  datastores:
    postgres:
      storageSize: 20Gi
      resources: { cpu: 500m, memory: 1Gi, memoryLimit: 2Gi }
    clickhouse:
      storageSize: 50Gi
      resources: { cpu: 500m, memory: 2Gi, memoryLimit: 3Gi }
    redis:
      storageSize: 5Gi
      resources: { cpu: 100m, memory: 256Mi, memoryLimit: 512Mi }
    mongo:
      storageSize: 20Gi
      resources: { cpu: 250m, memory: 1Gi, memoryLimit: 2Gi }

Wird resources weggelassen, gelten die Standardwerte des Upstream-Charts (ClickHouse behält sein vom Operator abgestimmtes 3Gi-Speicherlimit, um OOMs unter Langfuse-Last zu vermeiden).

Gateway-Implementierung wählen ​

spec.gateway.type wählt das Gateway-Produkt: litellm (Standard) oder waeg (das Wäg-Gateway über den waeg-operator). Die Unterschiede finden Sie unter Gateway → Wäg-Gateway.

Mit type: waeg gilt für den Stack:

  • datastores.clickhouse.enabled ist im Standardmodus split erforderlich — die Admission lehnt den Stack sonst ab, denn dort liegen Wägs Analysedaten. Setzen Sie gateway.waeg.storageMode: single, um sie stattdessen im Postgres des Stacks zu halten; damit entfällt die Anforderung vollständig — siehe Wäg-Analysespeicher;
  • das eigene ClickHouse des Stacks (und Redis, sofern aktiviert) wird automatisch als zweite Speicher-Ebene des Gateways verdrahtet — nur im split-Modus;
  • auf diesem ClickHouse wird die Datenbank waeg deklariert, damit das Gateway überhaupt starten kann: Wäg legt nur seine Tabellen an, nie seine Datenbank (ClickHouseCluster → databases[]);
  • die logische Postgres-Datenbank des Gateways heißt waeg statt litellm, damit sich die beiden Implementierungen nie ein Schema teilen;
  • es wird keine Langfuse-Referenz gesetzt — Wäg kann Traces nicht deklarativ exportieren, daher verdrahtet der Stack nichts, was das Gateway nur ablehnen müsste.

spec.gateway.waeg reicht die Wäg-spezifischen Einstellungen durch: topology, jobWorkers, worker, storageMode, openfga, autoscaling, bootstrapAdmin, dataEncryptionKeySecretRef, modelAccess, scim und jwt. Die Enterprise-Lizenz wird einmalig über spec.gateway.licenseSecretRef deklariert, die Organisation des Gateways über spec.gateway.organization — die ein Wäg-Gateway braucht, bevor sich eine ChatUI mit ihm verbinden kann.

yaml
spec:
  datastores:
    clickhouse: { enabled: true, backup: { enabled: true, provider: s3, s3: { destinationPath: s3://b } } }
    redis:      { enabled: true }
  gateway:
    enabled: true
    type: waeg
    licenseSecretRef: { name: waeg-enterprise-license, key: license }
    waeg:
      topology: Split
      worker: { enabled: true, replicas: 2 }
      scim:
        enabled: true
        tokenSecretRef: { name: waeg-scim-token, key: scim-token }
        defaultRole: viewer
      jwt:
        appClaim: azp
        requireRegisteredApplication: true

Die weiter unten beschriebenen LiteLLM-spezifischen Felder (rolePermissions, healthCheck, storePromptsInLogs, guardrails, modellspezifische Limits — und die Claim-Zuordnungen von jwtAuth) werden bei type: waeg über die Bedingung FeaturesSupported des Gateway-Kindes gemeldet — sie werden nie stillschweigend verworfen.

Wäg-Analysespeicher ​

spec.gateway.waeg.storageMode bestimmt, wo die Analysedaten des Gateways (Request-Logs, Nutzung, Kosten) liegen. Es ist die Stack-Durchreichung von Gateway.spec.waeg.storageMode.

ModusAnalysedaten liegen indatastores.clickhouse.enabled
split (Standard)dem ClickHouse des Stackserforderlich
singledem Postgres des Stacksnicht nötig — es wird kein ClickHouse provisioniert

Wägs eigener Upstream-Standard ist single und gilt dort als empfohlener Einstieg; dieser Operator behält split als Standard bei — aus Kontinuität und weil es bei hohem Volumen trägt. Mit single provisioniert der Stack überhaupt kein ClickHouse, und die Admission-Regel, die eines verlangte, greift nicht mehr. Der Standard ohne ClickHouse wird abgelehnt mit:

gateway.type=waeg with the default storageMode 'split' requires
datastores.clickhouse.enabled; set gateway.waeg.storageMode: single to keep
analytics in Postgres instead

Ein Moduswechsel migriert die Analysehistorie nicht

Die beiden Ebenen sind getrennte Speicher. Ein geänderter storageMode an einem laufenden Stack zeigt das Gateway auf den jeweils anderen; die bereits gesammelten Analysedaten bleiben, wo sie sind, und weder der Operator noch Wäg kopiert sie hinüber.

yaml
apiVersion: core.navique.com/v1alpha1
kind: Stack
metadata:
  name: forge
  namespace: forge
spec:
  datastores:
    postgres:
      enabled: true
      storageSize: 20Gi
      backup: { enabled: true, provider: s3, s3: { destinationPath: s3://forge-pg } }
    redis: { enabled: true }
    # gar kein clickhouse-Block — storageMode: single braucht keines
  gateway:
    enabled: true
    type: waeg
    organization: { name: forge }
    waeg:
      storageMode: single

Im Standardmodus split deklariert der Stack zusätzlich die Datenbank waeg auf seinem verwalteten ClickHouse, denn Wäg legt nur seine Tabellen an und niemals seine Datenbank — siehe ClickHouseCluster → databases[].

Organisation des Gateways ​

spec.gateway.organization legt die oberste Organisation des Gateways an — jenes Objekt, auf das Budgets und Ratenlimits aufaddiert werden. Das Feld wird unverändert an spec.organization des Gateway-Kindes durchgereicht.

FeldBeschreibung
name (erforderlich)Name der Organisation
maxBudgetAusgabenobergrenze der Organisation
budgetDurationZeitraum, nach dem die Obergrenze zurückgesetzt wird (z. B. 30d). Nur LiteLLM — Wägs Budget-Richtlinien kennen kein Periodenfeld
rpmLimitObergrenze für Anfragen pro Minute
tpmLimitObergrenze für Tokens pro Minute

Bei einem Gateway mit type: litellm bleibt eine Organisation optional — Teams, Schlüssel und Modelle funktionieren auch ohne sie.

Ein Wäg-Gateway braucht sie, bevor sich eine ChatUI verbinden kann

Bei type: waeg ist dieses Feld erforderlich, um eine ChatUI automatisch zu verdrahten. Wäg kennt keinen eigenständigen virtuellen Schlüssel: Ein Schlüssel gehört zu einer Anwendung, und Anwendungen sind an eine Organisation gebunden — ohne Organisation gibt es also nichts, unter dem das Zugangsmittel der ChatUI ausgestellt werden könnte. Bevor es dieses Feld gab, blieb eine ChatUI in einem Stack mit gateway.type: waeg und chatUI.enabled: true hängen und meldete

Gateway "…" is type=waeg and has no spec.organization: a Wäg virtual key belongs
to an application, and applications are org-rooted

wobei sich aus einem Stack heraus nichts setzen ließ, um das zu erfüllen — das granulare Gateway hatte spec.organization, der Stack hatte keine Durchreichung. Wird das Feld hier gesetzt, registriert der Operator die ChatUI als WaegApplication unter dieser Organisation und stellt ihren WaegVirtualKey dagegen aus.

An der Organisation hängen außerdem die Berechtigungen aus spec.gateway.waeg.modelAccess: Die Modell-ACL wird gegen sie deklariert und bleibt ohne sie wirkungslos.

yaml
spec:
  datastores:
    clickhouse: { enabled: true, backup: { enabled: true, provider: s3, s3: { destinationPath: s3://forge-ch } } }
    redis:      { enabled: true }
  gateway:
    enabled: true
    type: waeg
    licenseSecretRef: { name: waeg-enterprise-license, key: license }
    organization:
      name: navique-ag
      maxBudget: 2000
      rpmLimit: 1000
      tpmLimit: 500000
    waeg:
      modelAccess:                  # an die Organisation gebunden: braucht sie
        openByDefault: false
        fallbackMode: deny
        grants:
          - models: [premium, economy]
  chatUI:
    enabled: true                   # wird als Anwendung unter navique-ag verdrahtet

Welche Objekte der Operator dabei erzeugt, steht unter ChatUI verbindet sich als Anwendung; welche Organisationsfelder Wäg nicht umsetzen kann, unter Was nicht angewendet wird.

Enterprise-Lizenz des Gateways ​

spec.gateway.licenseSecretRef liefert die Enterprise-Lizenz des Gateways und wird an instance.licenseSecretRef des Gateways durchgereicht. Bei type: litellm ist das der LiteLLM-Enterprise-Schlüssel, bei type: waeg die Wäg-Enterprise-Lizenz — und damit der Unterschied zwischen einem funktionierenden Enterprise-Gateway und einem, das mit 402 license_required antwortet.

Jedes Wäg-Enterprise-Modul hängt daran — SSO, SCIM, Audit, CMEK, FIPS und das Konsolen-Branding — und die Durchsetzung ist bedingungslos. Ohne dieses Feld hat ein vom Stack verwaltetes Wäg-Gateway also überhaupt kein erreichbares Enterprise-Feature.

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

Der Secret-Schlüssel ist standardmäßig license. Lassen Sie das Secret vom SecretsManagement-Backend des Stacks materialisieren, statt es von Hand anzulegen.

Branding folgt der Lizenz

Sobald ein Wäg-Gateway lizenziert ist, wendet der Operator automatisch das eingebaute Navique-Theme-Pack auf dessen Konsole an (eine ihm gehörende ConfigMap <gateway>-branding). Der Stack hat dafür kein Abwahlfeld — für ein eigenes Pack oder um Wägs eigenes Erscheinungsbild zu behalten, verwenden Sie das granulare Gateway mit waeg.brandingConfigMapRef / waeg.defaultBranding: false.

Dimensionierung des Gateways ​

spec.gateway.resources dimensioniert den LiteLLM-Proxy-Container. Anders als die Datenspeicher-Kurzform ist dies ein vollständiges Kubernetes-ResourceRequirements (Sie können also ein CPU-Limit setzen); es wird direkt an instance.resources des erzeugten Gateway → spec.resources der LiteLLMInstance durchgereicht. Weglassen belässt den Standard des litellm-operator.

yaml
spec:
  gateway:
    resources:
      requests: { cpu: "500m", memory: 512Mi }
      limits:   { cpu: "2", memory: 2Gi }

Gateway-Proxy-Build anpinnen ​

spec.gateway.image pinnt das LiteLLM-Proxy-Image. Der Wert wird an das erzeugte Gateway (instance.image) und von dort an die LiteLLMInstance (spec.image) durchgereicht.

yaml
spec:
  gateway:
    image:
      repository: ghcr.io/berriai/litellm
      tag: v1.94.0-dev.2

Lassen Sie das Feld leer, sofern Sie nicht gezielt einen bestimmten Build qualifizieren. Ohne Pin verwendet der litellm-operator seinen eigenen Standard-Tag — denjenigen, der gegen seinen Datenbank-Migrations-Entrypoint validiert ist. Ein beliebiger Tag kann die Migration zerstören und das Gateway lahmlegen.

Patchen Sie nicht stattdessen das untergeordnete Gateway von Hand: mit der Standard-Drift-Richtlinie Enforce wendet der Stack seine Kindressourcen bei jedem Reconcile erneut an und setzt Ihre Änderung innerhalb von Sekunden zurück. Deklarieren Sie den Pin hier.

Gateway-JWT-Auth & RBAC ​

spec.gateway.jwtAuth und spec.gateway.rolePermissions aktivieren die JWT-basierte API-Authentifizierung und den modellbezogenen Rollenzugriff am Gateway des Stacks — direkt an instance.jwtAuth / instance.rolePermissions des Gateways durchgereicht. Die Felder finden Sie unter JWT-API-Authentifizierung & RBAC. Erfordert eine LiteLLM-Enterprise-Lizenz.

yaml
spec:
  gateway:
    jwtAuth:
      enabled: true
      userRolesJWTField: roles
      userAllowedRoles: ["basic_user"]
      enforceRBAC: true
    rolePermissions:
      internal_user: { models: ["anthropic-claude"] }

Wäg-Konsolen-Branding ​

spec.gateway.waeg.brandingConfigMapRef und defaultBranding werden an das Konsolen-Branding des Gateways durchgereicht: standardmäßig das Navique-Theme, Ihr eigenes Theme-Pack mit dem lizenzierten Feature custom-branding oder Wägs eigenes Erscheinungsbild mit defaultBranding: false.

Auf einem Wäg-Gateway ​

spec.gateway.jwtAuth schaltet die JWT-Authentifizierung auch bei type: waeg ein — enabled, publicKeyURL (→ Wägs jwksUrl), issuer, audience und userIDJWTField (→ subjectClaim) werden angewendet. Die Wäg-spezifischen Einstellungen liegen unter spec.gateway.waeg.jwt; spec.gateway.rolePermissions sowie die Claim-Zuordnungen (userRolesJWTField, userRoleJWTField, userAllowedRoles, enforceRBAC, userIDUpsert, teamIDsJWTField, adminJWTScope) werden stattdessen als nicht unterstützt gemeldet: Wäg autorisiert über OpenFGA und registrierte Anwendungen. Siehe JWT-Authentifizierung auf der Datenebene.

yaml
spec:
  gateway:
    type: waeg
    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
    waeg:
      jwt:
        appClaim: azp
        requireRegisteredApplication: true

Härtungsschalter können den Start des Gateways verhindern

waeg.jwt.allowMasterKey: false oder allowVirtualKeys: false erfordern jwtAuth.enabled: true, eine publicKeyURL, eine audience und insecureSkipVerify: false — sonst startet Wäg nicht. allowVirtualKeys: false legt außerdem jeden Anwendungsschlüssel still, auch den der ChatUI des Stacks.

Wäg-SCIM-Bereitstellung ​

spec.gateway.waeg.scim aktiviert Wägs SCIM-v2-Endpunkte am Gateway des Stacks, sodass Ihr IdP Konsolenbenutzer direkt anlegt und wieder entfernt. Das ist unabhängig von SSO — es funktioniert auch ohne konfiguriertes interaktives Login — und erfordert spec.gateway.licenseSecretRef.

yaml
spec:
  gateway:
    type: waeg
    licenseSecretRef: { name: waeg-enterprise-license, key: license }
    waeg:
      scim:
        enabled: true                                              # Standard
        tokenSecretRef: { name: waeg-scim-token, key: scim-token }  # erforderlich
        defaultRole: viewer
        orgSource: waeg
        roleMap: { "AI Platform Admins": admin }

Das Bearer-Token wird als WAEG_EE_SCIM_TOKEN auf die Gateway-Pods projiziert; die Rotation besteht also allein darin, das Secret zu erneuern. Die Endpunkte liegen unter /waeg/admin/v1/ee/scim/v2. Alle Felder finden Sie unter SCIM-Bereitstellung.

ChatUI-Datenspeicher: MongoDB & Meilisearch ​

MongoDB (der Konversationsspeicher) und Meilisearch (Suche) versorgen die ChatUI und werden unter datastores.mongo / datastores.meilisearch konfiguriert — mit demselben Schalter mode: managed | external wie die übrigen Datenspeicher. Sie werden nur bereitgestellt, wenn die ChatUI aktiviert ist, und sind standardmäßig managed.

  • managed (Standard): Der Stack stellt eine MongoCluster / MeilisearchInstance bereit, besitzt sie und verdrahtet die ChatUI damit. Größe über storageSize (Meilisearch berücksichtigt zusätzlich storageClass / resources).
  • external: Es wird keine Datenspeicher-CR bereitgestellt; die ChatUI wird mit einer benutzerverwalteten Instanz verdrahtet. Mongo benötigt connectionSecretRef (Secret-Schlüssel MONGO_URI, überschreibbar via .key). Meilisearch benötigt host plus ein connectionSecretRef mit dem Master-Key (Secret-Schlüssel MEILI_MASTER_KEY).
yaml
spec:
  datastores:
    mongo:
      mode: managed
      storageSize: 10Gi
    meilisearch:
      mode: external
      host: https://search.example.com
      connectionSecretRef: { name: meili-master-key }   # Schlüssel MEILI_MASTER_KEY

MCP-Server (Websuche & Tools) ​

chatUI.mcp bindet MCPServer-Server in die LibreChat-Oberfläche des Stacks ein. Zwei kombinierbare Wege:

  • catalog — gebündelte Server, die der Stack als MCPServer-Kinder (eines pro Schlüssel) bereitstellt und besitzt und in die ChatUI einbindet. Erster Eintrag: websearch (Enterprise-Websuche). Erfordert die Lizenz bundled-mcp-catalog; ein nicht lizenzierter Eintrag wird Refused und übersprungen (die Oberfläche startet trotzdem).
  • refs — bindet bereits vorhandene MCPServer-CRs ein, die Sie selbst erstellen (Katalog oder extern — z. B. ein selbst betriebener clusterinterner MCP-Server), in beliebigen Namespaces. Der Stack referenziert sie, ohne ihren Lebenszyklus zu besitzen.
yaml
spec:
  chatUI:
    enabled: true
    mcp:
      catalog: [ websearch ]                 # Stack erstellt + bindet den MCPServer ein
      refs:
        - { name: my-internal-mcp }          # ein von Ihnen verwalteter MCPServer

Das Referenzieren eines MCP-Servers aktiviert automatisch den Agents-Endpunkt von LibreChat. Die MCPServer-Kinder des Stacks erscheinen in seinem Fortschrittsbaum und werden mit dem Stack abgebaut. Siehe MCPServer für die Serverdefinition und die automatische SSRF-Allowlist.

Guardrails (Datenpfad) ​

gateway.guardrails bindet Guardrail-Datenpfad-Guardrails in das Gateway des Stacks ein. Zwei kombinierbare Wege:

  • catalog — gebündelte Engines, die der Stack als Guardrail-Kinder bereitstellt und besitzt (eines pro Schlüssel) und in das Gateway einbindet. Erster Eintrag: pseudonymizer (PII-Pseudonymisierung). Erfordert die guardrail-(Enterprise-)Lizenz; ein nicht lizenzierter Eintrag wird Refused und übersprungen (das Gateway kommt trotzdem hoch, ungeschützt).
  • refs — binden vorhandene Guardrail-CRs ein, die Sie selbst anlegen (catalog oder externer HTTP-Guardrail), in beliebigem Namespace. Der Stack referenziert sie, ohne ihren Lebenszyklus zu besitzen.
yaml
spec:
  gateway:
    enabled: true
    guardrails:
      catalog: [ pseudonymizer ]             # Stack erstellt + bindet den Guardrail ein
      refs:
        - { name: my-guardrail }             # ein Guardrail, das Sie verwalten

Sowohl Katalog- als auch externe Guardrails sind lizenziert und werden über den vom Operator eingefügten Lizenz-Gate-Proxy geleitet (schließt im Fehlerfall, wenn die Lizenz ausläuft). Die Guardrail-Kinder des Stacks erscheinen in seinem Fortschrittsbaum und werden mit dem Stack abgebaut. Siehe Guardrail für die vollständige Definition.

Einen Guardrail innerhalb eines Stacks debuggen ​

Setze logLevel (und optional blockedReason) am Stack, nicht am Kind:

yaml
spec:
  gateway:
    guardrails:
      catalog: [ pseudonymizer ]
      logLevel: debug                        # ein Proxy-Logeintrag pro Anfrage
      blockedReason: "blocked by the Navique license gate"

Ein Stack überschreibt die kuratierte Spec jedes Kindes. Unter der Voreinstellung driftPolicy: Enforce wird ein kubectl patch des logLevel am Kind-Guardrail daher zurückgesetzt — und da der Stack seine Guardrail-Kinder beobachtet, löst der Patch genau den Reconcile aus, der ihn rückgängig macht. (Mit driftPolicy: Adopt bleibt ein manueller Patch erhalten, aber nur bis zur nächsten Änderung am Stack, und er schaltet die Drift-Korrektur für alle Kinder ab.) Diese Felder gelten für die catalog-Kinder, die der Stack besitzt; über refs eingebundene Guardrails gehören dir — setze logLevel dort direkt am CR.

Die Entscheidungszähler des Proxys brauchen überhaupt kein Opt-in — siehe Fehlersuche bei einem fehlgeschlagenen Guardrail-Aufruf.

Backups & Löschschutz ​

Da ein Stack der Weg ist, den nicht-technische Nutzer wählen, behandelt er zustandsbehaftete Daten defensiv.

Backups sind für verwaltete zustandsbehaftete Datenspeicher verpflichtend. Ein Stack muss ein aktiviertes backup für jedes verwaltete datastores.postgres, datastores.clickhouse und datastores.mongo setzen (dieselbe Menge, die automatisch gesperrt wird) — andernfalls wird er abgelehnt (Postgres bei der Admission via CEL; ClickHouse/Mongo beim Reconcile), sodass niemand einen Datenspeicher ohne Backups in Betrieb nimmt. Redis (Cache) und Meilisearch (ein neu aufbaubarer Index) sind ausgenommen. Der Block hat überall dieselbe anbieterneutrale Form (s3/azure/gcs + Zeitplan + Aufbewahrung) und wird in das verwaltete Kind übernommen; Postgres nutzt CNPG-natives PITR, ClickHouse einen clickhouse-backup CronJob, MongoDB einen mongodump+rclone CronJob. Externe Datenspeicher benötigen hier kein Backup — das verwalten Sie selbst. Setzen Sie datastores.requireBackup: false, um die Anforderung zu überspringen (fortgeschrittene Nutzer / CI).

Verwaltete Datenspeicher werden automatisch gesperrt (Lock). Wenn der Stack ein verwaltetes PostgresCluster, ClickHouseCluster oder MongoCluster (das die ChatUI-Konversationshistorie enthält) bereitstellt, erstellt er darauf zusätzlich ein Lock, sodass ein versehentliches kubectl delete blockiert wird. Redis (ein Cache) und Meilisearch (ein neu aufbaubarer Suchindex) werden nie gesperrt.

  • Dies ist Löschschutz auf der Datenebene: Das Löschen des Stack bleibt blockiert, bis Sie diese Locks entfernen — die Datenspeicher (und ihre PVCs) werden nie versehentlich zerstört. Der Stack meldet die blockierenden Lock(s) in seinem Status.
  • Der Stack erstellt jedes Lock einmal und stellt ein von Ihnen entferntes niemals wieder her — um also einen geschützten Stack zu löschen, führen Sie einfach zuerst kubectl delete lock <name> aus und löschen dann den Stack. Der Operator setzt das Lock nicht im Wettlauf erneut.
  • Setzen Sie datastores.protectData: false, um das Erstellen der Locks zu überspringen (bestehende Locks bleiben unberührt).
yaml
spec:
  datastores:
    postgres:
      storageSize: 20Gi
      backup:
        enabled: true
        provider: s3
        s3:
          destinationPath: s3://forge-backups/pg
          credentialsSecretRef: { name: pg-backup }
    clickhouse: { storageSize: 20Gi }
    redis: { storageSize: 5Gi }
    # protectData: false   # Verzicht auf die automatischen Locks (nicht empfohlen)

Namespace-Trennung ​

Standardmäßig landet jede Komponente im eigenen Namespace des Stack. Jeder Komponentenblock akzeptiert optional ein namespace, um sie woanders zu platzieren:

yaml
spec:
  gateway:   { namespace: forge-gateway }
  chatUI:    { namespace: forge-ui }
  observability: { type: langfuse }   # bleibt im Namespace des Stack

Wenn eine Komponente einen Namespace deklariert, wird der Stack:

  • den Namespace anlegen, falls er nicht existiert (und ihn beim Löschen bestehen lassen);
  • in jedem Namespace mit einem Workload ein SecretsManagement platzieren, damit der secretsRef jeder Komponente im selben Namespace lokal aufgelöst wird;
  • die namespaceübergreifenden Referenzen automatisch verdrahten (z. B. ein Gateway im einen Namespace zum Postgres/Langfuse im anderen) — Sie setzen weiterhin keine Refs von Hand.

Da Kubernetes-Owner-Referenzen Namespaces nicht überspannen können, wird ein namespaceübergreifendes Kind über das Label core.navique.com/owned-by verfolgt und beim Löschen vom Finalizer des Stack entfernt (Kinder im selben Namespace behalten die Owner-Referenz-Kaskade). Die angelegten Namespaces selbst werden nicht gelöscht.

SSO / OIDC login ​

spec.sso konfiguriert Single Sign-On über den gesamten Stack hinweg von einem Identity Provider aus. Der gemeinsame Issuer/Provider/Scopes gelten für jede aktivierte Komponente, während jede Komponente über einen komponentenspezifischen Client-Ref ihren eigenen OAuth-Client erhält (die Callback-URLs unterscheiden sich). Eine Komponente erhält SSO nur, wenn sie aktiviert ist und ihr Client-Ref gesetzt ist — so können Sie SSO selektiv ausrollen.

FeldBeschreibung
issuerURLGemeinsame OIDC-Issuer-/Discovery-Basis-URL
providerNameGemeinsame Anzeigebezeichnung
scopesGemeinsame angeforderte Scopes
provider / tenantID / authorizationEndpoint / tokenEndpoint / userinfoEndpointProvider-Variante + Endpunkte ausschließlich für das Gateway (siehe die Gateway-SSO-Hinweise)
gateway / chatUI / observabilityOAuth-Client-Refs pro Komponente ({ name, clientIDKey?, clientSecretKey? })
yaml
spec:
  sso:
    issuerURL: https://idp.example.com
    providerName: "Acme SSO"
    provider: generic-oidc
    authorizationEndpoint: https://idp.example.com/authorize
    tokenEndpoint: https://idp.example.com/token
    userinfoEndpoint: https://idp.example.com/userinfo
    gateway:  { name: gateway-oidc }
    chatUI:   { name: chatui-oidc }
    observability: { name: langfuse-oidc }

Dies ist die Grundlage für Self-Service-SSO über den Bereitstellungsassistenten der Management-Konsole. Komponentenspezifische Details (welche Umgebungsvariablen jede App erhält, Lizenzierung) finden Sie auf den Seiten Gateway, ChatUI und Observability.

Mesh ​

spec.mesh.mode nimmt die Komponenten-Namespaces des Stack in das Cluster- ServiceMesh auf (mTLS + Isolation). Standard ist auto (beitreten, wenn ein bereites ServiceMesh existiert, sonst No-op); enabled erfordert eines; disabled tritt nie bei. Der Stack beschriftet nur seine Namespaces — das ServiceMesh-Singleton übernimmt die Aufnahme — sodass ein später aktiviertes Mesh bestehenden Stacks ohne erneutes Deployment beitritt.

Lizenzierung ​

Stack erfordert das Feature managed-deployment und eine stacks-Instanz­obergrenze in der License. Ohne diese setzen Sie die Plattform mit den granularen Ressourcen zusammen, die stets verfügbar sind (vorbehaltlich ihrer eigenen Obergrenzen in der Community Edition).

Siehe Editionen & Lizenzierung.

Open Core unter AGPL-3.0. Enterprise-Komponenten sind proprietär und lizenzgebunden.