# Catalyst Blueprint umbrella metadata — the upstream chart is resolved as
# a Helm subchart via Chart.yaml `dependencies:`. Catalyst-curated values
# under the `harbor:` key flow into the upstream subchart unchanged.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every operationally-
# meaningful value is configurable; cluster overlays in clusters/<sovereign>/
# may override any of these without rebuilding the Blueprint OCI artifact.

global:
  # When set, ALL image pulls in this chart route through this registry.
  # Used post-handover when the Sovereign's own Harbor takes over the
  # proxy_cache role from contabo's central Harbor. Empty = no rewrite
  # (image references use upstream defaults). The upstream harbor subchart
  # exposes per-component `harbor.<component>.image.repository` for override.
  # Per-Sovereign overlays should wire those alongside this value. Tracked under #560.
  imageRegistry: ""

catalystBlueprint:
  upstream:
    chart: harbor
    version: "1.18.3"
    repo: "https://helm.goharbor.io"

# ─── Upstream chart values (subchart key: harbor) ────────────────────────
harbor:

  # ─── External hostname / endpoint ──────────────────────────────────────
  # Per-Sovereign overlay MUST set externalURL (e.g.
  # "https://harbor.<location-code>.<sovereign-domain>"). Default is a
  # placeholder so `helm template` smoke renders without `--set`.
  externalURL: "https://harbor.example.local"

  # ─── Admin password (issue #935) ───────────────────────────────────────
  # The upstream goharbor/harbor chart writes its `harbor-core` Secret
  # with key HARBOR_ADMIN_PASSWORD. Either it generates a fresh password
  # from the chart values (`harborAdminPassword`) every reconcile — which
  # rotates every helm upgrade and breaks every running session — OR it
  # consumes a pre-existing Secret via `existingSecretAdminPassword`.
  #
  # The umbrella chart owns the Secret (templates/admin-secret.yaml)
  # because:
  #   1. Per docs/INVIOLABLE-PRINCIPLES.md #10, the password MUST be
  #      fully-random + per-install (no shared default across Sovereigns).
  #   2. The bp-self-sovereign-cutover Step 02 (harbor-projects) Job
  #      runs in the `catalyst` namespace and must read the admin
  #      password via secretKeyRef — K8s forbids cross-namespace
  #      secretKeyRef. The umbrella's admin-secret carries Reflector
  #      annotations that mirror the Secret into `catalyst` so the
  #      cutover Job's references resolve. (Caught live on otech113
  #      2026-05-05 — Job CreateContainerConfigError "secret
  #      harbor-core not found".)
  #   3. lookup-on-reconcile keeps the bytes stable across helm upgrade
  #      so the rotation pathology above never fires.
  #
  # Operator override: set .Values.harborAdmin.password at the umbrella
  # values layer to inject a specific password (e.g. when sealed-secrets
  # already pin it). Empty = generate at first install + lookup on
  # subsequent reconciles.
  existingSecretAdminPassword: "harbor-admin"
  existingSecretAdminPasswordKey: "HARBOR_ADMIN_PASSWORD"

  # ─── Expose (clusterIP + Cilium Gateway HTTPRoute) ─────────────────────
  # Catalyst standard (issue #387): expose harbor-core as a ClusterIP
  # Service and route external traffic through the per-Sovereign Cilium
  # Gateway via the HTTPRoute in templates/httproute.yaml. TLS terminates
  # at the Gateway (wildcard cert from cert-manager — see
  # clusters/_template/bootstrap-kit/01-cilium.yaml), so the upstream
  # chart's expose.tls block is disabled here.
  expose:
    type: clusterIP
    tls:
      enabled: false
    # ingress block retained for documentation / fallback only — when
    # expose.type=clusterIP, the upstream chart does not render an Ingress.
    ingress:
      hosts:
        core: "harbor.example.local"

  # ─── Image-chart storage ───────────────────────────────────────────────
  # Per ADR-0001 §13 + docs/omantel-handover-wbs.md §3 + §3a, S3-aware
  # apps (Harbor is one) write DIRECTLY to the cloud-provider's native S3
  # endpoint. On Hetzner Sovereigns that is Hetzner Object Storage
  # (https://<region>.your-objectstorage.com), NOT SeaweedFS — SeaweedFS
  # is reserved as a POSIX→S3 buffer for legacy POSIX-only writers and
  # is not in the minimal Sovereign set.
  #
  # Defaults below leave bucket/region/regionendpoint EMPTY because the
  # umbrella chart MUST render cleanly on contabo (which has no Hetzner
  # credentials). Per-Sovereign HelmRelease in
  # clusters/<sovereign>/bootstrap-kit/19-harbor.yaml supplies real
  # values via Flux `valuesFrom` against the flux-system/object-storage
  # Secret (issue #371, vendor-agnostic since #425).
  #
  # `type: filesystem` keeps the upstream chart on the local PVC path
  # at default-render time (so an empty bucket/regionendpoint doesn't
  # produce a half-broken S3 driver); the per-Sovereign overlay MUST
  # flip type back to `s3` and set `existingSecret: harbor-objectstorage-credentials`
  # once it supplies real config — see the bootstrap-kit slot for the
  # wiring.
  persistence:
    enabled: true
    resourcePolicy: "keep"
    persistentVolumeClaim:
      registry:
        # Used in `type: filesystem` mode (contabo dev/test) AND as a
        # tiny cache when `type: s3` is engaged. Per-Sovereign overlay
        # MAY override storageClass.
        existingClaim: ""
        storageClass: ""
        subPath: ""
        accessMode: ReadWriteOnce
        size: 5Gi
      jobservice:
        jobLog:
          existingClaim: ""
          storageClass: ""
          subPath: ""
          accessMode: ReadWriteOnce
          size: 1Gi
      database:
        # Catalyst uses bp-cnpg for DB — no embedded postgres PVC. The
        # upstream chart still requires this block to exist when
        # `database.type: external`; size kept at upstream default.
        existingClaim: ""
        storageClass: ""
        subPath: ""
        accessMode: ReadWriteOnce
        size: 1Gi
      redis:
        existingClaim: ""
        storageClass: ""
        subPath: ""
        accessMode: ReadWriteOnce
        size: 1Gi
      trivy:
        existingClaim: ""
        storageClass: ""
        subPath: ""
        accessMode: ReadWriteOnce
        size: 5Gi
    imageChartStorage:
      disableRedirect: false
      # Default `filesystem` so contabo dev/test renders cleanly with
      # no S3 endpoint configured. Per-Sovereign overlay flips this to
      # `s3` and supplies bucket/region/regionendpoint/existingSecret
      # via Flux valuesFrom.
      type: filesystem
      filesystem:
        rootdirectory: /storage
      s3:
        # Per-Sovereign overlay populates these via Flux `valuesFrom`
        # against flux-system/object-storage Secret (#371, vendor-
        # agnostic since #425). The corresponding `targetPath` entries
        # are documented in clusters/_template/bootstrap-kit/19-harbor.yaml.
        region: ""
        bucket: ""
        # Credentials flow through `existingSecret` (NOT inline
        # accesskey/secretkey) so the umbrella's templated Secret
        # carries them — see templates/objectstorage-credentials.yaml.
        # Per-Sovereign overlay MUST set existingSecret to the value of
        # `objectStorage.credentialsSecretName` (default
        # `harbor-objectstorage-credentials`).
        existingSecret: ""
        accesskey: ""
        secretkey: ""
        regionendpoint: ""
        encrypt: false
        keyid: ""
        secure: true
        v4auth: true
        chunksize: ""
        rootdirectory: ""
        storageclass: "STANDARD"
        multipartcopychunksize: ""
        multipartcopymaxconcurrency: ""
        multipartcopythresholdsize: ""

  # ─── Database (external CNPG) ──────────────────────────────────────────
  # Per-Sovereign overlay supplies CNPG Cluster ref via ExternalSecret +
  # populates `host`, `password` from the secret. Default is placeholder
  # so `helm template` smoke renders without `--set`.
  database:
    type: external
    external:
      host: "harbor-pg-rw.harbor.svc.cluster.local"
      port: "5432"
      username: "harbor"
      password: ""
      coreDatabase: "registry"
      existingSecret: "harbor-database-secret"   # ESO-projected from CNPG
      sslmode: "disable"
    maxIdleConns: 100
    maxOpenConns: 900
    podAnnotations: {}

  # ─── Redis (internal — for solo Sovereign; HA overlay swaps to Valkey) ─
  redis:
    type: internal
    internal:
      image:
        repository: goharbor/redis-photon
        tag: "v2.14.3"
      resources:
        requests:
          memory: 64Mi
          cpu: 50m
    external:
      addr: ""
      sentinelMasterSet: ""
      coreDatabaseIndex: "0"
      jobserviceDatabaseIndex: "1"
      registryDatabaseIndex: "2"
      trivyAdapterIndex: "5"
      password: ""
      existingSecret: ""

  # ─── Image pin (per docs/INVIOLABLE-PRINCIPLES.md #4) ─────────────────
  # The upstream chart sub-images come pre-pinned to appVersion 2.14.3 by
  # the bundled defaults; we leave them at upstream defaults rather than
  # diverging.

  # ─── Component replica counts ──────────────────────────────────────────
  portal:
    image:
      repository: goharbor/harbor-portal
      tag: "v2.14.3"
    replicas: 1
    resources:
      requests:
        memory: 64Mi
        cpu: 50m
      limits:
        memory: 256Mi
        cpu: 500m

  core:
    image:
      repository: goharbor/harbor-core
      tag: "v2.14.3"
    replicas: 1
    resources:
      requests:
        memory: 256Mi
        cpu: 100m
      limits:
        memory: 512Mi
        cpu: 1
    # `secretName` lets per-Sovereign overlay supply the Harbor admin
    # secret (CSRF key, JWT signing key, admin password). Default empty
    # — chart auto-generates on first install (rotated by overlay later).
    secretName: ""
    # Internal TLS — Catalyst's Cilium WireGuard handles cluster-internal
    # traffic encryption; Harbor's internal TLS off by default.
    tokenCert: ""
    tokenKey: ""

  jobservice:
    image:
      repository: goharbor/harbor-jobservice
      tag: "v2.14.3"
    replicas: 1
    resources:
      requests:
        memory: 256Mi
        cpu: 100m
      limits:
        memory: 1Gi
        cpu: 1
    maxJobWorkers: 10
    jobLoggers:
      - file
    loggerSweeperDuration: 14

  registry:
    registry:
      image:
        repository: goharbor/registry-photon
        tag: "v2.14.3"
      resources:
        requests:
          memory: 256Mi
          cpu: 100m
        limits:
          memory: 1Gi
          cpu: 1
    controller:
      image:
        repository: goharbor/harbor-registryctl
        tag: "v2.14.3"
      resources:
        requests:
          memory: 64Mi
          cpu: 50m
        limits:
          memory: 256Mi
          cpu: 500m
    replicas: 1
    middleware:
      enabled: false

  # ─── Trivy vulnerability scanner ───────────────────────────────────────
  # DEFAULT FALSE per docs/BLUEPRINT-AUTHORING.md §11.2 logic — bp-trivy
  # (slot 30) reconciles AFTER bp-harbor (slot 19) in the bootstrap kit
  # ordering, so Trivy scanning here would race against an absent CRD
  # surface. Per-Sovereign overlay flips this on once bp-trivy is Ready.
  trivy:
    enabled: false
    image:
      repository: goharbor/trivy-adapter-photon
      tag: "v2.14.3"
    replicas: 1
    resources:
      requests:
        memory: 256Mi
        cpu: 100m
      limits:
        memory: 1Gi
        cpu: 1
    skipUpdate: false
    offlineScan: false
    timeout: 5m0s

  # ─── Notary (image signing) — DEPRECATED in upstream Harbor 2.x ────────
  # Sigstore (bp-sigstore, slot 32) is the Catalyst signing path. Notary
  # OFF.
  notary:
    enabled: false

  # ─── Exporter / metrics ────────────────────────────────────────────────
  # ServiceMonitor / metrics — DEFAULT FALSE per docs/BLUEPRINT-AUTHORING
  # .md §11.2 (Observability toggles default false; kube-prometheus-stack
  # CRDs may not exist on a fresh Sovereign).
  metrics:
    enabled: false
    core:
      path: /metrics
      port: 8001
    registry:
      path: /metrics
      port: 8001
    jobservice:
      path: /metrics
      port: 8001
    exporter:
      path: /metrics
      port: 8001
    serviceMonitor:
      enabled: false

  # ─── Logging ──────────────────────────────────────────────────────────
  logLevel: info

  # ─── Update strategy ──────────────────────────────────────────────────
  updateStrategy:
    type: RollingUpdate

