Observability
Scope: namespaced · Kind: Observability (short name obs, plural observabilities) · Group: core.navique.com/v1alpha1
LLM observability — traces, evals, prompt management. Observability is the generic observability workload: spec.type selects the backend implementation. Today only langfuse is implemented (Langfuse v3 via the langfuse-operator); other types are scaffolded for the future.
Spec
| Field | Type | Description |
|---|---|---|
type | enum (required) | Backend implementation. Only langfuse is implemented today |
secretsRef | LocalRef (optional) | Same-namespace SecretsManagement to wait for. Leave it out to use plain Kubernetes Secrets you manage yourself — see secretsRef |
mesh | object | Opt the namespace into the cluster ServiceMesh (auto / enabled / disabled) |
image | object | Image override for the selected backend |
ingress | object | Host + TLS (TLS ⇒ ensures cert-manager) |
sso | object | OIDC/SSO login (see SSO) |
langfuse | object | Langfuse-specific configuration — present when type: langfuse (see langfuse block) |
These top-level fields are common to every backend type. Backend-specific configuration lives under a block named after the type (spec.langfuse for the Langfuse backend).
type
type | Status | Emits |
|---|---|---|
langfuse | Implemented | A LangfuseInstance (langfuse.palena.ai/v1alpha1) via the langfuse-operator |
Other backend types are reserved and not yet implemented; setting one yields a clear "not yet implemented" status condition.
langfuse block
When type: langfuse, spec.langfuse holds the Langfuse v3 wiring. Langfuse v3 needs Postgres, ClickHouse, Redis, and object (blob) storage; each datastore can reference a datastore resource or point at an external host.
| Field | Type | Description |
|---|---|---|
postgres | DatastoreRef (required) | Reference a PostgresCluster or external |
clickhouse | DatastoreRef (required) | Reference a ClickHouseCluster or external |
redis | DatastoreRef (required) | Reference a RedisInstance or external |
blob | object (required) | Object storage (required by Langfuse v3): S3 / Azure / GCS |
auth | object | nextAuthUrl; secret/salt auto-generated by the operator |
bootstrap | object | Headless seed of an org / project / API key + admin user (see Bootstrap) |
licenseSecretRef | SecretKeyRef | Langfuse Enterprise licence — per-person access (see Giving people access) |
defaultAccess | object | Automatic access for everyone who signs in (see Giving people access) |
DatastoreRef
| Field | Description |
|---|---|
mode | ref (a datastore resource) or external |
ref | The datastore resource to use (mode ref) |
databaseName | Database inside the shared datastore. Postgres: required for a managed/adopted PostgresCluster. ClickHouse: optional (default default); must be listed in the ClickHouseCluster’s spec.databases (a Stack does this) — Langfuse waits for it. Letters, digits and _ only |
connectionSecretRef | Connection Secret (mode external) |
blob (discriminated union)
provider selects the backend; supply the matching block. Credentials are read from a Secret in the namespace under canonical keys; omitting the credentials secret selects ambient cloud credentials (IRSA / Workload Identity).
| Provider | Block | Credential keys |
|---|---|---|
s3 (default) | s3 (bucket, region, endpoint, forcePathStyle) | access-key-id, secret-access-key |
azure | azure (storageAccountName, containerName, endpoint) | account-key |
gcs | gcs (bucket, projectId) | credentials (SA JSON) |
What it emits
For type: langfuse, the controller resolves the datastores, ensures cert-manager if TLS/ingress is enabled, ensures the langfuse-operator, and emits a LangfuseInstance (langfuse.palena.ai/v1alpha1) referencing the resolved datastores and secrets. The managed/adopt/external provenance lives in the referenced datastore resources — Observability just points at them.
Operator-managed datastores by default
For ClickHouse and Redis, the langfuse-operator can manage them internally. Unless you reference an external datastore, the controller defaults to operator-managed ClickHouse/Redis — so you don't strictly need a dedicated ClickHouseCluster / RedisInstance. Postgres is typically shared with the Gateway.
Example
apiVersion: core.navique.com/v1alpha1
kind: Observability
metadata:
name: observability
namespace: forge-langfuse
spec:
type: langfuse
secretsRef: { name: langfuse-secrets }
ingress: { enabled: true, host: langfuse.forge.example.com, tls: true }
langfuse:
postgres: { mode: ref, ref: { name: forge-pg, namespace: forge-data }, databaseName: langfuse }
clickhouse: { mode: ref, ref: { name: forge-ch, namespace: forge-data } }
redis: { mode: ref, ref: { name: forge-redis, namespace: forge-data } }
blob:
provider: azure
azure:
storageAccountName: forgelangfuse
containerName: langfuse-eventsSSO / OIDC login
spec.sso enables single sign-on. For the Langfuse backend it uses Langfuse v3's generic custom-OIDC provider: the operator sets the LangfuseInstance's native spec.auth.oidc block, which the langfuse-operator translates to Langfuse's AUTH_CUSTOM_* configuration. The OAuth client credentials come from a same-namespace Secret (via SecretsManagement).
| Field | Description |
|---|---|
issuerURL | OIDC issuer / discovery base URL |
clientSecretRef.name | Secret holding the OAuth client (keys default to client-id / client-secret) |
scopes | Requested scopes (default openid, email, profile) |
providerName | Login-button label (AUTH_CUSTOM_NAME) |
The provider, tenantID, and explicit-endpoint fields of the shared SSO type are Gateway-only and ignored here.
The IdP must whitelist the custom-OIDC callback <publicURL>/api/auth/callback/custom, so enable spec.ingress (or set langfuse.auth.nextAuthUrl) to a public URL.
spec:
type: langfuse
sso:
issuerURL: https://idp.example.com
providerName: "Acme SSO"
clientSecretRef: { name: langfuse-oidc }TIP
Static OIDC providers are part of Langfuse's open-source self-hosted build. Only the in-UI SSO setup, per-org SSO enforcement, and RBAC/SCIM are Langfuse Enterprise features.
Bootstrap
spec.langfuse.bootstrap headlessly seeds an organization, a project, an initial API key, and an admin user into a fresh community Langfuse via the LANGFUSE_INIT_* environment. This lets the platform come up fully wired without anyone logging into the Langfuse UI first — for example so a Gateway can export traces against a known project key on a community Langfuse that cannot otherwise mint project keys.
| Field | Description |
|---|---|
organization | Initial organization name |
project | Initial project name |
apiKeySecretRef | Secret to populate with the seeded project's public/secret key |
admin | Admin user (email + password from a Secret) seeded on first start |
The seeded credentials are written through to owned Secrets so consumers (Gateway trace export, the management console) can read them without UI steps.
Giving people access
Langfuse has two ways in, and they need different things:
- One by one — an
Identityoftype: observability(withobservabilityRefand arole:viewer,member,adminorowner) makes that person a member of this Langfuse's organization: the bootstrap organization whenbootstrapis on, which holds the projects and traces. Langfuse's member management is an Enterprise feature, so this needslicenseSecretRef(the Langfuse Enterprise licence key). Without it, such an Identity reports that it needs the licence. - Everyone who signs in —
defaultAccessadds every new Langfuse user (for example on first company sign-in) to the bootstrap organization and project automatically. No licence is needed. Without one,orgRoleis the role that counts on every project: project-level roles (projectRole) are also an Enterprise feature.
status.peopleAccess reports whether one-by-one access is Available, or why not (NeedsLicense, NeedsNewerLangfuseOperator), and status.signInAccessRole the role automatic access actually grants. The management console reads both to offer the right option.
| Field | Description |
|---|---|
licenseSecretRef | Secret holding the Langfuse Enterprise licence (key license by default) |
defaultAccess.orgRole | OWNER / ADMIN / MEMBER / VIEWER (default) / NONE |
defaultAccess.projectRole | OWNER / ADMIN / MEMBER / VIEWER (default) — Enterprise only |
Requires bootstrap for defaultAccess (refused at admission otherwise).
Secrets
nextauth-secret and salt are auto-generated and rotated by the langfuse-operator into a <instance>-generated-secrets Secret — you do not source these from Key Vault unless you want to override them. Datastore credentials come from the referenced datastore resources' materialized Secrets (plus Key Vault for external datastores). See Secrets Management.
Status
Per-datastore readiness, instance readiness, and the ingress host, plus the standard conditions and observedGeneration. An unimplemented type surfaces a clear "not yet implemented" condition.