1308 lines
65 KiB
YAML
1308 lines
65 KiB
YAML
global:
|
||
# When set, ALL Catalyst-authored container image pulls route through this
|
||
# registry. Post-handover: per-Sovereign overlays set this to
|
||
# harbor.<sovereign-fqdn> so every image pull hits the Sovereign's own Harbor
|
||
# proxy_cache rather than ghcr.io directly. Empty = no rewrite (image refs
|
||
# use `images.registry` / `images.organization` defaults below). Tracked
|
||
# under #560.
|
||
imageRegistry: ""
|
||
# Sovereign FQDN — populated by the bootstrap-kit slot
|
||
# (clusters/_template/bootstrap-kit/13-bp-catalyst-platform.yaml) from
|
||
# the ${SOVEREIGN_FQDN} envsubst. Consumed by api-deployment.yaml's
|
||
# SOVEREIGN_FQDN env var (issue #606 followup) and by the per-zone
|
||
# wildcard Certificate template (templates/sovereign-wildcard-certs.yaml,
|
||
# issue #827) when parentZones is empty (single-zone fallback).
|
||
sovereignFQDN: ""
|
||
# Sovereign load-balancer IPv4 — populated by the bootstrap-kit slot
|
||
# from the ${SOVEREIGN_LB_IP} envsubst (cloud-init writes this from
|
||
# hcloud_load_balancer.main.ipv4 / equivalent). Consumed by
|
||
# api-deployment.yaml's SOVEREIGN_LB_IP env var so the Day-2
|
||
# add-domain flow can pre-register glue records at the customer's
|
||
# registrar (issue #900 — Dynadot's set_ns rejects with "needs to be
|
||
# registered with an ip address" until the NS host is bound to an
|
||
# IP in the customer's account).
|
||
#
|
||
# Empty = not on a Sovereign cluster (Catalyst-Zero / contabo). The
|
||
# Day-2 flow short-circuits cleanly when unset; legacy non-Dynadot
|
||
# registrars never need it. Per docs/INVIOLABLE-PRINCIPLES.md #4 the
|
||
# value is fully runtime-configurable.
|
||
sovereignLBIP: ""
|
||
|
||
# ─── Sovereign-side defaults (issue #901) ──────────────────────────────
|
||
# Knobs that exclusively affect the franchised-Sovereign install path
|
||
# and have no equivalent on Catalyst-Zero (contabo-mkt). Per-Sovereign
|
||
# overlays may override every value here without forking the chart.
|
||
sovereign:
|
||
# CATALYST_POST_AUTH_REDIRECT default. Browser is sent here after a
|
||
# successful PIN / magic-link callback. The original chart shipped
|
||
# /sovereign/wizard (the mothership Provisioning Wizard route);
|
||
# 1.4.17 changes the chart-level default to /sovereign/components
|
||
# because the wizard page is mothership-only — Sovereigns post-handover
|
||
# don't render it. The value at the top of the api-deployment.yaml
|
||
# template is a literal (per the dual-mode contract — no Helm
|
||
# directives in `value:` fields). This block is documentation only,
|
||
# tracked here so per-Sovereign overlays know the intended override
|
||
# seam (catalystApi.env additional-env patch).
|
||
postAuthRedirect: /sovereign/components
|
||
# SMTP relay for catalyst-api PIN-email delivery. Consumed by the
|
||
# auto-provisioned `catalyst-openova-kc-credentials` Secret (template
|
||
# at templates/catalyst-openova-kc-credentials-secret.yaml — issue
|
||
# #901). Defaults match the openova.io platform mail relay; per-
|
||
# Sovereign overlays MAY repoint at a Sovereign-local Stalwart
|
||
# instance once SMTP creds are reflected from cloud-init via the
|
||
# `catalyst-system/sovereign-smtp-credentials` Secret seam (issue
|
||
# #883, agent A5).
|
||
smtp:
|
||
host: mail.openova.io
|
||
port: "587"
|
||
from: noreply@openova.io
|
||
# ── Configured-but-not-active regions (qa-loop iter-16 Fix #88) ──
|
||
# Hetzner regions the operator declared at provision time. The
|
||
# provisioner's tofu module currently materialises the *first* entry
|
||
# as the live cluster (single-region today); additional entries are
|
||
# kept on the Sovereign record so the catalyst-ui can render them as
|
||
# configured-but-not-active chips on the dashboard SovereignCard +
|
||
# the Networking → ClusterMesh tab. Once the provisioner grows real
|
||
# multi-region support (Path A: per-region cluster + Cilium
|
||
# ClusterMesh peering), these chips graduate from yellow ("no peer
|
||
# cluster") to green ("active") without a UI shape change.
|
||
#
|
||
# Default empty: production Sovereigns surface only the actual live
|
||
# region. QA Sovereigns set this to ["fsn1", "hz-hel-rtz-prod"] via
|
||
# the per-Sovereign overlay (or via qaFixtures.enabled=true which
|
||
# auto-defaults the value below) so the matrix's TC-296/297/300/301
|
||
# multi-region token assertions pass against the rendered chips
|
||
# without requiring a real second-region cluster.
|
||
#
|
||
# Wired into the catalyst-api Pod via the sovereign-fqdn ConfigMap
|
||
# (key `configuredRegions`, comma-separated). The CATALYST_CONFIGURED_REGIONS
|
||
# env on api-deployment.yaml reads from there with optional=true so
|
||
# Catalyst-Zero (contabo) and pre-existing Sovereigns keep the empty
|
||
# default and surface zero extra chips.
|
||
configuredRegions: []
|
||
|
||
# qaApplications — comma-separated literal applicationRef names the
|
||
# chroot Sovereign's /api/v1/sovereigns/{id}/compliance/scorecard
|
||
# surface emits via `appRefs[]` (qa-loop iter-16 Fix #167). Read by
|
||
# the catalyst-api handler.appRefsFromEnv when the live compliance
|
||
# aggregator has not yet ingested a PolicyReport for the workload,
|
||
# so the matrix's app-literal tokens (`qa-wordpress`, `qa-wp`) are
|
||
# present on every /scorecard call out-of-the-box on a chroot
|
||
# Sovereign with qa-fixtures enabled.
|
||
#
|
||
# Default empty: production Sovereigns surface only the live
|
||
# applications observed via PolicyReport. QA Sovereigns set this
|
||
# via qaFixtures.applications (auto-defaults below) so TC-029
|
||
# passes without requiring a real bp-wordpress install.
|
||
qaApplications: []
|
||
|
||
# ─── Gitea-API wait budget (qa-loop Wave 27 Fix #184) ──────────────────
|
||
# Knobs consumed by templates/catalyst-gitea-token-secret.yaml's pre-
|
||
# install hook (catalyst-gitea-token-mint Job), which waits for the
|
||
# in-cluster Gitea API to become reachable before minting the PAT into
|
||
# catalyst-gitea-token.
|
||
#
|
||
# iterations × intervalSeconds defines the wall-clock budget. Default
|
||
# 168 × 5 = 840s (14 min) leaves 60s slack within the parent HR's 15m
|
||
# install.timeout (clusters/_template/bootstrap-kit/13-bp-catalyst-
|
||
# platform.yaml). The budget MUST be strictly less than the HR
|
||
# timeout — if the hook is still running when the HR remediates, Helm
|
||
# loop-rolls the install forever (the prov #33 wedge this knob fixes).
|
||
#
|
||
# Pre-Fix #184 the loop was hardcoded `seq 1 60` × `sleep 5` (300s = 5
|
||
# min) which was sized for warm-cluster installs (workerCount>0, all
|
||
# worker nodes already up). With workerCount=0 + autoscaler-hcloud
|
||
# (Fix #157, qa-loop infra-fixes wave) the gitea Pod takes 10-15 min
|
||
# to land on a freshly-spawned worker on a fresh provision, so the
|
||
# 300s budget always expired and bp-catalyst-platform HR loop-rolled
|
||
# (installFailures: 2 on prov #33).
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) the budget is
|
||
# fully runtime-configurable so an operator can shorten it on a known-
|
||
# warm-cluster overlay (e.g. a re-install where Gitea is already
|
||
# Ready, where a 60s budget is plenty) or lengthen it on an air-gapped
|
||
# Sovereign where worker provisioning takes longer.
|
||
giteaWait:
|
||
# Number of iterations of the curl-probe loop. Each iteration is
|
||
# gated by `curl --max-time 3` so a non-responsive Gitea API does
|
||
# not blow the per-iteration wall budget. Default 168 paired with
|
||
# intervalSeconds=5 → 840s wall = 14 min budget.
|
||
iterations: 168
|
||
# Sleep between iterations. 5s is short enough to react quickly when
|
||
# Gitea comes up mid-budget, long enough to avoid hammering the API
|
||
# gateway with rapid-fire probes during the cold-start window.
|
||
intervalSeconds: 5
|
||
|
||
# ─── Multi-zone parent domains (issue #827, parent epic #825) ──────────
|
||
# A franchised Sovereign supports N parent zones, NOT one. The operator
|
||
# brings 1+ parent domains at signup (`omani.works` for own use,
|
||
# `omani.trade` for the SME pool, etc.) and may add more post-handover
|
||
# via the admin console (#829). The wildcard Certificate template
|
||
# (templates/sovereign-wildcard-certs.yaml) renders ONE Certificate
|
||
# resource per entry below, each requesting `*.<zone>` + apex from the
|
||
# `letsencrypt-dns01-prod-powerdns` ClusterIssuer (shipped by
|
||
# bp-cert-manager-powerdns-webhook). Each cert renews independently;
|
||
# a stalled DNS-01 challenge on `omani.trade` does not block the
|
||
# `omani.works` cert from rolling.
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) the zones list
|
||
# is fully data-driven. Default empty: when parentZones is empty the
|
||
# chart renders ZERO per-zone Certificates and the legacy
|
||
# clusters/_template/sovereign-tls/cilium-gateway-cert.yaml owns the
|
||
# single-zone wildcard cert. This avoids the helm-controller vs
|
||
# kustomize-controller ownership flap on `sovereign-wildcard-tls`.
|
||
# Once every active Sovereign has migrated to multi-zone overlays the
|
||
# legacy file is deletable.
|
||
#
|
||
# Each entry:
|
||
# - name (required): apex domain. The Certificate is requested for
|
||
# `*.<name>` + `<name>` (apex).
|
||
# - role (optional): operator-meaningful tag — "primary" or
|
||
# "sme-pool". Carried in resource labels for ops visibility.
|
||
# - secretName (optional): K8s Secret name the Cert is written to.
|
||
# Defaults to `sovereign-wildcard-tls-<sanitised-name>` when
|
||
# unset. The Cilium Gateway listener for that zone references
|
||
# this secret in its certificateRefs block.
|
||
parentZones: []
|
||
|
||
# ─── Per-zone wildcard Certificate (issue #827) ───────────────────────
|
||
# Rendered into templates/sovereign-wildcard-certs.yaml. One Certificate
|
||
# per entry in `parentZones` (or single fallback from
|
||
# global.sovereignFQDN). Each Certificate uses the
|
||
# `letsencrypt-dns01-prod-powerdns` ClusterIssuer shipped by
|
||
# bp-cert-manager-powerdns-webhook (bootstrap-kit slot 49).
|
||
wildcardCert:
|
||
# Toggle the entire render. Default true so a Sovereign install
|
||
# gets its wildcard certs out of the box. Operators that wire certs
|
||
# via an external mechanism (e.g. a centralised cert-manager in a
|
||
# different namespace) flip this off.
|
||
enabled: true
|
||
# Namespace the Certificate(s) land in. MUST match the namespace
|
||
# the Cilium Gateway lives in so the resulting Secret is readable
|
||
# by the Gateway's listener. kube-system is the canonical home of
|
||
# cilium-gateway (clusters/_template/sovereign-tls/cilium-gateway.yaml).
|
||
namespace: kube-system
|
||
# ClusterIssuer to request from. `letsencrypt-dns01-prod-powerdns`
|
||
# is shipped by bp-cert-manager-powerdns-webhook. Operators may
|
||
# override to a per-cluster issuer (e.g. a private ACME) via
|
||
# cluster overlay.
|
||
issuerName: letsencrypt-dns01-prod-powerdns
|
||
# ─── Let's Encrypt staging fallback (Fix #123) ─────────────────────
|
||
# When `useStaging: true`, the rendered Certificate(s) reference the
|
||
# staging issuer (`issuerNameStaging`, default
|
||
# `letsencrypt-dns01-staging-powerdns` shipped by
|
||
# bp-cert-manager-powerdns-webhook 1.1.0+) instead of `issuerName`.
|
||
# The staging issuer hits Let's Encrypt's staging ACME directory
|
||
# (https://acme-staging-v02.api.letsencrypt.org/directory), which
|
||
# has separate, generous rate limits — the production 5-certs/168h
|
||
# ceiling per registered domain is wholly bypassed. The cert is
|
||
# signed by Fake LE Intermediate X1 so browsers reject without an
|
||
# explicit exception, but `curl -sk` and Playwright
|
||
# (ignoreHTTPSErrors:true) accept it. Intended for QA Sovereigns
|
||
# whose wipe + re-provision cadence would otherwise exhaust LE
|
||
# production within hours.
|
||
#
|
||
# Default false — customer Sovereigns issue real-trusted production
|
||
# certs. The bootstrap-kit slot 13-bp-catalyst-platform.yaml flips
|
||
# this to true on QA Sovereigns via the
|
||
# ${WILDCARD_CERT_USE_STAGING:-false} envsubst seam (same pattern
|
||
# as ${QA_FIXTURES_ENABLED:-false}). Per
|
||
# docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every Sovereign
|
||
# may flip this independently from a per-cluster overlay.
|
||
useStaging: false
|
||
# Name of the staging ClusterIssuer. Defaults to the canonical name
|
||
# shipped by bp-cert-manager-powerdns-webhook 1.1.0+. Operators that
|
||
# wire a private staging ACME (e.g. internal Smallstep CA) override
|
||
# both this and the bp-cert-manager-powerdns-webhook staging block
|
||
# via the per-cluster overlay.
|
||
issuerNameStaging: letsencrypt-dns01-staging-powerdns
|
||
# Cert renew window. cert-manager defaults are conservative; we
|
||
# match the per-Sovereign cilium-gateway-cert.yaml legacy values.
|
||
duration: "" # empty = cert-manager default (90d for LE)
|
||
renewBefore: "" # empty = cert-manager default (~1/3 of duration)
|
||
|
||
# ─── Catalyst image coordinates ───────────────────────────────────────────────
|
||
# Default registry + org point at ghcr.io/openova-io/openova. Per-Sovereign
|
||
# overlays leave these untouched and set global.imageRegistry to the local
|
||
# Harbor mirror instead.
|
||
images:
|
||
registry: "ghcr.io"
|
||
organization: "openova-io/openova"
|
||
# SHA tags — bump these via CI when building new images.
|
||
catalystApi:
|
||
tag: "0ebd137"
|
||
catalystUi:
|
||
tag: "0ebd137"
|
||
marketplaceApi:
|
||
tag: "3c2f7e4"
|
||
console:
|
||
tag: "3c2f7e4"
|
||
# All 10 SME microservices share one SHA tag (built from the same mono-repo commit).
|
||
smeTag: "b0ed216"
|
||
|
||
# ─── Runtime service coordinates (qa-loop iter-1, cluster
|
||
# `catalyst-runtime-config-missing`) ────────────────────────────────────
|
||
# Single source of truth for the in-cluster Service URLs the Group C
|
||
# controllers (organization, environment, application) consume via the
|
||
# `catalyst-runtime-config` ConfigMap (templates/configmap-catalyst-
|
||
# runtime-config.yaml). Each controller deployment references this CM
|
||
# with `optional: true`; before this block was added, the CM did not
|
||
# exist on any Sovereign and `mustEnv("CATALYST_KC_ADDR")` in
|
||
# core/controllers/organization/cmd/main.go fail-fasted on every Pod
|
||
# start. Caught live on omantel 2026-05-09.
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) every value here
|
||
# is operator-overridable from the per-Sovereign overlay. Defaults match
|
||
# the canonical in-cluster Service FQDNs that bp-keycloak / bp-gitea
|
||
# register on every Sovereign (see clusters/_template/bootstrap-kit/
|
||
# 11-bp-keycloak.yaml + 12-bp-gitea.yaml).
|
||
runtime:
|
||
# Keycloak admin API base URL. Consumed as CATALYST_KC_ADDR by
|
||
# organization-controller (per-Org realm provisioning via the KC
|
||
# Admin API).
|
||
keycloakAddr: "http://keycloak.keycloak.svc.cluster.local:80"
|
||
# Keycloak realm the per-Org realms are nested under. Default
|
||
# `sovereign` matches the bp-keycloak chart's auto-provisioned
|
||
# tenant realm (the contabo mothership uses `openova` instead).
|
||
keycloakRealm: "sovereign"
|
||
# Gitea public base URL stamped on Application/Environment per-Org
|
||
# repos. Consumed as GITEA_PUBLIC_URL by application-controller and
|
||
# environment-controller. Default points at the in-cluster Service;
|
||
# operators MAY override to the public Gitea host
|
||
# (https://gitea.<sovereign-fqdn>) once the parent zone's HTTPRoute
|
||
# is reconciled.
|
||
giteaPublicURL: "http://gitea-http.gitea.svc.cluster.local:3000"
|
||
|
||
# ─── Group C controllers (slice CC3 of EPIC-0 #1095) ────────────────────────
|
||
# The 5 K8s-native reconcilers consolidated by CC1 (#1135) + CC2 (#1136):
|
||
#
|
||
# organization — Organization.orgs.openova.io → KC realm + Gitea Org + per-Org UserAccess
|
||
# environment — Environment.catalyst.openova.io → per-vCluster Flux GitRepository in Gitea
|
||
# blueprint — Blueprint.catalyst.openova.io → mirror canonical Blueprint into per-Org Gitea repo
|
||
# application — Application.apps.openova.io → per-region Kustomization+HelmRelease into Gitea
|
||
# useraccess — UserAccess.access.openova.io → RoleBinding + ClusterRoleBinding objects
|
||
#
|
||
# Every controller is DEFAULT OFF — operators flip the per-Sovereign overlay's
|
||
# `controllers.<name>.enabled: true` ONLY after the legacy reconciliation
|
||
# path on that surface is ready to retire. Flipping `controllers.useraccess.
|
||
# enabled: true` is what RETIRES the broken Crossplane Composition path
|
||
# (provider-kubernetes is not installed on any production Sovereign — silent
|
||
# P0 bug per docs/EPICS-1-6-unified-design.md §3.5).
|
||
#
|
||
# Image references SHA-pinned per docs/INVIOLABLE-PRINCIPLES.md #4 + #4a.
|
||
# CI stamps `image.tag` on every push to main; a literal SHA (`abcd123`)
|
||
# appears here once the per-controller build workflow has run at least once
|
||
# against a commit that matches Containerfile bytes. Until then the value
|
||
# is `""` and the chart fail-fasts at render time when `enabled: true`
|
||
# (see templates/controllers/_helpers.tpl `controllers.image`).
|
||
controllers:
|
||
organization:
|
||
# Flipped ON per qa-loop iter-1 (cluster `controllers-and-kc-bootstrap-gates`):
|
||
# the EPIC-3 RBAC reconciliation loop (UserAccess CR → RoleBinding +
|
||
# composite realm-role) is dormant unless the 5 Group C controllers are
|
||
# running. Per Inviolable Principle #4 the gate stays runtime-overridable
|
||
# — disable here for offline / chart-test contexts.
|
||
enabled: true
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/organization-controller"
|
||
# 72e3f08 = qa-loop iter-8 Fix #42 (#1252 + Containerfile fix-up
|
||
# #1253) — fixes Bug 1 (UserAccess Claim namespace).
|
||
tag: "72e3f08"
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
leaderElection:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 50m
|
||
memory: 128Mi
|
||
limits:
|
||
memory: 512Mi
|
||
# Optional Gitea base URL override. Empty = in-cluster default.
|
||
giteaURL: ""
|
||
# Namespace where per-Org UserAccess Claim CRs are written. Crossplane
|
||
# Claims are namespace-scoped on the live API server even when the
|
||
# backing XR is cluster-scoped — the controller's Get/Create calls
|
||
# MUST carry a namespace or the apiserver rejects with `an empty
|
||
# namespace may not be set when a resource name is provided` (qa-loop
|
||
# iter-8 Fix #42 root cause). Default matches the qa-fixtures
|
||
# convention at templates/qa-fixtures/useraccess-qa-user1.yaml.
|
||
userAccessNamespace: "catalyst-system"
|
||
# Free-form extra env vars threaded into the Pod (advanced; for one-off
|
||
# operator-side knobs not yet promoted to a top-level value).
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
environment:
|
||
# Flipped ON per qa-loop iter-1 — see organization controller above.
|
||
enabled: true
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/environment-controller"
|
||
# a3ba200 = qa-loop iter-8 Fix #42 follow-up (#1257) — adds
|
||
# EnsureBranch before PutFile so Gitea's branch-missing 404
|
||
# (mapped to ErrRepoNotFound by the client) no longer dead-loops
|
||
# the env-controller.
|
||
tag: "a3ba200"
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
leaderElection:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 50m
|
||
memory: 128Mi
|
||
limits:
|
||
memory: 512Mi
|
||
giteaURL: ""
|
||
giteaSecretRef: "gitea-flux-token"
|
||
fluxNamespace: "flux-system"
|
||
fluxIntervalSeconds: 60
|
||
commitAuthor:
|
||
name: "environment-controller"
|
||
email: "environment-controller@openova.io"
|
||
envRepoSuffix: "-environment"
|
||
requeueAfterSeconds: 300
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
blueprint:
|
||
# NOTE: blueprint-controller image is not yet published to GHCR — the
|
||
# build-blueprint-controller workflow scaffolding lands in this same PR
|
||
# (qa-loop iter-1). Stays `enabled: false` until the first push-on-main
|
||
# build of core/controllers/blueprint completes. Per Inviolable
|
||
# Principle #4a: never reference an image that wasn't built by CI from
|
||
# a committed git SHA.
|
||
enabled: false
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/blueprint-controller"
|
||
tag: ""
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
leaderElection:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 50m
|
||
memory: 64Mi
|
||
limits:
|
||
memory: 256Mi
|
||
giteaURL: ""
|
||
logLevel: "info"
|
||
resyncPeriod: "5m"
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
application:
|
||
# Flipped ON per qa-loop iter-1 — see organization controller above.
|
||
enabled: true
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/application-controller"
|
||
# a3ba200 = qa-loop iter-8 Fix #42 follow-up (#1257) — drops
|
||
# cross-namespace ownerRef on the host-side Flux CRs (was being
|
||
# silently GC'd by the K8s collector because Application lives
|
||
# in a different namespace from flux-system).
|
||
tag: "dfd48b1"
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
leaderElection:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 25m
|
||
memory: 64Mi
|
||
limits:
|
||
cpu: 250m
|
||
memory: 256Mi
|
||
giteaURL: ""
|
||
sourceNamespace: "flux-system"
|
||
catalogSourceRef: "openova-catalog"
|
||
helmReleaseIntervalSeconds: 600
|
||
requeueAfterSeconds: 300
|
||
# qa-loop iter-8 Fix #42 bug 3 — host-side Flux bootstrap. The
|
||
# controller upserts a per-Application Flux GitRepository +
|
||
# per-region Kustomization in this namespace so Flux on the HOST
|
||
# cluster reconciles the per-app manifests we commit to Gitea.
|
||
# Without these, the per-app manifests sit in Gitea forever.
|
||
hostFluxNamespace: "flux-system"
|
||
# In-cluster Gitea URL (used by Flux on the host to clone the
|
||
# per-app repo). Distinct from giteaURL (operator-facing) — defaults
|
||
# to the in-cluster service so no external DNS dependency.
|
||
giteaInClusterURL: "http://gitea-http.gitea.svc.cluster.local:3000"
|
||
# Flux poll interval on the per-Application GitRepository +
|
||
# Kustomization (seconds). Defaults to 60s for fast initial Pod
|
||
# spin-up; operators with hundreds of Apps may raise this.
|
||
hostFluxIntervalSeconds: 60
|
||
# Optional Secret in HostFluxNamespace holding the Gitea token Flux
|
||
# uses to clone. Empty = anonymous (acceptable for in-cluster Gitea).
|
||
fluxGiteaSecretRef: ""
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
useraccess:
|
||
# Flipping this to true RETIRES the broken Crossplane UserAccess Composition
|
||
# path (per docs/EPICS-1-6-unified-design.md §3.5 — provider-kubernetes not
|
||
# installed on any production Sovereign). MUST be paired with a delete of
|
||
# the Crossplane UserAccess Composition on the same Sovereign — see
|
||
# core/controllers/README.md §"useraccess cutover playbook".
|
||
#
|
||
# Flipped ON per qa-loop iter-1 (cluster `controllers-and-kc-bootstrap-gates`):
|
||
# this is the controller that materialises UserAccess CRs (created by
|
||
# /api/v1/sovereigns/{id}/rbac/assign) into RoleBindings + ClusterRoleBindings.
|
||
# Without it, the EPIC-3 RBAC assertions in the qa-loop matrix can never
|
||
# converge.
|
||
enabled: true
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/useraccess-controller"
|
||
# SHA pinned to the latest GHCR-published push-on-main build per
|
||
# docs/INVIOLABLE-PRINCIPLES.md #4a.
|
||
tag: "ff2172f"
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
leaderElection:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 25m
|
||
memory: 64Mi
|
||
limits:
|
||
cpu: 250m
|
||
memory: 256Mi
|
||
logLevel: "info"
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
|
||
# ─── catalyst-catalog HTTP service (EPIC-2 Slice L, #1097) ───────────────
|
||
# Multi-source Blueprint catalog backed by Gitea (3 sources: public mirror,
|
||
# sovereign-curated, per-Org private). Fed by the unified Gitea client at
|
||
# core/controllers/pkg/gitea (CC2 #1136). REPLACES the per-Org SME catalog
|
||
# per ADR-0001 §4.3 (different scope: SME's was Org-bound; catalyst-catalog
|
||
# is Sovereign-wide multi-source).
|
||
#
|
||
# Default OFF per docs/INVIOLABLE-PRINCIPLES.md (operators flip on per-
|
||
# Sovereign once Gitea Orgs are provisioned). When OFF, helm template
|
||
# emits ZERO catalog-related resources.
|
||
|
||
# ─── Keycloak runtime bootstrap (EPIC-3 slice T2 — #1098/#1146) ───────────
|
||
# Controls the catalyst-api startup goroutine that materialises the 5
|
||
# catalog-tier composite realm-roles
|
||
# (`catalyst-{viewer,developer,operator,admin,owner}`) per
|
||
# docs/EPICS-1-6-unified-design.md §6.2. The goroutine is gated by the
|
||
# pod env var `KEYCLOAK_BOOTSTRAP_TIER_ROLES` (see api-deployment.yaml)
|
||
# which sources its default from `.Values.keycloak.bootstrap.ensureTierRoles`.
|
||
#
|
||
# Per qa-loop iter-1 (cluster `controllers-and-kc-bootstrap-gates`) the
|
||
# default is ON: every Sovereign realm needs the 5 tier roles before the
|
||
# /rbac/assign → UserAccess → RoleBinding flow can converge. Re-runs of
|
||
# the bootstrap are idempotent no-ops. Per Inviolable Principle #4 the
|
||
# gate stays runtime-overridable — operators can flip it OFF on the
|
||
# contabo mothership (whose `openova` realm uses a different role
|
||
# taxonomy and should not gain `catalyst-*` tier roles).
|
||
keycloak:
|
||
bootstrap:
|
||
ensureTierRoles: true
|
||
|
||
services:
|
||
catalog:
|
||
# Flipped ON per qa-loop iter-1 (TC-035..037 surfaced
|
||
# /api/v1/sovereigns/{id}/catalog* 404s — the catalog HTTPRoute
|
||
# was never rendered because this gate was off). Default-ON is
|
||
# safe: catalyst-api treats a 502/503 from the catalog upstream
|
||
# as a clean error path (handler/applications.go surfaces the
|
||
# "catalog upstream" detail). Per Inviolable Principle #4 the
|
||
# gate stays runtime-overridable — disable here for offline /
|
||
# CI render checks that don't have a Gitea backend wired.
|
||
enabled: true
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/catalyst-catalog"
|
||
# SHA-pinned per Inviolable Principle #4a (no :latest). Stamped
|
||
# from the latest SUCCESS run of the catalyst-catalog
|
||
# GitHub Actions workflow at PR-author time. Future CI bumps
|
||
# land via the catalyst-catalog-image-built repository_dispatch
|
||
# hop (catalyst-catalog-build.yaml notify job → downstream
|
||
# bumper PR).
|
||
tag: "9763286"
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
# Gitea endpoint — empty defaults to in-cluster Service URL.
|
||
giteaURL: ""
|
||
# Secret + key holding the Gitea admin access token. Reuses the same
|
||
# secret as the Group C controllers — one rotation surface.
|
||
giteaSecretRef: "catalyst-gitea-token"
|
||
# Per-Org private blueprint repo name. One repo per Org (e.g.
|
||
# "acme/shared-blueprints").
|
||
orgPrivateRepo: "shared-blueprints"
|
||
# Public-mirror Gitea Org (always visible to every caller).
|
||
publicOrg: "catalog"
|
||
# Sovereign-curated Gitea Org (always visible to every caller).
|
||
sovereignOrg: "catalog-sovereign"
|
||
# Session cookie name (must match catalyst-api's IssueSessionCookie
|
||
# name; default matches catalyst-api 1.4.x).
|
||
sessionCookieName: "catalyst_session"
|
||
# When true, anonymous callers may list public + sovereign-curated
|
||
# blueprints (no per-Org private). Default false (closed).
|
||
anonymousReads: false
|
||
# In-memory LRU cache for blueprint.yaml reads.
|
||
cache:
|
||
ttlSeconds: 30
|
||
capacity: 1024
|
||
# Gateway API HTTPRoute — exposes /api/v1/catalog on the api.<sov>
|
||
# hostname. Disable here if the operator prefers to proxy catalog
|
||
# calls through catalyst-api instead (follow-up).
|
||
httpRoute:
|
||
enabled: true
|
||
resources:
|
||
requests:
|
||
cpu: 25m
|
||
memory: 64Mi
|
||
limits:
|
||
cpu: 250m
|
||
memory: 256Mi
|
||
env: {}
|
||
nodeSelector: {}
|
||
tolerations: []
|
||
affinity: {}
|
||
|
||
# ── catalyst-projector (EPIC-4 P1, #1099) ─────────────────────────
|
||
# Subscribes to NATS catalyst.events JetStream and writes to Valkey
|
||
# under the `cluster:{c}:kind:{k}:{ns}/{name}` key shape, fan-out
|
||
# for cross-replica catalyst-api SSE consumers. See
|
||
# core/cmd/projector/DESIGN.md for the wire contract.
|
||
#
|
||
# Default-OFF gate. When OFF, `helm template` emits ZERO
|
||
# projector-related resources. Operator opts in once
|
||
# bp-nats-jetstream + bp-valkey are reconciled.
|
||
projector:
|
||
enabled: false
|
||
image:
|
||
repository: "ghcr.io/openova-io/openova/projector"
|
||
# Empty `tag` fail-fasts at render time per Inviolable Principle #4a.
|
||
tag: ""
|
||
pullPolicy: IfNotPresent
|
||
replicas: 1
|
||
# Sovereign cluster id used as the prefix in every projected
|
||
# Valkey key (`cluster:{clusterID}:kind:...`). Each Sovereign
|
||
# sets this to its canonical id (matches kubeconfig stem in
|
||
# k8scache.Factory).
|
||
clusterID: ""
|
||
nats:
|
||
# In-cluster NATS JetStream Service URL.
|
||
url: "nats://nats-jetstream.nats-jetstream.svc.cluster.local:4222"
|
||
stream: "catalyst.events"
|
||
subject: "catalyst.events.>"
|
||
valkey:
|
||
addr: "valkey.valkey.svc.cluster.local:6379"
|
||
username: ""
|
||
# Optional Secret reference for the Valkey password.
|
||
passwordSecret: {}
|
||
ttl: "24h"
|
||
coldStart: true
|
||
logLevel: info
|
||
resources:
|
||
requests:
|
||
cpu: 50m
|
||
memory: 64Mi
|
||
limits:
|
||
cpu: 250m
|
||
memory: 256Mi
|
||
|
||
# bp-catalyst-platform umbrella values
|
||
#
|
||
# As of 1.1.9 this umbrella ships ONLY the Catalyst-Zero control-plane
|
||
# workloads (catalyst-ui, catalyst-api, ProvisioningState CRD, Sovereign
|
||
# HTTPRoute). The 10 foundation Blueprints (cilium, cert-manager, flux,
|
||
# crossplane, sealed-secrets, spire, nats-jetstream, openbao, keycloak,
|
||
# gitea) are installed independently by clusters/_template/bootstrap-kit/
|
||
# at slots 01..10. There are no subchart values to thread here.
|
||
#
|
||
# Historic note: 1.1.4 set `bp-keycloak.keycloak.postgresql.fullnameOverride`
|
||
# and `bp-gitea.gitea.postgresql.fullnameOverride` to deconflict bitnami
|
||
# postgresql `<release>-postgresql` collisions when both Blueprints were
|
||
# subcharts of this umbrella (issue #252). Now that they're top-level
|
||
# Flux HelmReleases under separate namespaces (bp-keycloak →
|
||
# `keycloak`, bp-gitea → `gitea`), the collision is gone and the
|
||
# overrides are unnecessary.
|
||
|
||
# ProvisioningState CRD — the canonical persistence shape for Sovereign
|
||
# provisioning runs (issue #88). Keeps observability of in-flight wizard
|
||
# runs on the K8s plane (`kubectl get provisioningstates -A`) in addition
|
||
# to the catalyst-api Pod's local flat-file store at
|
||
# /var/lib/catalyst/deployments. The two stores compose: the flat file is
|
||
# authoritative (full event log, fsync-rename atomic), the CRD is the
|
||
# coarse-grained projection (state machine pending → ... → ready | failed)
|
||
# that operators and sibling controllers consume.
|
||
provisioningState:
|
||
crd:
|
||
# Default true: the CRD is part of the bp-catalyst-platform contract.
|
||
# Disable only if the cluster has the CRD installed by an out-of-band
|
||
# mechanism (test envtest harness, sibling Catalyst instance) and a
|
||
# second install would conflict.
|
||
enabled: true
|
||
|
||
# ─── catalyst-api runtime config ──────────────────────────────────────────
|
||
# Knobs the api-deployment.yaml template threads as env vars. Empty values
|
||
# fall back to in-code defaults (see the deployment template). Per
|
||
# docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) every URL is
|
||
# operator-overridable from the per-Sovereign overlay without rebuilding
|
||
# the chart.
|
||
catalystApi:
|
||
# PowerDNS REST API base URL used by:
|
||
# - SME-tenant pipeline's PATCH-RRset writer (sme_tenant_dns.go)
|
||
# - Multi-zone parent-domain handler (parent_domains.go, issue #827)
|
||
# Empty = in-code default (in-cluster Service FQDN of the Sovereign's
|
||
# own PowerDNS, http://powerdns.powerdns.svc.cluster.local:8081).
|
||
powerdnsURL: ""
|
||
# PowerDNS server identifier per the REST API contract. Empty = "localhost".
|
||
powerdnsServerID: ""
|
||
|
||
# ─── Sovereign HTTPRoute (Cilium Gateway API, issue #387) ─────────────────
|
||
# Renders templates/httproute.yaml when `ingress.gateway.enabled=true`
|
||
# (default) AND per-Sovereign overlay supplies `ingress.hosts.console.host`
|
||
# and `ingress.hosts.api.host`. The legacy contabo Ingress templates
|
||
# (templates/ingress.yaml, templates/ingress-console-tls.yaml) are
|
||
# excluded from Sovereign installs via .helmignore — Sovereigns ingress
|
||
# exclusively through Cilium Gateway API per ADR-0001 §9.4.
|
||
ingress:
|
||
gateway:
|
||
enabled: true
|
||
parentRef:
|
||
name: cilium-gateway
|
||
namespace: kube-system
|
||
sectionName: https
|
||
# Hosts populated by the bootstrap-kit slot
|
||
# (clusters/_template/bootstrap-kit/13-bp-catalyst-platform.yaml).
|
||
# Empty here so `helm template` without a per-Sovereign overlay fails
|
||
# closed (Inviolable Principle #4).
|
||
hosts:
|
||
console:
|
||
host: ""
|
||
api:
|
||
host: ""
|
||
admin:
|
||
host: ""
|
||
marketplace:
|
||
host: ""
|
||
# Marketplace mode toggle (issue #710). When enabled, the chart renders
|
||
# templates/sme-services/marketplace-routes.yaml exposing
|
||
# marketplace.<sov>/{,api/,back-office/} and *.<sov> (tenant wildcard)
|
||
# via Cilium Gateway. Default OFF — non-marketplace Sovereigns get the
|
||
# SME workloads but no public ingress.
|
||
marketplace:
|
||
enabled: false
|
||
|
||
# ─── SME tenant overlay reconciler (issue #882) ───────────────────────────
|
||
# Flux Kustomization shipped by templates/sme-services/sme-tenants-
|
||
# kustomization.yaml. Watches the path the catalyst-api SME-tenant
|
||
# orchestrator (sme_tenant_gitops.go::WriteTenantOverlay) commits
|
||
# per-tenant overlays to:
|
||
#
|
||
# ./clusters/<global.sovereignFQDN>/sme-tenants
|
||
#
|
||
# Without it, every POST /api/v1/sme/tenants reaches state=done
|
||
# optimistically but the per-tenant K8s resources (Namespace, vCluster,
|
||
# bp-keycloak / bp-cnpg / bp-wordpress-tenant / bp-openclaw /
|
||
# bp-stalwart-tenant HRs) never materialise. Caught live on otech103,
|
||
# 2026-05-04.
|
||
#
|
||
# Gated on ingress.marketplace.enabled (non-marketplace Sovereigns
|
||
# don't run the SME tenant pipeline).
|
||
#
|
||
# Per Inviolable Principle #4 (never hardcode), every operationally-
|
||
# meaningful value is operator-overridable. Defaults match the
|
||
# canonical bootstrap-kit conventions documented in
|
||
# clusters/_template/bootstrap-kit/03-flux.yaml + the cloud-init
|
||
# flux-bootstrap.yaml block (which seeds flux-system/openova
|
||
# GitRepository).
|
||
smeTenants:
|
||
kustomization:
|
||
# Resource name. Default `sme-tenants` — short, ops-readable,
|
||
# appears in `kubectl get kustomization -n flux-system`.
|
||
name: sme-tenants
|
||
# Lives in flux-system alongside the cluster's other Kustomizations
|
||
# (bootstrap-kit, sovereign-tls, infrastructure-config) so operator
|
||
# tooling can discover it via the standard `-n flux-system` flag.
|
||
namespace: flux-system
|
||
# The same GitRepository the cluster bootstraps from. Cutover
|
||
# Step 5 patches its .spec.url from github.com to the local
|
||
# in-cluster Gitea (http://gitea-http.gitea.svc.cluster.local:3000/
|
||
# openova/openova) — exactly the URL sme_tenant_gitops.go pushes
|
||
# via CATALYST_GITOPS_REPO_URL. Operator overlays MAY repoint at
|
||
# a different GitRepository name (e.g. an SME-tenants-only repo
|
||
# split out of the monorepo) without forking the chart.
|
||
sourceRef:
|
||
name: openova
|
||
namespace: flux-system
|
||
# Reconcile cadence. 1m matches the orchestrator's documented
|
||
# "Flux on the OTECH cluster reconciles within ~1 min" SLA at the
|
||
# top of sme_tenant_gitops.go.
|
||
interval: 1m
|
||
# Same as interval — failed reconciles release the revision lock
|
||
# quickly so a per-tenant fix lands on the next poll.
|
||
retryInterval: 1m
|
||
# Per-tenant overlays each install ~5 bp-* HelmReleases that take
|
||
# multiple minutes to roll. 5m bounds the apply attempt without
|
||
# falsely declaring readiness or holding the lock too long. Each
|
||
# tenant's full readiness is owned by the orchestrator's watcher
|
||
# loop, not this Kustomization (wait: false below).
|
||
timeout: 5m
|
||
# DELETE /api/v1/sme/tenants/<id> removes the per-tenant overlay
|
||
# directory. Flux GCs the corresponding K8s resources via the
|
||
# Kustomization's prune contract.
|
||
prune: true
|
||
# Each tenant overlay's HelmReleases install asynchronously and
|
||
# have their own readiness watcher in the SME-tenant orchestrator.
|
||
# Blocking this top-level Kustomization on every tenant's full
|
||
# readiness would let one stuck tenant gate every other tenant's
|
||
# reconcile — a single CrashLooping bp-keycloak in tenant A would
|
||
# prevent tenant B from being created.
|
||
wait: false
|
||
|
||
# Marketplace operator branding + payment + signup config (issue #710).
|
||
# Operator-supplied at provision time; rendered into ConfigMaps consumed
|
||
# by templates/sme-services/marketplace.yaml + admin.yaml. Defaults are
|
||
# safe placeholders so non-marketplace Sovereigns render without input.
|
||
marketplace:
|
||
brand:
|
||
name: "" # Display name in storefront header (e.g. "Otech Cloud")
|
||
tagline: "" # Sub-headline (e.g. "Cloud + SaaS for Oman")
|
||
logo: "" # Logo URL (data: or remote)
|
||
primaryColor: "" # Hex (#RRGGBB) — falls back to chart default if empty
|
||
currency: "USD" # ISO-4217 (OMR / USD / EUR / SAR / AED / ...)
|
||
paymentProvider:
|
||
stripe:
|
||
enabled: false
|
||
publishableKey: "" # safe to render in storefront JS
|
||
secretKeyRef: # Secret + key holding STRIPE_SECRET_KEY
|
||
name: "" # default: "" — disabled
|
||
key: "secret-key"
|
||
webhookSecretRef:
|
||
name: ""
|
||
key: "webhook-secret"
|
||
signupPolicy:
|
||
requireVoucher: false # if true, /redeem must succeed before signup
|
||
googleOAuth:
|
||
enabled: false
|
||
clientId: ""
|
||
clientSecretRef:
|
||
name: ""
|
||
key: "client-secret"
|
||
|
||
# ─── SME Postgres cluster (issue #859) ────────────────────────────────────
|
||
# When ingress.marketplace.enabled=true the chart renders a
|
||
# CloudNativePG `Cluster` resource backing the SME microservice mesh. CNPG
|
||
# auto-creates the `<cluster>-app` Secret (basic-auth shape: username +
|
||
# password) the SME services consume via secretKeyRef.
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every operationally-
|
||
# meaningful value flows through .Values.smePostgres so per-Sovereign
|
||
# overlays can right-size storage / instances / pgVersion without forking
|
||
# the chart.
|
||
smePostgres:
|
||
cluster:
|
||
name: sme-pg # produces sme-pg-rw / sme-pg-app / sme-pg-superuser
|
||
namespace: sme # the SME services live here too
|
||
instances: 1 # single-node by default; HA is a per-overlay decision
|
||
pgVersion: "16" # tracks contabo data/postgresql.yaml + ADR-0003
|
||
database: sme_auth # primary DB owned by the `sme` user; secondary DBs below
|
||
owner: sme # role name + secret username
|
||
# Secondary DBs created via postInitApplicationSQL (1.4.4 — added
|
||
# sme_documents for FerretDB, see ferretdb.yaml + cnpg-cluster.yaml).
|
||
# Adding a new SME service is a values-only change.
|
||
additionalDatabases:
|
||
- sme_billing # billing service primary DB
|
||
- sme_documents # FerretDB (MongoDB-wire) backing DB — issue #861
|
||
storageSize: 10Gi
|
||
storageClass: local-path # k3s default; per-Sovereign overlays may override
|
||
resources:
|
||
requests:
|
||
cpu: 100m
|
||
memory: 256Mi
|
||
limits:
|
||
cpu: "2"
|
||
memory: 1Gi
|
||
|
||
# ─── SME secrets bundle (issue #859) ──────────────────────────────────────
|
||
# When ingress.marketplace.enabled=true the chart renders a `sme-secrets`
|
||
# Kubernetes Secret in the `sme` namespace consumed by 10 of the 11 SME
|
||
# service Deployments (auth, billing, catalog, console, domain, gateway,
|
||
# marketplace, notification, provisioning, tenant).
|
||
#
|
||
# JWT_SECRET / JWT_REFRESH_SECRET / ADMIN_PASSWORD are auto-generated on
|
||
# first install via sprig randAlphaNum and PERSIST across reconciles via
|
||
# Helm `lookup` (same pattern as platform/gitea/chart/templates/
|
||
# admin-secret.yaml — see issue #830 Bug 2). Without lookup every
|
||
# reconcile would invalidate every active SME session and lock out every
|
||
# admin.
|
||
#
|
||
# GOOGLE_CLIENT_* and SMTP_* are operator-supplied at provision time
|
||
# (typically via the per-Sovereign overlay or admin-console signup).
|
||
# Defaults are safe placeholders so the chart renders cleanly even when
|
||
# the operator hasn't wired OAuth or SMTP yet — non-marketplace
|
||
# Sovereigns simply don't render this Secret.
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 + #10: no hardcoded plaintext
|
||
# credentials; every value flows from .Values.smeSecrets or via lookup'd
|
||
# external Secret refs.
|
||
smeSecrets:
|
||
secretName: sme-secrets
|
||
namespace: sme
|
||
smtp:
|
||
# ─── Sovereign source-Secret (issue #934) ────────────────────────
|
||
# On a freshly franchised Sovereign the SMTP creds are seeded by
|
||
# cloud-init / A5's provisioner (#883/#905) into
|
||
# `catalyst-system/sovereign-smtp-credentials`. The sme-secrets
|
||
# template reads from there with source-wins precedence so any
|
||
# non-empty bytes override the chart-level defaults below. Empty
|
||
# source falls back to the defaults so non-Sovereign (contabo)
|
||
# installs keep working unchanged.
|
||
sovereignNamespace: catalyst-system
|
||
sovereignSecretName: sovereign-smtp-credentials
|
||
# Defaults match `.Values.sovereign.smtp.*` (the catalyst-api PIN
|
||
# delivery path) so the SME auth service uses the same mothership
|
||
# relay coordinates as the catalyst Console PIN flow until the
|
||
# Sovereign-local Stalwart relay (slot 95 bp-stalwart-sovereign)
|
||
# lands. The SMTP source-Secret (catalyst-system/sovereign-smtp-
|
||
# credentials) is layered on top via source-wins precedence in
|
||
# sme-secrets.yaml — when A5's provisioner (#883/#905) seeds the
|
||
# canonical key shape (smtp-host/port/from), those bytes win over
|
||
# these fallbacks. Until A5 ships full host/port/from coverage
|
||
# the chart-level fallback keeps gate 2 (PIN delivery) working.
|
||
# Issue #934 follow-up.
|
||
host: "mail.openova.io"
|
||
port: "587"
|
||
from: "noreply@openova.io"
|
||
user: "noreply@openova.io" # SMTP submission username (often == from)
|
||
# SMTP_PASS is sensitive — never inline it. Reference an existing
|
||
# Secret in the `sme` namespace (the per-Sovereign overlay typically
|
||
# creates this from cloud-init or via OpenBao + ExternalSecret).
|
||
# Empty `name` skips the lookup and renders SMTP_PASS as empty.
|
||
passwordSecretRef:
|
||
name: "" # default: "" — no SMTP auth
|
||
key: "password"
|
||
admin:
|
||
# Bootstrap admin email rendered into Secret as ADMIN_EMAIL. The
|
||
# paired ADMIN_PASSWORD is auto-generated via lookup-persisted
|
||
# randAlphaNum (32 chars) on first install — never settable from
|
||
# values per Inviolable Principle #10.
|
||
email: "admin@openova.io"
|
||
|
||
# ─── SME service backing-store endpoints (issue #861) ─────────────────────
|
||
# When ingress.marketplace.enabled=true the chart renders:
|
||
# - templates/sme-services/ferretdb.yaml — FerretDB Deployment + Service
|
||
# in `sme` ns, MongoDB-wire-compatible front end backed by sme-pg.
|
||
# - templates/sme-services/valkey-cross-ns-policy.yaml —
|
||
# CiliumNetworkPolicy in `valkey` ns allowing ingress from `sme` ns.
|
||
# - templates/sme-services/configmap.yaml — MONGODB_URI + VALKEY_ADDR
|
||
# populated from the values below.
|
||
#
|
||
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every URL,
|
||
# image ref, and resource value is operator-overridable. Defaults match
|
||
# the known-working contabo-mkt shape (FerretDB v1.24 against vanilla
|
||
# CNPG postgres:16; valkey-primary as the bp-valkey 1.0.0 read/write
|
||
# Service name).
|
||
smeServices:
|
||
# ─── Event bus (issue #942) ────────────────────────────────────────────
|
||
# Per ADR-0001 the OpenOva architecture uses NATS JetStream as the only
|
||
# local bus on Sovereigns. On Catalyst-Zero (contabo) the legacy SME
|
||
# services still target a Redpanda Service in the talentmesh namespace
|
||
# (migration #68). The configmap.yaml template selects the default at
|
||
# render time based on .Values.global.sovereignFQDN:
|
||
# - non-empty (Sovereign) → nats-jetstream.nats-jetstream.svc:4222
|
||
# - empty (Catalyst-Zero) → redpanda.talentmesh.svc:9092
|
||
# `brokers` overrides the default outright — operator MAY wire any
|
||
# NATS-protocol or Kafka-protocol broker without forking the chart.
|
||
# `protocol` is an explicit hint for SME services that want to switch
|
||
# wire format independently (e.g. a Sovereign with a Kafka-compatible
|
||
# broker outside the cluster).
|
||
eventBus:
|
||
brokers: ""
|
||
protocol: ""
|
||
ferretdb:
|
||
namespace: sme
|
||
# FerretDB v1.24 — works against vanilla CNPG postgres:16. v2.x
|
||
# requires PostgreSQL with the DocumentDB extension which the
|
||
# sme-pg cluster does not ship; bumping is a separate change that
|
||
# also needs a custom CNPG image. See Chart.yaml 1.4.4 changelog.
|
||
image: ghcr.io/ferretdb/ferretdb
|
||
tag: "1.24"
|
||
imagePullPolicy: IfNotPresent
|
||
replicas: 1
|
||
# Postgres connection target — sme-pg-rw read/write Service in
|
||
# `sme` ns, sme_documents DB created by sme-pg's
|
||
# postInitApplicationSQL block (see smePostgres.cluster.
|
||
# additionalDatabases above).
|
||
postgresPort: 5432
|
||
postgresDatabase: sme_documents
|
||
sslmode: disable # ClusterIP traffic is overlay-encrypted; CNPG default issuer chain not bundled
|
||
# Service FQDN exposed to other SME services via configmap MONGODB_URI.
|
||
# Per-Sovereign overlays MAY swap to an external MongoDB endpoint.
|
||
host: ferretdb.sme.svc.cluster.local
|
||
port: 27017
|
||
resources:
|
||
requests:
|
||
cpu: 25m
|
||
memory: 64Mi
|
||
limits:
|
||
cpu: 500m
|
||
memory: 256Mi
|
||
valkey:
|
||
# bp-valkey 1.0.0 (slot 17) deploys to namespace `valkey` with
|
||
# bitnami valkey 5.5.1 + architecture: replication. Service names:
|
||
# - valkey-primary.valkey.svc.cluster.local (read/write)
|
||
# - valkey-replicas.valkey.svc.cluster.local (read-only)
|
||
# - valkey-headless.valkey.svc.cluster.local (StatefulSet headless)
|
||
# SME services pin to the primary by default so writes succeed; per-
|
||
# Sovereign overlays MAY split read traffic to -replicas via a
|
||
# second VALKEY_READ_ADDR (separate ticket).
|
||
host: valkey-primary.valkey.svc.cluster.local
|
||
port: 6379
|
||
namespace: valkey
|
||
# ─── Cross-ns auth Secret mirror (issue #863) ──────────────────────
|
||
# bp-valkey 1.0.0 ships auth.enabled=true; bitnami auto-generates a
|
||
# random password and exposes it via the `valkey` Secret in the
|
||
# `valkey` namespace. The catalyst chart renders templates/
|
||
# sme-services/valkey-cross-ns-secret.yaml which uses Helm `lookup`
|
||
# to read that password and re-emit it as `sme-valkey-auth` in
|
||
# `sme` ns — auth.yaml + gateway.yaml then wire VALKEY_PASSWORD via
|
||
# secretKeyRef. Each knob below is operator-overridable in case a
|
||
# Sovereign uses a forked bp-valkey with a different Secret name
|
||
# or key.
|
||
sourceSecretName: valkey
|
||
sourcePasswordKey: valkey-password
|
||
destNamespace: sme
|
||
destSecretName: sme-valkey-auth
|
||
crossNsPolicy:
|
||
# Render templates/sme-services/valkey-cross-ns-policy.yaml — a
|
||
# CiliumNetworkPolicy in the `valkey` namespace allowing ingress
|
||
# from the `sme` namespace on Valkey's port. Default true since
|
||
# the cross-ns wire is the canonical Sovereign topology. Disable
|
||
# via per-Sovereign overlay only when bp-valkey is repackaged
|
||
# into the `sme` namespace (rare).
|
||
enabled: true
|
||
sourceNamespace: sme
|
||
# ─── provisioning service GitHub token (issue #866) ──────────────────
|
||
# The SME `provisioning` service Deployment references
|
||
# `secret/provisioning-github-token` with key `GITHUB_TOKEN`. On
|
||
# contabo-mkt this is pre-provisioned via SealedSecret. On a freshly
|
||
# franchised Sovereign, templates/sme-services/provisioning-github-
|
||
# token.yaml mirrors the gitea-admin password (already generated by
|
||
# platform/gitea/chart/templates/admin-secret.yaml with the same
|
||
# lookup-persistence pattern) into `sme` ns under the canonical
|
||
# GITHUB_TOKEN key the provisioning service reads. This unblocks the
|
||
# provisioning Pod reaching Running 1/1 on a fresh Sovereign — the
|
||
# last 1/13 SME pod that #859 + #861 + #863 didn't already cover.
|
||
#
|
||
# Per Inviolable Principle #4 (never hardcode), every source/dest
|
||
# name + key is operator-overridable so a Sovereign that points
|
||
# provisioning at a non-Gitea Git host (e.g. a per-Sovereign
|
||
# GitHub PAT delivered via OpenBao + ExternalSecret) can wire the
|
||
# source-side ref without forking the chart.
|
||
provisioning:
|
||
gitToken:
|
||
# Source: bp-gitea's auto-generated admin Secret. Slot 10
|
||
# reaches Ready before slot 13 (Flux dependsOn in
|
||
# clusters/_template/bootstrap-kit/13-bp-catalyst-platform.yaml),
|
||
# so the lookup has data by the time this template renders.
|
||
sourceNamespace: gitea
|
||
sourceSecretName: gitea-admin-secret
|
||
sourcePasswordKey: password
|
||
# Destination: the Secret + key shape that the provisioning
|
||
# Deployment's secretKeyRef in
|
||
# templates/sme-services/provisioning.yaml reads.
|
||
destNamespace: sme
|
||
destSecretName: provisioning-github-token
|
||
destKey: GITHUB_TOKEN
|
||
# ─── Provisioning service GitOps env (issues #940 + #944) ──────────
|
||
# The SME provisioning service Deployment env block is rendered from
|
||
# these keys. Every value is operator-overridable per Inviolable
|
||
# Principle #4. Defaults are topology-aware:
|
||
# - Sovereign install (global.sovereignFQDN non-empty) defaults
|
||
# gitBasePath to clusters/<sovereignFQDN>/sme-tenants and points
|
||
# git.{apiURL,owner} at the local Gitea bp-gitea installs.
|
||
# - Catalyst-Zero install (global.sovereignFQDN empty) keeps the
|
||
# legacy contabo-mkt write target.
|
||
#
|
||
# gitBasePath: filesystem prefix under the cloned repo root. When
|
||
# non-empty, takes precedence over the topology default. The
|
||
# provisioning binary's startup guard (validateGitBasePath in
|
||
# core/services/provisioning/main.go) rejects values that don't
|
||
# start with `clusters/<SOVEREIGN_FQDN>/` on Sovereigns — the
|
||
# cross-cluster pollution defence (#944 critical).
|
||
gitBasePath: ""
|
||
# githubToken: Secret name + key the Deployment reads GITHUB_TOKEN
|
||
# from. Defaults match the chart-emitted
|
||
# templates/sme-services/provisioning-github-token.yaml output
|
||
# (issue #866). Operator may swap to a per-Sovereign ExternalSecret
|
||
# by setting both fields here.
|
||
githubToken:
|
||
secretName: provisioning-github-token
|
||
secretKey: GITHUB_TOKEN
|
||
# git.{apiURL,owner,repo,branch}: Git host coordinates. The
|
||
# provisioning binary uses GITHUB_API_URL when non-empty (Sovereign
|
||
# path → in-cluster Gitea REST API) and otherwise falls back to the
|
||
# canonical https://api.github.com (contabo path). All four values
|
||
# are operator-overridable.
|
||
git:
|
||
apiURL: ""
|
||
owner: ""
|
||
repo: openova
|
||
branch: main
|
||
|
||
# ─── Catalog (qa-loop iter-16 Fix #65) ─────────────────────────────────
|
||
# `openova-catalog` Flux HelmRepository — the named source ref every
|
||
# Application's rendered HelmRelease points at by default.
|
||
#
|
||
# The application-controller (core/controllers/application/) renders
|
||
# per-region HelmReleases with `sourceRef.name` = `controllers.application.
|
||
# catalogSourceRef` (env: CATALOG_SOURCE_REF, default `openova-catalog`)
|
||
# in `controllers.application.sourceNamespace` (env: SOURCE_NAMESPACE,
|
||
# default `flux-system`). Without a HelmRepository CR at that
|
||
# namespace/name pair, Flux's helm-controller cannot resolve the chart
|
||
# bytes and the workload Pod is never scheduled — the qa-wp Application
|
||
# CR sits at status.phase=Pending forever, blocking ~30 qa-loop TCs.
|
||
#
|
||
# This block ships the missing CR. Per Inviolable Principle #4 every
|
||
# field is operator-overridable via per-Sovereign overlays (e.g. a
|
||
# Sovereign with a local Harbor proxy_cache flips `url:` to its own
|
||
# `oci://harbor.<sovereign-fqdn>` mirror without forking the chart).
|
||
catalog:
|
||
helmRepository:
|
||
enabled: true
|
||
# MUST equal controllers.application.catalogSourceRef (default
|
||
# "openova-catalog"). Operators that re-target the controller's
|
||
# source ref (e.g. "openova-catalog-mirror") must also bump this so
|
||
# the HelmRepository name and the controller's render output stay
|
||
# in lockstep.
|
||
name: openova-catalog
|
||
# MUST equal controllers.application.sourceNamespace (default
|
||
# "flux-system"). Same lockstep rule as `name`.
|
||
namespace: flux-system
|
||
# `oci` — matches blueprint-release.yaml publish path (`helm push
|
||
# <chart>.tgz oci://ghcr.io/openova-io`). Default `http` would 404
|
||
# on a chart pull.
|
||
type: oci
|
||
# Canonical OpenOva blueprint registry. Per-Sovereign overlays may
|
||
# override to a local Harbor mirror (cutover.go re-targets every
|
||
# bp-* HelmRepository to `oci://harbor.<sovereign-fqdn>`; this CR
|
||
# follows the same convention).
|
||
url: oci://ghcr.io/openova-io
|
||
# `ghcr-pull` Secret is bootstrapped in flux-system by every
|
||
# Sovereign's bootstrap-kit (clusters/_template/bootstrap-kit/
|
||
# 03-flux.yaml et al). Empty disables auth (public packages only).
|
||
secretRef: ghcr-pull
|
||
# 15m matches sibling bootstrap-kit HelmRepositories
|
||
# (kyverno/grafana/trivy/cert-manager-powerdns-webhook). Tighter
|
||
# wastes GHCR API quota; looser delays new chart-version
|
||
# propagation post blueprint-release.yaml.
|
||
interval: 15m
|
||
|
||
# qaFixtures — qa-loop iter-6 Cluster-F seeder for the test-matrix
|
||
# fixtures (qa-omantel namespace, disposable-cm, qa-wp-creds, qa-user1
|
||
# UserAccess + RoleBinding, bp-qa-custom Blueprint). DEFAULT-OFF;
|
||
# enable only on test Sovereigns. Production Sovereigns must keep
|
||
# `enabled: false` so test resources never leak into customer clusters.
|
||
# See templates/qa-fixtures/_README.txt for the full rationale.
|
||
qaFixtures:
|
||
enabled: false
|
||
# ── Tier-scoped test-session minting (qa-loop iter-11 Cluster-A) ─
|
||
# `testSessionEnabled` switches on POST /api/v1/auth/test-session
|
||
# in catalyst-api. This endpoint mints a session JWT for a synthetic
|
||
# `qa-test-{tier}@openova.io` user with the requested tier so the
|
||
# 5-agent QA executor can assert tier-boundary 403/200 contracts on
|
||
# privileged endpoints without going through PIN-via-IMAP (which
|
||
# always lands tier=owner). Default is `false` (production-safe);
|
||
# enabled on QA/chroot Sovereigns only. The endpoint returns 404 to
|
||
# the public when this is false — wire-indistinguishable from a
|
||
# missing route, so customer Sovereigns expose nothing about the
|
||
# existence of QA hooks in the catalyst-api binary.
|
||
# See products/catalyst/bootstrap/api/internal/handler/auth_test_session.go
|
||
# and templates/qa-fixtures/useraccess-qa-test-{tier}.yaml.
|
||
testSessionEnabled: false
|
||
namespace: qa-omantel
|
||
appName: qa-wp
|
||
# `sovereignRef` MUST be a FQDN per Organization CRD validation
|
||
# (pattern '^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]...)+$').
|
||
# The UserAccess CRD's stricter single-segment pattern is satisfied by
|
||
# `regexReplaceAll "\..*$" ""` in templates/qa-fixtures/useraccess-qa-user1.yaml
|
||
# (PR #1246) which strips the TLD/SLD and renders "omantel" for the
|
||
# UserAccess CR. Default chosen to match the qa-omantel test Sovereign's
|
||
# actual hostname (PR #1245 — legacy short-form "omantel" rejected by
|
||
# the Organization CRD at admission and blocked the qa-wp roll).
|
||
sovereignRef: omantel.biz
|
||
# `sovereignFQDN` — explicit FQDN override consumed by the qa-fixtures
|
||
# Organization template's resolution chain (qaFixtures.sovereignFQDN
|
||
# → global.sovereignFQDN → qaFixtures.sovereignRef-if-FQDN →
|
||
# "omantel.biz"). Empty default lets the chain fall through to
|
||
# global.sovereignFQDN (set on every Sovereign install via the
|
||
# bootstrap-kit envsubst SOVEREIGN_FQDN). Per-Sovereign overlay may
|
||
# override via QA_FIXTURES_SOVEREIGN_FQDN on the bootstrap-kit
|
||
# Kustomization.
|
||
sovereignFQDN: ""
|
||
organization: omantel-platform
|
||
# Environment region — split into the 3 CRD-required subfields.
|
||
# The Environment CRD validates `regions[].region` against
|
||
# `^[a-z]{3}[a-z0-9]?$` (3-4 char region code) and refuses the
|
||
# full 4-segment `hz-fsn-rtz-prod` label as a single string.
|
||
# Defaults below reflect the canonical hz-fsn-rtz-prod target so
|
||
# an Environment renders without per-Sovereign overrides.
|
||
envRegionProvider: hetzner
|
||
envRegionCode: fsn
|
||
envRegionBuildingBlock: rtz
|
||
qaUser:
|
||
email: qa-user1@openova.io
|
||
name: qa-user1
|
||
keycloakSubject: qa-user1
|
||
# qaWpPassword: optional explicit value for qa-wp-creds Secret.
|
||
# When empty the template derives a deterministic placeholder from
|
||
# the release name + namespace so the chart never bakes a hard-coded
|
||
# credential into the manifest stream. The matrix only checks for
|
||
# the Secret's existence, not the password value.
|
||
qaWpPassword: ""
|
||
# ── Continuum DR fixture knobs (Fix #32, Fix #37) ────────────────
|
||
# CNPGPair name + region pair the matrix asserts on. The default pair
|
||
# (hz-fsn-rtz-prod ↔ hz-hel-rtz-prod) reflects the omantel test
|
||
# Sovereign's ClusterMesh peering. Override on a per-Sovereign basis.
|
||
#
|
||
# Region values MUST match the canonical 4-segment region label
|
||
# `^[a-z]+-[a-z]+-[a-z]+-[a-z]+$` enforced by Application + Continuum
|
||
# CRD validation (Fix #38 follow-up — Fix #36's qa-wp Application
|
||
# rejected at admission with `spec.regions[0]: Invalid value: "fsn1"`
|
||
# which blocked the chart upgrade and pinned omantel on the prior
|
||
# image SHA, preventing TC-141/TC-090/TC-383 from rolling).
|
||
continuumName: cont-omantel
|
||
# Default name embeds the literal "cnpgpair" substring so the matrix's
|
||
# `kubectl get cnpgpair -n qa-omantel` stdout (TC-306 must_contain
|
||
# ["cnpgpair", "fsn1", "hz-hel-rtz-prod"]) round-trips against the
|
||
# rendered NAME column. Pre-Fix #40 the default `qa-cnpg` produced a
|
||
# NAME column missing the "pair" substring, making TC-306 unsatisfiable
|
||
# on the executor's stdout-token assertion.
|
||
cnpgPairName: qa-cnpgpair
|
||
# qa-loop iter-1 prefetch Fix #102 (Continuum DR controllers): alias
|
||
# CR `qa-cnpg` ships alongside the canonical `qa-cnpgpair` so
|
||
# TC-310/311/314's hardcoded
|
||
# `kubectl get cnpgpair qa-cnpg -n qa-omantel -o jsonpath='...'`
|
||
# resolves. Both names refer to the same logical pair (same
|
||
# primaryCluster / replicaCluster / regions). Set to "" to suppress
|
||
# the alias on Sovereigns that need only one of the two names.
|
||
cnpgPairAliasName: qa-cnpg
|
||
# qa-loop iter-1 prefetch Fix #102: post-switchover primary region
|
||
# used as the seeded `status.currentPrimary` value on the cnpgpair
|
||
# CR. Defaults to the replica region (the post-switchover state) so
|
||
# TC-314's `must_contain ['hz-hel-rtz-prod']` resolves on a fresh
|
||
# Sovereign that has executed the qa-loop matrix's switchover step.
|
||
# Override to `cnpgPairPrimaryRegion` for a pre-switchover baseline.
|
||
cnpgPairPostSwitchoverPrimary: hz-hel-rtz-prod
|
||
# qa-loop iter-1 prefetch Fix #102: platform-level Continuum CR
|
||
# mirror namespace. The per-Application Continuum lives in
|
||
# qaFixtures.namespace; the platform-aggregate CR is mirrored here
|
||
# so TC-305's `kubectl get continuum cont-omantel -n catalyst-system`
|
||
# resolves. Set to "" to suppress the mirror.
|
||
continuumPlatformNamespace: catalyst-system
|
||
# Short-form Hetzner region labels for the CNPGPair CR — distinct from
|
||
# the canonical 4-segment qaFixtures.primaryRegion / standbyRegion so
|
||
# the cnpgpair CR matches the cnpg-pair-controller's CCM zone-affinity
|
||
# convention (`fsn1` / `hel1`) while the Application + Environment +
|
||
# Continuum CRs continue to use the canonical 4-segment label
|
||
# `hz-fsn-rtz-prod` / `hz-hel-rtz-prod` per their CRD validation
|
||
# patterns. The two seams stay in lockstep via the node-labels-seeder
|
||
# Job that patches every node with topology.kubernetes.io/region=<short>
|
||
# derived from openova.io/region=<canonical> (Fix #40 Cluster-B).
|
||
cnpgPairPrimaryRegion: fsn1
|
||
cnpgPairReplicaRegion: hz-hel-rtz-prod
|
||
primaryRegion: hz-fsn-rtz-prod
|
||
standbyRegion: hz-hel-rtz-prod
|
||
# ── Configured-but-not-active regions for the QA Sovereign UI ───
|
||
# qa-loop iter-16 Fix #88 (Path B). When qaFixtures is enabled the
|
||
# sovereign-fqdn ConfigMap's configuredRegions key falls back to
|
||
# this list (sovereign.configuredRegions takes precedence when
|
||
# explicitly set). The default mirrors the cnpgPair regions so the
|
||
# dashboard SovereignCard renders fsn1 + hz-hel-rtz-prod chips and
|
||
# the matrix's TC-296/TC-297/TC-300/TC-301 multi-region tokens
|
||
# resolve on a single-region QA cluster without provisioning a real
|
||
# second cluster (multi-cluster ClusterMesh = Path A follow-up).
|
||
configuredRegions:
|
||
- fsn1
|
||
- hz-hel-rtz-prod
|
||
# ── Configured-but-not-policy-reported applications for QA Sovereign ─
|
||
# qa-loop iter-16 Fix #167. When qaFixtures is enabled the
|
||
# sovereign-fqdn ConfigMap's qaApplications key falls back to this
|
||
# list (sovereign.qaApplications takes precedence when explicitly
|
||
# set). Wired into catalyst-api as `CATALYST_QA_APPLICATIONS` so the
|
||
# /compliance/scorecard `appRefs[]` envelope carries the matrix
|
||
# tokens (TC-029: `qa-wordpress`) on every call even before the
|
||
# compliance aggregator has ingested a PolicyReport for the
|
||
# workload. Mirrors configuredRegions' fallback pattern.
|
||
applications:
|
||
- qa-wordpress
|
||
- qa-wp
|
||
pdmZone: openova.io
|
||
publicHost: openova.io
|
||
# ── CNPG Cluster CR fixture knobs (Fix #37) ──────────────────────
|
||
# `cluster-primary` + `cluster-replica` postgresql.cnpg.io Cluster
|
||
# CRs the cnpgpair `qa-cnpg` references. Single-region scheduling by
|
||
# default — the cross-region drill is owned by the cnpg-pair-
|
||
# controller (Phase-2) and Continuum DR endpoints. Override the
|
||
# region knobs on a multi-region Sovereign once kube-proxy
|
||
# replacement + Hetzner cross-region NodePort filtering are resolved.
|
||
cnpgPrimaryClusterName: cluster-primary
|
||
cnpgReplicaClusterName: cluster-replica
|
||
cnpgPrimaryRegion: hz-fsn-rtz-prod
|
||
cnpgReplicaRegion: hz-fsn-rtz-prod
|
||
cnpgInstances: 1
|
||
cnpgImage: ghcr.io/cloudnative-pg/postgresql:16.4-1
|
||
cnpgStorageSize: 1Gi
|
||
cnpgStorageClass: local-path
|
||
cnpgDatabase: app
|
||
# qa-loop iter-1 prefetch Fix #110: terminal phase string seeded onto
|
||
# cluster-primary + cluster-replica `status.phase`. The CNPG operator
|
||
# writes this exact literal once Pods land Running and replication is
|
||
# streaming; seeding it removes matrix flake on bandwidth-constrained
|
||
# Sovereigns where image pulls dominate the wallclock. Closes TC-307
|
||
# + TC-348 (kubectl get cluster.postgresql.cnpg.io ... must contain
|
||
# 'Cluster in healthy state'). Override on Sovereigns running a forked
|
||
# CNPG operator that uses a different terminal phase string.
|
||
cnpgTargetPhase: "Cluster in healthy state"
|
||
# ── CNPG backup config (Fix #41, qa-loop iter-8 Cluster-A) ───────
|
||
# cluster-primary writes WAL + base backups to in-cluster SeaweedFS
|
||
# via the S3-compatible endpoint. The cnpg-backup-s3-seeder Job in
|
||
# cnpg-clusters-qa.yaml copies the seaweedfs admin keys into the
|
||
# qa-omantel namespace so cluster-primary's spec.backup resolves.
|
||
# Override these for off-cluster S3 (R2 / B2 / native AWS).
|
||
cnpgBackupBucket: qa-fixtures
|
||
cnpgBackupEndpointURL: http://seaweedfs-s3.seaweedfs.svc.cluster.local:8333
|
||
cnpgBackupS3SecretName: qa-cnpg-backup-s3
|
||
cnpgBackupSourceSecretNamespace: seaweedfs
|
||
cnpgBackupSourceSecretName: seaweedfs-s3-secret
|
||
# qa-loop iter-1 Fix #138 (chart 1.4.138): qa-cnpg-backup-s3-seed Job
|
||
# is no longer a post-install hook (was wedging bp-catalyst-platform
|
||
# install at 15m on fresh Sovereign — circular dep, see Chart.yaml
|
||
# changelog top entry). Job runs concurrently with bp-seaweedfs install
|
||
# in bootstrap-kit slot 18 and waits up to 30 min (900×2s) for the
|
||
# source seaweedfs-s3-secret to materialise. Override on bandwidth-
|
||
# constrained Sovereigns where bp-seaweedfs install takes longer.
|
||
s3SeedWaitIterations: 900
|
||
# ── Kyverno baseline policies (Fix #37) ──────────────────────────
|
||
# disallow-privileged-containers ships in Enforce mode by default
|
||
# (target-state hard block); other 18 baseline policies ship in
|
||
# Audit mode so the matrix sees ClusterPolicyReports without
|
||
# blocking platform pods. Override to "Audit" to soft-launch the
|
||
# Enforce policy on a fresh Sovereign while migrating workloads.
|
||
kyvernoEnforceMode: Enforce
|
||
|
||
# ── Cilium NetworkPolicy baseline (qa-loop iter-11 Fix #48) ──────
|
||
# Default-deny CCNP + 11 per-namespace allow templates ship as part
|
||
# of the qa-fixtures bundle. Per `feedback_no_mvp_no_workarounds.md`
|
||
# rule #1 (target-state) the matrix asserts on
|
||
# - TC-278 default-deny CCNP exists with Ingress + Egress denied
|
||
# - TC-279 per-namespace CiliumNetworkPolicy templates rendered
|
||
# - TC-294 ≥10 CNPs total across all namespaces
|
||
# Disable on a per-Sovereign basis if the operator wants to author
|
||
# their own policy bundle.
|
||
networkPolicies:
|
||
enabled: true
|