# ─── Harbor admin credentials (issue #935) ────────────────────────────────
# Catalyst-curated admin Secret that the umbrella chart materialises in
# the harbor namespace and Reflector-mirrors into `catalyst` so the
# bp-self-sovereign-cutover Step 02 Job (which lives in the `catalyst`
# namespace) can read HARBOR_ADMIN_PASSWORD via secretKeyRef.
#
# Persistence: lookup-on-reconcile keeps the password stable across
# every helm upgrade. First-install path generates a 32-char alphanum
# password (190 bits entropy, per feedback_passwords.md). Operator may
# inject a fixed password via .password (e.g. when sealed-secrets pin
# it). The Secret is annotated with helm.sh/resource-policy=keep so
# `helm uninstall` does NOT delete the bytes — a re-install picks the
# same password back up via lookup.
harborAdmin:
  # Name of the Secret materialised in `.Release.Namespace` (the harbor
  # namespace by convention). The upstream Harbor subchart's
  # `existingSecretAdminPassword` field (default "harbor-admin", set in
  # the harbor: subchart values above) MUST match this value.
  secretName: "harbor-admin"
  # Operator-supplied password override. Empty = generate at first
  # install + preserve via lookup on every reconcile thereafter.
  password: ""
  # Comma-separated list of namespaces Reflector mirrors the admin
  # Secret into. `catalyst` is required for the cutover Job; operators
  # may add more if other in-cluster consumers need the bytes.
  reflectionAllowedNamespaces: "catalyst"

