Skip to content

Identity, Organization & Team ​

Scope: namespaced · Licensed (identity-management feature + organizations / teams caps)

These resources manage identities across the platform's components — the gateway (LiteLLM or Wäg), Langfuse, and LibreChat — as Kubernetes objects, so access and tenancy are declarative and auditable rather than configured by hand in each tool.

ResourceManages
UserA person / seat — the natural key (email) tying that person's accounts together
IdentityOne per-backend account for a person, on the gateway, Observability (Langfuse), or LibreChat
OrganizationA top-level tenant / billing boundary
TeamA group within an organization, with budgets and model access

People vs. accounts

A User is a person (a licensed seat). An Identity is one of that person's backend accounts. The license users cap counts User seats; Identity accounts are uncapped, and each must point at a User via userRef. One person with a gateway key, a Langfuse membership, and a LibreChat login is one seat, three Identity objects.

Identity ​

An Identity provisions a single account for a person on one backend. Its spec.type selects which component the account lives in:

typeEmits
gatewayRecommended. Follows the referenced Gateway's own spec.type — see Choosing the backend
litellmLiteLLM user resources (LiteLLMUser), checked against the referenced Gateway
waegWäg console user (WaegUser), checked against the referenced Gateway — see Wäg accounts
observabilityMembership of the Observability's Langfuse organization (needs a Langfuse Enterprise licence)
librechatLibreChat user / role configuration

Spec ​

FieldTypeDescription
typeenum (required)gateway / litellm / waeg / observability / librechat
userRef{ name } (required)The owning User in the same namespace
emailstring (optional)Account email; defaults to the owning User's spec.email
displayNamestringHuman-readable name
userIdstringStable external user id for the backend
rolestringBackend role. On Wäg, exactly admin or platform_admin grants the cluster-wide platform-admin privilege — nothing else is inferred
budgetobjectPer-account budget (where the backend supports it; not applied on Wäg)
teamRefslistTeams this account belongs to (LiteLLM; not applied on Wäg)
gatewayRefObjectRefThe Gateway this account lives on. Required for type: gateway / litellm / waeg, and it must be in the same namespace — see Admission rules
observabilityRefObjectRefAn Observability (for type: observability)
chatUIRefObjectRefA ChatUI (for type: librechat)
passwordSecretRefSecretKeyRefPassword Secret (for type: librechat)

Choosing the backend: type: gateway ​

Identity, Organization and Team all accept type: gateway, and it is the value to use for new resources. A gateway-backed identity resource targets a Gateway — and that Gateway already knows which implementation it runs. With type: gateway the operator reads the backend from the referenced Gateway's own spec.type, which is the one source that cannot drift.

yaml
spec:
  type: gateway               # resolved from the Gateway below
  gatewayRef: { name: forge-gateway }

The product-named values litellm and waeg still work and are not scheduled for removal. What changed is that they are now checked against the Gateway instead of trusted. Declaring a type that contradicts the Gateway is refused with 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 gateway

The message names the declared type, the Gateway, the Gateway's actual type, and both ways out: follow the Gateway with type: gateway, or point gatewayRef at a gateway of the type you declared.

Why this check exists

Nothing used to compare the two. An Organization / Team / Identity declaring type: litellm against a Wäg gateway was accepted, and the operator emitted a LiteLLMOrganization / LiteLLMTeam / LiteLLMUser whose instanceRef named a Wäg instance — an object that binds to nothing and materializes in no gateway.

On a cluster where both operators are installed that apply succeeds: the CRD exists, the object is stored, and nothing ever surfaces the mistake. The account simply never appears in the gateway. The mismatch is now a refusal instead of a silently orphaned object.

Admission rules ​

Two rules are enforced by the API server on Identity, Team andOrganization, so a bad reference is refused at kubectl apply rather than hours later in a status condition.

1. A gateway-backed type requires gatewayRef.

type gateway/litellm/waeg requires gatewayRef

gatewayRef used to be optional on every one of these resources. A Team with type: waeg and no gatewayRef was admitted and only failed at reconcile with "a gateway-backed identity resource requires gatewayRef" — the same information, hours later.

2. gatewayRef.namespace must be empty.

gatewayRef.namespace must be empty: the Gateway must live in the same namespace as this resource

Both the litellm-operator and the waeg-operator resolve the upstream instanceRefnamespace-locally, so a cross-namespace gatewayRef could never bind to anything. Put the Gateway in the same namespace as the identity resources that target it, and set only { name: … }.

LibreChat accounts ​

