apiVersion: v2
name: bp-keycloak
version: 1.4.5
description: |
  Catalyst-curated Blueprint umbrella chart for Keycloak. Depends on the
  upstream `keycloak` chart (bitnami) as a Helm subchart so
  `helm dependency build` pulls the upstream payload into this artifact;
  the Catalyst overlay templates in templates/ (HTTPRoute, sovereign realm
  ConfigMap, tenant realm ConfigMap) sit alongside the upstream subchart and
  Helm renders both at install time. Catalyst-curated values flow into the
  upstream subchart under the `keycloak:` key in values.yaml.
  Phase-8b (issue #604): adds catalyst-ui PKCE client, catalyst-api-server
  service-account client with impersonate role, token-exchange enabled on
  sovereign realm, WebAuthn Required Actions, and SMTP configuration.
  1.4.0 (issue #915 / C1): tenant-mode realm import. When
  realmConfig.tenant.enabled=true the chart emits a per-tenant realm with
  wordpress / openclaw / stalwart OIDC clients pre-registered, plus the
  matching `wordpress-oidc-client-secret` / `openclaw-oidc-client-secret`
  / `stalwart-oidc-client-secret` Secrets the downstream
  bp-wordpress-tenant / bp-openclaw / bp-stalwart-tenant charts consume
  via secretKeyRef. Sovereign-mode unchanged. Closes the chart-side gap
  left by orchestrator PR #911 (which already emits realmConfig.tenant.*).
  1.4.1 (qa-loop iter-4 Fix #23): grant catalyst-api-server service-account
  manage-realm + view-realm + view-clients on realm-management so the
  EPIC-3 T2 tier-role bootstrap goroutine (KEYCLOAK_BOOTSTRAP_TIER_ROLES=
  true) can materialize the catalog-tier realm-roles + composite chain
  at catalyst-api startup. Without these, GET /admin/realms/<r>/roles/<n>
  returned 403 and EnsureRealmRole gave up after 5 retries → TC-248
  failure on the access-matrix UI.
  1.5.0 (qa-loop iter-12 Fix #53, target-state realm-per-Sovereign):
  parameterize the realm name via .Values.sovereignRealm.name (default
  "sovereign" for backward compat). Per-Sovereign overlays now set this
  to the tenant short-name (e.g. omantel.omani.works → "omantel") so
  matrix tests asserting `/admin/realms/<tenant>/...` paths PASS. Both
  the realm import JSON (`"realm": "<name>"`) and the catalyst-kc-sa-
  credentials Secret `realm` key flow from the same value, keeping
  Keycloak realm and catalyst-api CATALYST_KC_REALM env in sync. Closes
  TC-124, TC-125, TC-159, TC-160, TC-161, TC-176, TC-190, TC-285.
  1.4.5 (issue #146, post-#140 retune — prov #21 + #22 hung 30+ min on
  bp-keycloak install): Fix #140 set availabilityCheck.timeout=900s +
  HR install/upgrade.timeout=30m + remediation.retries=3. In practice
  the 900s per-attempt window meant a single failed availability check
  busted the 30m HR window before Job-level backoff could retry —
  AND each HR remediation triggered a full Helm uninstall+reinstall,
  losing all hook-Pod state and restarting Liquibase from zero
  (90m+ wasted wall-clock). On top, Liquibase + JVM cold-start
  legitimately took 5-10min before the StatefulSet's Service had
  Endpoints, but the Bitnami Pod's livenessProbe (initialDelaySeconds
  300) was killing it mid-migration on slower-disk Sovereigns.
  Three coupled changes for target-state fix:
    - keycloakConfigCli.availabilityCheck.timeout 900s → 300s. Faster
      failure means more attempts fit inside 30m HR envelope (~5 vs.
      ~1.5 with 900s). Each 300s window still covers one full
      Liquibase pass + JVM warm-up (typical 90-180s).
    - HR install.remediation.retries + upgrade.remediation.retries
      3 → 1 (in clusters/_template/bootstrap-kit/09-keycloak.yaml).
      The Job's own backoffLimit=5 handles transient races without
      losing state; HR-level remediation is reserved for genuine
      release-failure.
    - keycloak.startupProbe.enabled=true (failureThreshold 360 ×
      periodSeconds 5 = 30m budget). startupProbe is an exclusive
      kubelet gate — livenessProbe + readinessProbe are SUSPENDED
      until startupProbe passes, so slow Liquibase isn't killed
      mid-migration. keycloak.livenessProbe.initialDelaySeconds
      300 → 60 (cold-start protection now lives in startupProbe).
  All knobs operator-overridable via valuesFrom (Principle #4). TODO
  follow-up: move realm-import OUT of the bitnami post-install Helm
  hook into a Catalyst-owned Job that runs after Keycloak Service
  has Endpoints — decouples HR-Ready from realm-imported.
  1.4.4 (issue #140, post-upgrade hook regression on prov #21): Fix #129
  set 600s + backoffLimit=5, which works for fresh-install but races on
  upgrade. On prov #21 (f84f6c3ff2b60296, 2026-05-11) the chart-roll on
  contabo (PR #1346/#1347 stack) triggered an HR UPGRADE; the keycloak
  StatefulSet rolled, Helm fired the post-upgrade hook before the new
  Pod's admin endpoint recovered (Liquibase re-validation + JVM cold
  start on freshly-provisioned node), and the inner 600s availability
  window expired before the first attempt found Keycloak Ready. With
  backoffLimit=5 + 10s..6m exponential backoff the worst-case wall clock
  exceeds the parent HR's 15m upgrade.timeout → Helm aborts the hook →
  "post-upgrade hooks failed". Target-state fix:
    - keycloakConfigCli.availabilityCheck.timeout 600s → 900s (15m inner
      wait covers a single rolling-restart + Liquibase cycle without
      retry, eliminating most backoff time)
    - keycloakConfigCli.cleanupAfterFinished.enabled true (1h TTL) so
      stale hook Pods don't race the before-hook-creation delete on
      subsequent upgrades
    - HR install.timeout + upgrade.timeout 15m → 30m
      (clusters/_template/bootstrap-kit/09-keycloak.yaml) so Helm's
      outer hook-wait accommodates the inner 15m availability window
      plus normal backoff.
  Coupled chart + HR move per Principle #3 (no half-fix). All knobs stay
  operator-overridable via valuesFrom (Principle #4).
  1.4.3 (issue #129, post-upgrade hook timeout wedge): bump bitnami
  keycloakConfigCli.availabilityCheck.timeout 120s → 600s and
  backoffLimit 1 → 5. Fresh-Sovereign provision #15 (otech
  0ad3687ddd72deb7) wedged at phase1-watching because the
  keycloak-config-cli Job (rendered as a Helm post-install/post-upgrade
  hook by the bitnami subchart's default annotations) timed out twice
  inside its 120s availability window before Postgres+Liquibase finished
  bootstrapping Keycloak. Helm reported "post-upgrade hooks failed:
  timed out waiting for the condition" → bp-keycloak HR never reached
  Ready → bp-gitea (dependsOn keycloak OIDC) blocked → bp-self-sovereign-
  cutover never converged. New defaults sit comfortably inside the parent
  HR's 15m install/upgrade timeout. Both knobs remain per-Sovereign
  overridable via valuesFrom (Inviolable Principle #4: no hardcoding).
type: application
keywords: [catalyst, blueprint, keycloak]
maintainers:
  - name: OpenOva Catalyst
    email: catalyst@openova.io

# Upstream chart pulled in as a Helm subchart so `helm dependency build`
# bundles it into the OCI artifact. Pinned to bitnami/keycloak 25.2.0
# (matches platform/keycloak/blueprint.yaml + values.yaml
# `catalystBlueprint.upstream.version`). Per
# docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) the version is
# operator-bumpable via PR + Blueprint release.
#
# Bitnami consolidated their tag scheme around 2025-09 (see
# https://github.com/bitnami/charts/issues/30852): the original
# `bitnami/keycloak:<x>-debian-12-rN` tags now 404 in the registry. The
# preserved historic tags live under `bitnamilegacy/keycloak` (read-only
# archive). values.yaml overrides image.registry/image.repository to point
# every Bitnami image used by this chart at `bitnamilegacy/*` so installs
# succeed. Long-term we will migrate to a Catalyst-built or upstream
# replacement Keycloak image (issue #191 follow-up).
dependencies:
  - name: keycloak
    version: "25.2.0"
    repository: "https://charts.bitnami.com/bitnami"