# ─── Catalyst overlay values (consumed by templates/ in this chart) ──────
# Reserved for Catalyst-side overlays added in a follow-up PR once
# bp-harbor is consumed in clusters/_template/:
#  - NetworkPolicy isolating harbor namespace from non-Catalyst traffic
#  - ExternalSecret projecting CNPG password + SeaweedFS S3 keys via ESO
#    (bp-external-secrets) so values.database.external.password and
#    values.persistence.imageChartStorage.s3.{accesskey,secretkey} land
#    via Reloader-restart-on-rotation rather than chart-render-time.
#  - cert-manager Certificate for harbor.<location-code>.<sovereign-domain>
#    pointing at the per-Sovereign Issuer.
#  - Keycloak OIDC client wiring for SSO when ssoEnabled flips true.
#  - Harbor Project + Pull-mirror policy bootstrap (project: catalyst,
#    pull-mirror: ghcr.io/openova-io/* every 6h).
harborOverlay:
  networkPolicy:
    enabled: false
    cnpgNamespace: "cnpg"
    keycloakNamespace: "keycloak"
  serviceMonitor:
    enabled: false                # docs/BLUEPRINT-AUTHORING.md §11.2
    interval: "30s"
    namespace: ""                 # default: release namespace
  externalSecret:
    enabled: false
  certificate:
    enabled: false
    issuerRef:
      kind: ClusterIssuer
      name: ""
  sso:
    enabled: false
    keycloakRealm: ""
    keycloakClientId: ""
  pullMirror:
    enabled: false
    upstreams: []     # list of {name, url, schedule, filter} entries

