# bp-openclaw values.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every
# operationally-meaningful value is configurable; cluster overlays in
# clusters/<sovereign>/bootstrap-kit/ may override any of these without
# rebuilding the Blueprint OCI artifact.
#
# Required at install time (overlay MUST set):
#   - oidc.issuerURL             (per-tenant Keycloak realm OIDC issuer)
#   - oidc.clientSecret.name     (ExternalSecret with OIDC client_secret)
#   - llm.baseURL                (per-tenant NewAPI OpenAI-compatible endpoint)
#   - llm.apiKey.name            (Secret carrying NewAPI bearer token)
#   - tenant.namespace           (SME tenant namespace where per-user pods run)
#   - ingress.host               (controller public hostname)
#   - controller.image.tag       (SHA-pinned tag)
#   - perUserPod.image.tag       (SHA-pinned tag)
#
# Legacy keys `keycloak.*` and `newapi.*` remain accepted for back-compat
# (they fall back when the canonical `oidc.*` / `llm.*` block omits a
# field); new overlays SHOULD use the canonical blocks. See umbrella
# epic #915 (per-tenant SSO + per-tenant NewAPI gateway DoD).
#
# Placeholder defaults below let `helm template` smoke-render cleanly in
# CI without --set (mirroring the bp-self-sovereign-cutover pattern).
# Runtime use with the placeholder values is non-functional by design;
# the chart's integration tests + deploy-time validation enforce real
# values on actual installs.

catalystBlueprint:
  upstream:
    chart: ""              # scratch chart — no upstream Helm chart
    version: ""
    repo: ""
    images:
      controller: "ghcr.io/openova-io/openova/openclaw-controller"
      runtime: "ghcr.io/openova-io/openova/openclaw-runtime"

# When `true`, _helpers.tpl :: assertNoPlaceholders fails render with a
# helpful error if any placeholder default is still in place. Per-cluster
# Flux overlays MUST set this to `true` so a bad overlay (forgot to set
# tenant.namespace, etc.) fails install fast instead of installing a
# non-functional release. Default `false` so CI's helm-template smoke
# render passes (the smoke run uses defaults — placeholders are valid
# bytes for render-time syntax checks but non-functional at runtime).
assertNoPlaceholders: false

# ─── Controller (multi-tenant) ───────────────────────────────────────────
controller:
  enabled: true
  replicas: 1
  image:
    # Operator-supplied SHA tag at install time. Placeholder default lets
    # smoke render pass; real installs MUST override (per Inviolable
    # Principle 4 — no floating tags in production).
    repository: ghcr.io/openova-io/openova/openclaw-controller
    tag: "0.1.0-placeholder"      # OVERRIDE on install with SHA-pinned tag
    pullPolicy: IfNotPresent
  port: 8080
  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 512Mi
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    readOnlyRootFilesystem: true
    allowPrivilegeEscalation: false
    capabilities:
      drop: ["ALL"]
  probes:
    liveness:
      path: /healthz
      port: 8080
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
    readiness:
      path: /readyz
      port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3

# ─── Auth: per-tenant Keycloak (OIDC) — canonical block ─────────────────
# The controller validates inbound end-user JWTs against the SME's own
# Keycloak realm. It NEVER uses the OTECH-ops realm, NEVER trusts a JWT
# from outside the SME vcluster.
#
# Per umbrella epic #915 the orchestrator (catalyst-api smeTenantBPOpenClaw
# template) emits this block per-tenant: `oidc.issuerURL` resolves to
# https://keycloak.<tenant-domain>/realms/<tenant>, `oidc.clientId` to
# `openclaw`, and `oidc.clientSecret.name` to the per-tenant Keycloak
# client-secret (Secret rendered by bp-keycloak post-install).
oidc:
  # Required at install time: full realm issuer URL (the OIDC `iss` claim
  # the controller will validate JWTs against). Placeholder default lets
  # `helm template` smoke-render pass.
  issuerURL: "https://keycloak.example.local/realms/example"
  # OIDC client registered in the per-tenant Keycloak realm.
  clientId: "openclaw"
  # OIDC client_secret. Loaded from a K8s Secret via secretKeyRef so the
  # plaintext NEVER lands in the HelmRelease values block (Inviolable
  # Principle 10 — credential hygiene). Required Secret key:
  # OIDC_CLIENT_SECRET. Provisioned by the SME-tenant onboarding pipeline
  # (epic #795 ticket #804).
  clientSecret:
    # Secret name (defaults to legacy `keycloak.clientSecretName` if
    # unset — see _helpers.tpl).
    name: "openclaw-oidc"
    key: "OIDC_CLIENT_SECRET"