LibreChat has no API to create accounts, so an Identity with type: librechat is provisioned by running LibreChat's bundled create-user script as a short-lived Kubernetes Job. The Job is cloned from the running LibreChat container (same image and environment), so it reaches the same MongoDB automatically.

  • The referenced ChatUI (spec.chatUIRef) must be Ready and in the same namespace as the Identity.
  • Provisioning is idempotent and create-only: an existing account is treated as success, and later edits to the Identity do not change an account that already exists.
  • Password: set spec.passwordSecretRef (key password by default) to use a known password. If you omit it, the operator generates one and stores it in an owned Secret named <identity>-librechat-password (key password). Retrieve it with kubectl get secret <identity>-librechat-password -o jsonpath='{.data.password}' | base64 -d and rotate it after first login. The password is never passed on the command line.

Wäg accounts ​

Identity, Team and Organization are fully supported on a Wäg gateway. They emit WaegUser, WaegTeam, WaegOrganization and the org- and team-scoped WaegBudget policies (all in gateway.waeg.ai/v1alpha1).

Console password. Wäg requires a password to create a console user. For a Wäg-backed Identity the operator generates one into an owned Secret named <identity-name>-waeg-password, key password, and points the WaegUser at it via spec.passwordSecretRef. Read it back for the person's first login with:

bash
kubectl get secret <identity>-waeg-password \
  -o jsonpath='{.data.password}' | base64 -d

The Secret is generated once and never rotated automatically — rotating the Secret rotates the console password. Nothing else needs to be set; there is no field to supply your own password for a Wäg account.

Ready means the account exists. A Wäg identity resource reports Ready only once the emitted WaegUser itself reports Synced=True. Applying the upstream CR used to be treated as success, which hid exactly the failure above: with no password Secret the WaegUser sat permanently at Synced=False — "passwordSecretRef is required to create a new console user", while the Identity showed green and the console account did not exist. A green Identity now means the gateway accepted the account.

Team members. Wäg addresses a team member by WaegUser, never by email. For a member who also has an Identity on the same gateway, the team points at that Identity's WaegUser — one console user per person. Only an address with no Identity gets a separate membership WaegUser (named <team>-<email-slug>), and it is deleted again when the address leaves the team. Removing it never deletes the person's Wäg account: Wäg deletes a directory user only when the CR being deleted created it, which a membership user (which carries no password) never does.

Not applied on Wäg — each one is reported as an Event (UnsupportedByBackend), never silently dropped:

FieldWhy
Identity.spec.teamRefsWäg keeps membership on the team, not the user. Add the person's address to the Team's spec.members instead
Identity.spec.budgetA Wäg per-user cap is a WaegBudget with scope: user, which this operator does not emit
Organization.spec.membersWaegOrganization models no members — org membership is an Admin API endpoint with no CR. A Team's members are applied
spec.budget.budgetDuration (Organization / Team)Wäg budget policies have no period field
spec.budget.models (Organization / Team)Model allow-lists are the separate WaegModelAccess ACL, not a budget field

Organization & Team ​

ResourceManages
OrganizationA top-level tenant / billing boundary
TeamA group within an organization, with budgets and model access

Each carries a type that selects which component's identity it drives:

typeEmits
gatewayRecommended. Follows the referenced Gateway's own spec.type
litellmLiteLLMOrganization / LiteLLMTeam
waegWaegOrganization / WaegTeam, plus the matching WaegBudget policy
observabilityLangfuse org / project / membership resources (teams are scaffolded)

Both accept a gatewayRef — required for gateway / litellm / waeg, and same-namespace only (Admission rules). Organization also exposes an observabilityRef to point at the Observability workload when type: observability.

A Team references its parent through spec.organizationRef ({ name }, same namespace). On LiteLLM the parent is optional; on Wäg it is required — Wäg teams are always rooted at an organization. A type: waeg Team without it is rejected at admission (spec.organizationRef: Required value); a type: gateway Team on a Wäg gateway is refused at reconcile instead, since admission cannot see the Gateway's type. The parent's own type is resolved the same way, so an Organization of type: gateway on a Wäg gateway is a valid parent for a Wäg team; an Organization on the other product is reported as a mismatch.

Example ​

One Gateway, one organization, one team, one person — and none of the identity resources name the gateway product. Switching the Gateway between litellm and waeg leaves all four unchanged.

yaml
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 User

On a type: waeg gateway this also produces the generated console password in alice-gateway-waeg-password — see Wäg accounts.

How it relates to the Gateway ​

A Gateway can declare an inline organization and teams[]. The identity resources are the broader, cross-component mechanism: they let you manage organizations, teams, people (User), and their backend accounts (Identity) once and have them reflected consistently across the gateway, Langfuse, and LibreChat, including across multiple gateways.

Licensing ​

Identity management is part of the multi-tenancy value and requires the identity-management feature plus the relevant organizations / teams instance caps in the License. The users cap counts User seats — not Identity accounts, which are uncapped. In the Community edition, use the inline organization / teams[] on a single Gateway.

See Editions & Licensing.

Open core under AGPL-3.0. Enterprise components are proprietary and license-gated.