# ─── Catalyst HTTPRoute (Cilium Gateway API, issue #387) ──────────────────
# Replaces the upstream chart's networking.k8s.io/v1.Ingress on Sovereigns.
# Per-Sovereign overlay supplies `gateway.host` (e.g. registry.<sovereign-fqdn>).
# parentRef defaults to cilium-gateway/kube-system, the per-Sovereign
# Gateway shipped by clusters/_template/bootstrap-kit/01-cilium.yaml.
gateway:
  enabled: true
  # host: registry.<sovereign-fqdn> — REQUIRED, no default per Inviolable Principle #4
  host: ""
  path: "/"
  # Backend Service — defaults to <release>-harbor-core (harbor subchart core Service)
  backendService: ""
  backendPort: 80
  parentRef:
    name: cilium-gateway
    namespace: kube-system
    sectionName: https

# ─── Vendor-agnostic Object Storage backend config (issue #383 / #425) ───
#
# Per ADR-0001 §13 + docs/omantel-handover-wbs.md §3 + §3a, S3-aware
# apps (Harbor is one) write DIRECTLY to the cloud-provider's native S3
# endpoint. The per-Sovereign HelmRelease populates these fields via
# Flux `valuesFrom` against the canonical `flux-system/object-storage`
# Secret. Per #425 the seam is vendor-agnostic — same Secret name +
# same chart-values shape support a future AWS / Azure / GCP / OCI
# Sovereign without renaming the seam.
#
# Default values below produce a clean render on contabo (no
# credentials configured, type: filesystem) — see the bootstrap-kit
# slot at clusters/_template/bootstrap-kit/19-harbor.yaml for the
# canonical mapping.
# ─── CNPG Postgres cluster for Harbor metadata DB ────────────────────────
# These values are consumed by templates/cnpg-cluster.yaml (the Cluster CR)
# and templates/database-secret.yaml (the harbor-database-secret Secret).
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), all operationally-
# meaningful sizing/naming values are overridable via per-Sovereign overlays.
postgres:
  cluster:
    name: harbor-pg
    # Default to the chart's own targetNamespace. Override via per-Sovereign
    # overlay if the CNPG cluster lives in a different namespace.
    namespace: harbor
    instances: 1                  # bump to 3 alongside HA overlay once beyond Phase-0
    storageSize: 5Gi
    storageClass: local-path      # Contabo k3s default; Hetzner Sovereign overlays → hcloud-volumes
    pgVersion: "16"
    # MUST match harbor.database.external.coreDatabase. Harbor upstream
    # defaults coreDatabase="registry" and connects to a DB named "registry".
    database: registry
    owner: harbor
    resources:
      requests: { cpu: 50m, memory: 128Mi }
      limits:   { cpu: 500m, memory: 512Mi }

objectStorage:
  # When false, no credentials Secret is rendered (contabo, local dev,
  # etc.). Per-Sovereign overlay flips to true.
  enabled: false
  # When true, skip rendering the harbor-namespace credentials Secret
  # entirely — the operator has already created one out-of-band
  # (sealed-secret / external-secret / cloud-init / etc.).
  useExistingSecret: false
  # Override the default secret name — must match the value supplied to
  # the upstream chart's persistence.imageChartStorage.s3.existingSecret
  # field by the per-Sovereign overlay.
  credentialsSecretName: ""
  s3:
    # Operator-issued S3 access/secret keys. Plaintext at runtime ONLY
    # — Flux populates these via valuesFrom at HelmRelease apply time,
    # never committed to git.
    accessKey: ""
    secretKey: ""