# ─── LLM gateway: per-tenant NewAPI ─────────────────────────────────────
# OpenClaw uses the per-tenant NewAPI as its OpenAI-compatible LLM
# gateway (NOT direct OpenAI). The orchestrator points this at
# https://api.<tenant-domain>/v1; NewAPI then routes to the configured
# channel (e.g. Qwen3.6@BankDhofar — wired by C4 of #915).
#
# Per ADR-0003 §3.3 each end-user has a per-user Secret rendered by
# the unified-rbac hook carrying `base-url` + `api-key`; per-user pods
# read those at session-start. The chart-level `llm.*` block carries the
# CONTROLLER-side defaults (used for /readyz probes, fallback when the
# per-user Secret is missing the field, and for the OpenAI-compatible
# `LLM_DEFAULT_MODEL` env that NewAPI maps to a channel).
llm:
  # Required at install time. The customer-facing OpenAI-compatible
  # endpoint (i.e. NewAPI's /v1 surface). Placeholder default lets
  # smoke render pass.
  baseURL: "https://newapi.example.local/v1"
  # Bearer token sourced from a K8s Secret (Inviolable Principle 10).
  # Required Secret key: NEWAPI_KEY (or `api-key` for ADR-0003-aligned
  # per-user secrets). Per-user pods continue to read the per-user
  # `newapi-key-{uuid}` Secret per ADR-0003 §3.3; this controller-side
  # secret is the controller's own service-token (used for /readyz
  # probes and for any controller-side LLM call that pre-dates a user
  # session).
  apiKey:
    name: "openclaw-llm-apikey"
    key: "NEWAPI_KEY"
  # The default OpenAI-compatible model identifier sent on /v1/chat/
  # completions when the client doesn't override. NewAPI uses the
  # `model` field to select a channel; for the umbrella #915 DoD this
  # is "qwen3.6" — C4 wires the BankDhofar channel routing at
  # tenant-create time.
  defaultModel: "qwen3.6"

# ─── Legacy aliases (back-compat, deprecated) ───────────────────────────
# Old overlays may still set `keycloak.*` / `newapi.*`. The chart's
# helpers prefer `oidc.*` / `llm.*` and fall back to these when the
# canonical block is unset (or still at placeholder). Removed once every
# overlay has migrated.
keycloak:
  realmURL: ""
  clientID: ""
  clientSecretName: ""
newapi:
  baseURL: ""

# ─── Tenant namespace ────────────────────────────────────────────────────
# SME tenant namespace where:
#   - per-user pods are created and deleted
#   - newapi-key-{uuid} Secrets are read by the controller's ServiceAccount
tenant:
  # Required at install time. Placeholder default lets smoke render pass.
  namespace: "sme-example"

# ─── Per-user pod template ───────────────────────────────────────────────
# These values are baked into a ConfigMap; the controller renders the
# pod-spec at session-start time, substituting NEWAPI_KEY (Secret ref)
# and NEWAPI_BASE_URL (env value).
perUserPod:
  image:
    repository: ghcr.io/openova-io/openova/openclaw-runtime
    tag: "0.1.0-placeholder"      # OVERRIDE on install with SHA-pinned tag
    pullPolicy: IfNotPresent
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: 1
      memory: 1Gi
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    readOnlyRootFilesystem: true
    allowPrivilegeEscalation: false
    capabilities:
      drop: ["ALL"]
  # Idle-session reaper — controller deletes the per-user pod after
  # this many minutes without inbound traffic. Default 30 minutes.
  idleTimeoutMinutes: 30
  # Optional extra env vars baked into every per-user pod (operator
  # overlay may extend, e.g. for HTTP proxy, telemetry endpoint, etc.).
  extraEnv: []

# ─── Service ─────────────────────────────────────────────────────────────
service:
  type: ClusterIP
  port: 8080
  targetPort: 8080

# ─── ServiceAccount ──────────────────────────────────────────────────────
serviceAccount:
  create: true
  name: ""

# ─── Ingress + cert-manager TLS ──────────────────────────────────────────
# Public hostname for the controller. Per Q-mine-1 of #795:
# `openclaw.<sme-domain-or-subdomain>`. cert-manager auto-issues a
# per-host certificate via the operator's ACME issuer.
ingress:
  enabled: true
  className: "traefik"
  # Required at install time. Placeholder default lets smoke render pass.
  host: "openclaw.example.local"
  tls:
    enabled: true
    issuer: "letsencrypt-prod"    # cert-manager ClusterIssuer
    secretName: "openclaw-tls"
  annotations: {}

# ─── NetworkPolicy ───────────────────────────────────────────────────────
# Default-allow ingress from the platform's gateway namespace (traefik).
# Egress from CONTROLLER:
#   - SME-vcluster Keycloak (OIDC discovery + JWKS)
#   - K8s api-server (pod create/delete; goes to kube-system endpoint)
#   - per-user pod range in tenant namespace (proxy WebSocket/SSE)
#   - DNS
# Egress from PER-USER pod (separate NetworkPolicy applied at pod-spawn
# time by the controller, NOT by this chart's render — see
# README.md §"per-user-pod NetworkPolicy"):
#   - NewAPI Service (cluster-internal hostname or external ingress)
#   - DNS
# This chart's NetworkPolicy template covers the controller pod only;
# per-user pods receive their NetworkPolicy from a controller-rendered
# template at session-start.
networkPolicy:
  enabled: true
  controller:
    ingress:
      fromNamespaceLabels:
        kubernetes.io/metadata.name: traefik
    # Egress targets — operator may extend if SME-vcluster Keycloak
    # reaches the controller via a different namespace label.
    egress:
      keycloakNamespaceLabel: "keycloak"
      keycloakPort: 8443

# ─── ServiceMonitor ──────────────────────────────────────────────────────
# Default false per docs/BLUEPRINT-AUTHORING.md §11.2 (Observability
# toggles must default false). Operator opts in via per-cluster overlay
# once kube-prometheus-stack reconciles.
serviceMonitor:
  enabled: false
  interval: "30s"
  scrapeTimeout: "10s"
  path: "/metrics"
  labels: {}
