Compare commits

..

No commits in common. "chore/310-bootstrap-trim-prep" and "fix/provision-as-spa-route" have entirely different histories.

680 changed files with 2689 additions and 80564 deletions

View File

@ -13,35 +13,6 @@
# original 11 plus bp-powerdns added at #167) plus every other Catalyst-
# curated wrapper chart. Application-Blueprint charts (cnpg, valkey, etc.)
# follow the same shape once authored.
#
# ─────────────────────────────────────────────────────────────────────
# Hollow-chart guard (issue #181)
# ─────────────────────────────────────────────────────────────────────
# Every umbrella Blueprint chart MUST declare its upstream chart(s) under
# `dependencies:` in Chart.yaml so `helm dependency build` pulls the
# upstream payload into the OCI artifact at publish time. Earlier in this
# cycle, bp-cert-manager:1.0.0 shipped as a "hollow chart" — only an
# overlay (ClusterIssuer template) with no upstream cert-manager subchart
# bytes — and Phase 1 broke on every Sovereign because cert-manager
# itself was never installed. See docs/BLUEPRINT-AUTHORING.md
# §"Umbrella shape".
#
# This workflow now structurally verifies the upstream payload is present
# at every stage of the publish pipeline:
#
# 1. After `helm dependency build` — assert the working-tree
# chart/charts/<dep>-<ver>.tgz (or unpacked chart/charts/<dep>/) exists
# for every entry in Chart.yaml `dependencies:`.
# 2. After `helm package` — `tar -tzf` the produced .tgz and assert
# every declared dep is inside the package's charts/ directory.
# 3. After `helm push` — `helm pull` the artifact back from GHCR and
# re-verify the deps survived the round-trip.
# 4. `helm template` smoke render — chart MUST render with default
# values; rendered manifests are uploaded as a workflow artifact for
# forensics.
#
# Any one of those steps failing fails the whole build — hollow charts
# can never reach a Sovereign.
name: Blueprint Release
@ -129,16 +100,6 @@ jobs:
- name: Install Syft
uses: anchore/sbom-action/download-syft@v0
- name: Install yq (declared-deps parser)
run: |
# We need a streaming YAML parser to read Chart.yaml's dependencies:
# block reliably; awk/grep on YAML is fragile and would let a
# subtly malformed Chart.yaml slip past the hollow-chart guard.
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
yq --version
- name: Login to GHCR
uses: docker/login-action@v3
with:
@ -180,207 +141,11 @@ jobs:
if: steps.chart.outputs.skip != 'true'
run: helm dependency build "${{ matrix.path }}/chart"
# ──────────────────────────────────────────────────────────────
# GUARD 1 — hollow-chart check on the working tree
# ──────────────────────────────────────────────────────────────
# After `helm dependency build`, every entry in Chart.yaml's
# `dependencies:` block MUST have a corresponding artifact under
# chart/charts/. Helm accepts either form:
# chart/charts/<dep-name>-<dep-version>.tgz (packed)
# chart/charts/<dep-name>/Chart.yaml (unpacked vendored)
# Either is fine; ZERO of them is a hollow chart and we fail loudly.
- name: "Verify upstream subcharts present in working tree (post-dependency-build)"
if: steps.chart.outputs.skip != 'true'
id: verify_workdir
run: |
set -euo pipefail
chart_dir="${{ matrix.path }}/chart"
chart_yaml="$chart_dir/Chart.yaml"
# Some Blueprints legitimately ship NO upstream subchart — their
# entire payload is Catalyst-authored CRs (e.g.
# bp-crossplane-claims, which carries only XRDs + Compositions
# for the compose.openova.io/v1alpha1 family). Those charts opt
# out of the dependencies-required rule by setting the
# annotation:
# annotations:
# catalyst.openova.io/no-upstream: "true"
# in their Chart.yaml. The hollow-chart rule remains in force
# for every other umbrella chart — the original failure
# (bp-cert-manager:1.0.0 silently shipping without cert-manager)
# cannot recur unless an author explicitly adds the annotation.
no_upstream=$(yq '.annotations["catalyst.openova.io/no-upstream"] // ""' "$chart_yaml")
dep_count=$(yq '.dependencies | length // 0' "$chart_yaml")
echo "Declared dependencies: $dep_count"
echo "no-upstream annotation: '$no_upstream'"
if [ "$dep_count" -eq 0 ]; then
if [ "$no_upstream" = "true" ]; then
echo "Chart marked catalyst.openova.io/no-upstream=true — skipping upstream-subchart presence check."
exit 0
fi
echo "::error title=Hollow chart::Chart $chart_yaml declares NO dependencies. Every Blueprint umbrella chart at platform/<name>/chart/ MUST declare its upstream chart under \`dependencies:\` per docs/BLUEPRINT-AUTHORING.md §11.1 Umbrella shape. See issue #181. (To opt out for charts that legitimately ship only Catalyst-authored CRs, set annotations.catalyst.openova.io/no-upstream: \"true\".)"
exit 1
fi
missing=0
for i in $(seq 0 $((dep_count - 1))); do
dep_name=$(yq ".dependencies[$i].name" "$chart_yaml")
dep_ver=$(yq ".dependencies[$i].version" "$chart_yaml")
dep_repo=$(yq ".dependencies[$i].repository" "$chart_yaml")
packed="$chart_dir/charts/${dep_name}-${dep_ver}.tgz"
unpacked="$chart_dir/charts/${dep_name}/Chart.yaml"
if [ -f "$packed" ]; then
echo " ✓ ${dep_name} ${dep_ver} → $packed"
elif [ -f "$unpacked" ]; then
echo " ✓ ${dep_name} ${dep_ver} → $unpacked (unpacked)"
else
echo "::error title=Subchart not pulled::Declared dependency '${dep_name}' (version='${dep_ver}', repo='${dep_repo}') was not pulled by \`helm dependency build\`. Expected $packed OR $unpacked. Investigate the dependency repository / Chart.lock; do NOT publish a hollow chart."
missing=$((missing + 1))
fi
done
if [ "$missing" -gt 0 ]; then
exit 1
fi
echo "All $dep_count declared upstream subcharts present in working tree."
- name: Helm package
if: steps.chart.outputs.skip != 'true'
run: |
mkdir -p /tmp/charts
helm package "${{ matrix.path }}/chart" --destination /tmp/charts
# ──────────────────────────────────────────────────────────────
# GUARD 2 — hollow-chart check inside the packaged tgz
# ──────────────────────────────────────────────────────────────
# `helm package` builds the OCI payload from the working tree, but
# we don't trust transitive bugs / .helmignore mishaps to leave
# chart/charts/ behind. Crack the produced .tgz and assert every
# declared dep is inside <chart_name>/charts/ in the package.
- name: "Verify upstream subcharts present in packaged tgz (post-helm-package)"
if: steps.chart.outputs.skip != 'true'
id: verify_package
run: |
set -euo pipefail
chart_yaml="${{ matrix.path }}/chart/Chart.yaml"
name="${{ steps.chart.outputs.name }}"
version="${{ steps.chart.outputs.version }}"
tgz="/tmp/charts/${name}-${version}.tgz"
if [ ! -f "$tgz" ]; then
echo "::error title=Package missing::Expected $tgz from helm package."
exit 1
fi
echo "Inspecting $tgz"
listing="/tmp/${name}-${version}.listing.txt"
tar -tzf "$tgz" > "$listing"
echo " total entries: $(wc -l < "$listing")"
dep_count=$(yq '.dependencies | length // 0' "$chart_yaml")
missing=0
for i in $(seq 0 $((dep_count - 1))); do
dep_name=$(yq ".dependencies[$i].name" "$chart_yaml")
dep_ver=$(yq ".dependencies[$i].version" "$chart_yaml")
packed_in_tgz="${name}/charts/${dep_name}-${dep_ver}.tgz"
unpacked_in_tgz="${name}/charts/${dep_name}/Chart.yaml"
if grep -qx "$packed_in_tgz" "$listing"; then
echo " ✓ ${dep_name} ${dep_ver} → $packed_in_tgz"
elif grep -qx "$unpacked_in_tgz" "$listing"; then
echo " ✓ ${dep_name} ${dep_ver} → $unpacked_in_tgz (unpacked)"
else
echo "::error title=Hollow package::Packaged tgz $tgz does NOT contain upstream subchart '${dep_name}' (version='${dep_ver}'). Refusing to publish a hollow chart."
missing=$((missing + 1))
fi
done
if [ "$missing" -gt 0 ]; then
echo "── package listing (first 200 lines) ──"
head -200 "$listing"
exit 1
fi
echo "All $dep_count declared upstream subcharts present inside packaged tgz."
# ──────────────────────────────────────────────────────────────
# SMOKE — `helm template` must render with default values
# ──────────────────────────────────────────────────────────────
# A chart can be structurally complete but render-broken (bad
# template, missing required value, schema violation). Render the
# packaged chart with defaults; on render failure the build dies
# and the rendered output (if any) ships as a workflow artifact
# for forensics.
- name: "Helm template smoke render (default values)"
if: steps.chart.outputs.skip != 'true'
id: smoke
run: |
set -euo pipefail
name="${{ steps.chart.outputs.name }}"
version="${{ steps.chart.outputs.version }}"
tgz="/tmp/charts/${name}-${version}.tgz"
mkdir -p /tmp/render
render_out="/tmp/render/${name}-${version}.default.yaml"
# `helm template` against the packaged tgz exercises the same
# bytes Flux will install at runtime — including subchart
# rendering. A leaf umbrella with the upstream subchart
# present should produce upstream resources here.
if ! helm template "smoke-${name}" "$tgz" \
--namespace "smoke-${name}" \
> "$render_out" 2> /tmp/render/stderr.log; then
echo "::error title=helm template failed::Default-values render of $tgz failed; see uploaded artifact and stderr below."
cat /tmp/render/stderr.log >&2
exit 1
fi
lines=$(wc -l < "$render_out")
echo "Rendered $lines lines to $render_out"
if [ "$lines" -lt 5 ]; then
echo "::error title=Empty render::Rendered output is suspiciously short ($lines lines). A working umbrella with an upstream subchart should produce many more resources."
exit 1
fi
- name: "Upload smoke render as workflow artifact"
if: ${{ always() && steps.chart.outputs.skip != 'true' && steps.smoke.conclusion != 'skipped' }}
uses: actions/upload-artifact@v4
with:
name: blueprint-smoke-render-${{ steps.chart.outputs.name }}-${{ steps.chart.outputs.version }}
path: /tmp/render/
if-no-files-found: warn
retention-days: 30
# ──────────────────────────────────────────────────────────────
# GATE — Per-chart integration tests under chart/tests/*.sh
# ──────────────────────────────────────────────────────────────
# Every chart under platform/<name>/chart/tests/ that ships a *.sh
# script gets executed here against the working-tree chart. Tests
# SHOULD be self-contained `helm template` assertions (no kind
# cluster); kind-based tests live in a separate workflow that we
# don't gate publish on.
#
# Canonical example: tests/observability-toggle.sh — verifies the
# docs/BLUEPRINT-AUTHORING.md §11.2 rule (observability toggles
# default false). A chart authoring regression that re-introduces
# a hardcoded `serviceMonitor.enabled: true` fails this gate and
# the publish job dies BEFORE the OCI artifact is pushed (issue
# #182).
- name: "Run chart integration tests (chart/tests/*.sh)"
if: steps.chart.outputs.skip != 'true'
id: chart_tests
run: |
set -euo pipefail
chart_dir="${{ matrix.path }}/chart"
tests_dir="$chart_dir/tests"
if [ ! -d "$tests_dir" ]; then
echo "No tests/ directory at $tests_dir — skipping chart tests."
exit 0
fi
shopt -s nullglob
scripts=( "$tests_dir"/*.sh )
if [ "${#scripts[@]}" -eq 0 ]; then
echo "tests/ directory present at $tests_dir but no *.sh scripts — skipping."
exit 0
fi
echo "Running ${#scripts[@]} chart-test script(s) under $tests_dir:"
for s in "${scripts[@]}"; do
echo " → $s"
done
for s in "${scripts[@]}"; do
echo "── $(basename "$s") ──"
bash "$s" "$chart_dir"
done
echo "All chart-test scripts passed."
- name: Helm push to GHCR
if: steps.chart.outputs.skip != 'true'
id: push
@ -395,61 +160,6 @@ jobs:
echo "ref=ghcr.io/openova-io/${name}:${version}" >> "$GITHUB_OUTPUT"
echo "digest=$digest" >> "$GITHUB_OUTPUT"
# ──────────────────────────────────────────────────────────────
# GUARD 3 — hollow-chart check after round-trip through GHCR
# ──────────────────────────────────────────────────────────────
# Pull the just-pushed artifact back from GHCR and re-verify deps
# survived. This catches any registry-side stripping, manifest
# rewriting, or path mangling — and proves end consumers (Flux
# helm-controller on every Sovereign) will see the upstream
# subchart bytes.
- name: "Verify upstream subcharts present in pulled OCI artifact (post-helm-push)"
if: steps.chart.outputs.skip != 'true'
id: verify_pull
run: |
set -euo pipefail
name="${{ steps.chart.outputs.name }}"
version="${{ steps.chart.outputs.version }}"
# `chart_yaml` is referenced relative to $GITHUB_WORKSPACE — DO NOT
# cd elsewhere before reading it. We pin `helm pull` output via
# --destination so the read of the source-tree Chart.yaml stays
# rooted at the checked-out repo root.
chart_yaml="${GITHUB_WORKSPACE}/${{ matrix.path }}/chart/Chart.yaml"
mkdir -p /tmp/pulled
helm pull "oci://ghcr.io/openova-io/${name}" --version "${version}" \
--destination /tmp/pulled
pulled_tgz="/tmp/pulled/${name}-${version}.tgz"
if [ ! -f "$pulled_tgz" ]; then
echo "::error title=Pull failed::helm pull did not produce $pulled_tgz; cannot verify GHCR contents."
ls -la /tmp/pulled
exit 1
fi
listing="/tmp/pulled/${name}-${version}.pulled.listing.txt"
tar -tzf "$pulled_tgz" > "$listing"
echo " pulled entries: $(wc -l < "$listing")"
dep_count=$(yq '.dependencies | length // 0' "$chart_yaml")
missing=0
for i in $(seq 0 $((dep_count - 1))); do
dep_name=$(yq ".dependencies[$i].name" "$chart_yaml")
dep_ver=$(yq ".dependencies[$i].version" "$chart_yaml")
packed_in_tgz="${name}/charts/${dep_name}-${dep_ver}.tgz"
unpacked_in_tgz="${name}/charts/${dep_name}/Chart.yaml"
if grep -qx "$packed_in_tgz" "$listing"; then
echo " ✓ ${dep_name} ${dep_ver} → $packed_in_tgz (in GHCR artifact)"
elif grep -qx "$unpacked_in_tgz" "$listing"; then
echo " ✓ ${dep_name} ${dep_ver} → $unpacked_in_tgz (in GHCR artifact, unpacked)"
else
echo "::error title=Hollow OCI artifact::GHCR-pulled $pulled_tgz does NOT contain upstream subchart '${dep_name}' (version='${dep_ver}'). The artifact at ghcr.io/openova-io/${name}:${version} is hollow — Flux will install it but the upstream payload will be missing on every Sovereign. Refusing to leave this published."
missing=$((missing + 1))
fi
done
if [ "$missing" -gt 0 ]; then
echo "── pulled-artifact listing (first 200 lines) ──"
head -200 "$listing"
exit 1
fi
echo "All $dep_count declared upstream subcharts present in pulled GHCR artifact."
- name: Cosign keyless sign
if: steps.chart.outputs.skip != 'true'
env:
@ -490,4 +200,3 @@ jobs:
echo "- **Digest:** \`${{ steps.push.outputs.digest }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **Cosigned:** ✓ (keyless via GitHub OIDC)" >> "$GITHUB_STEP_SUMMARY"
echo "- **SBOM attested:** ✓ (SPDX-JSON)" >> "$GITHUB_STEP_SUMMARY"
echo "- **Subchart guards:** ✓ working tree, ✓ packaged tgz, ✓ pulled OCI artifact, ✓ helm template smoke" >> "$GITHUB_STEP_SUMMARY"

View File

@ -1,135 +0,0 @@
name: Build cert-manager-dynadot-webhook
# cert-manager-dynadot-webhook — Catalyst-built Go binary that satisfies
# cert-manager's DNS-01 webhook contract against the Dynadot api3.json.
# Closes openova#159. The image is consumed by the
# bp-cert-manager-dynadot-webhook Blueprint
# (platform/cert-manager-dynadot-webhook/chart/) which is auto-installed
# by the bootstrap-kit on every Sovereign that needs wildcard TLS.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4a (GitHub Actions is the only
# build path) every image that runs on OpenOva infra MUST be produced
# by a CI workflow from a committed git SHA. This workflow mirrors
# pool-domain-manager-build.yaml — same auth flow, same cosign signing,
# same SBOM attestation.
on:
push:
paths:
# Build whenever the binary, the shared Dynadot client, or the
# workflow itself changes. The chart at platform/.../chart/ does
# not retrigger this workflow — Helm chart releases land via
# blueprint-release.yaml and consume the image tag this workflow
# publishes.
- 'core/cmd/cert-manager-dynadot-webhook/**'
- 'core/pkg/dynadot-client/**'
- '.github/workflows/build-cert-manager-dynadot-webhook.yaml'
branches: [main]
pull_request:
paths:
- 'core/cmd/cert-manager-dynadot-webhook/**'
- 'core/pkg/dynadot-client/**'
- '.github/workflows/build-cert-manager-dynadot-webhook.yaml'
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE: ghcr.io/openova-io/openova/cert-manager-dynadot-webhook
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# id-token write is required by cosign keyless signing (Sigstore).
# Per docs/INVIOLABLE-PRINCIPLES.md #3 every Catalyst image is
# cosign-signed + SBOM-attested.
id-token: write
outputs:
sha_short: ${{ steps.vars.outputs.sha_short }}
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set short SHA
id: vars
run: echo "sha_short=$(echo $GITHUB_SHA | head -c 7)" >> "$GITHUB_OUTPUT"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache-dependency-path: |
core/cmd/cert-manager-dynadot-webhook/go.sum
- name: Run unit tests — webhook
working-directory: core/cmd/cert-manager-dynadot-webhook
run: go test -count=1 -race ./...
- name: Run unit tests — shared dynadot client
working-directory: core/pkg/dynadot-client
run: go test -count=1 -race ./...
# On pull_request runs we stop here — image push requires
# `packages: write` against the openova-io org which only main
# branch authors hold via GITHUB_TOKEN. Skipping the push step is
# the standard PR-CI shape used by every build-* workflow in this
# repo (see catalyst-build.yaml).
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
if: github.event_name != 'pull_request'
uses: docker/setup-buildx-action@v3
- name: Build and push image
id: build
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v6
with:
# Build context is the repository root so the Dockerfile's
# COPY paths can reach both core/cmd/.../ and core/pkg/.../.
# The shared dynadot-client module is consumed via go.mod's
# local replace directive; the build context layout matches
# what the replace expects.
context: .
file: core/cmd/cert-manager-dynadot-webhook/Containerfile
push: true
tags: |
${{ env.IMAGE }}:${{ steps.vars.outputs.sha_short }}
${{ env.IMAGE }}:latest
labels: |
org.opencontainers.image.source=https://github.com/openova-io/openova
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.title=cert-manager-dynadot-webhook
org.opencontainers.image.description=cert-manager DNS-01 external webhook for Dynadot (closes openova#159)
provenance: true
sbom: true
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3
- name: Sign image with cosign (keyless)
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign --yes "${IMAGE}@${DIGEST}"
- name: Generate and attest SBOM
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign attest --yes \
--predicate <(echo '{"sbom":"in-toto-spdx attached at build time"}') \
--type spdx \
"${IMAGE}@${DIGEST}"

View File

@ -42,11 +42,7 @@ jobs:
- name: Build UI image (test)
uses: docker/build-push-action@v6
with:
# Build context is the repo root so the Vite prebuild script can
# walk platform/, products/, clusters/_template/bootstrap-kit/ to
# populate the catalog + BOOTSTRAP_KIT. The Containerfile fails
# the build if any of those dirs is missing.
context: openova-src
context: openova-src/products/catalyst/bootstrap/ui
file: openova-src/products/catalyst/bootstrap/ui/Containerfile
push: false
load: true
@ -89,8 +85,8 @@ jobs:
component-logos/cnpg.svg \
component-logos/loki.png \
component-logos/mimir.png \
component-logos/tempo.svg \
component-logos/ntfy.svg \
component-logos/tempo.png \
component-logos/ntfy.png \
component-logos/ferretdb.png \
component-logos/openmeter.png \
component-logos/coraza.png \
@ -109,54 +105,13 @@ jobs:
echo "Logo smoke OK: /${path} HTTP $CODE"
done
# Bootstrap-kit regression guard: the Provision page reads
# BOOTSTRAP_KIT from the bundled catalog.generated.ts to render
# the per-Blueprint bubbles. An earlier revision shipped with a
# docker context that didn't include clusters/_template/bootstrap-kit/
# so the prebuild script silently produced an empty array — the
# page rendered only the 2 supernodes. Asserting the bundle
# contains every bp-* id makes that regression impossible.
#
# Implementation note: we extract the entire bundle once via
# `tar c -C ... --transform`, then grep locally. Earlier we ran
# `grep` inside docker run -c "..." and the nested quote escaping
# produced false negatives (bp-cilium was in the bundle but the
# grep argument matched a literal `"bp-cilium"` whose surrounding
# quotes were eaten by shell expansion). Local grep on the
# extracted file removes that whole class of escaping bugs.
BUNDLE_TMP=$(mktemp)
docker run --rm --entrypoint sh ${{ env.UI_IMAGE }}:test \
-c 'cat $(find /usr/share/nginx/html/assets -name "index-*.js" | head -1)' \
> "$BUNDLE_TMP"
BUNDLE_BYTES=$(wc -c < "$BUNDLE_TMP")
echo "Bundle size: $BUNDLE_BYTES bytes"
if [ "$BUNDLE_BYTES" -lt 100000 ]; then
echo "Bootstrap-kit smoke FAILED: bundle suspiciously small ($BUNDLE_BYTES bytes)"
docker stop smoke-ui
exit 1
fi
for bp in bp-cilium bp-cert-manager bp-flux bp-crossplane bp-sealed-secrets \
bp-spire bp-nats-jetstream bp-openbao bp-keycloak bp-gitea ; do
if ! grep -q -F "$bp" "$BUNDLE_TMP" ; then
echo "Bootstrap-kit smoke FAILED: ${bp} missing from bundle"
docker stop smoke-ui
exit 1
fi
echo "Bootstrap-kit smoke OK: ${bp}"
done
rm -f "$BUNDLE_TMP"
docker stop smoke-ui
echo "All smoke tests passed."
- name: Push UI image
uses: docker/build-push-action@v6
with:
# Build context is the repo root so the Vite prebuild script can
# walk platform/, products/, clusters/_template/bootstrap-kit/ to
# populate the catalog + BOOTSTRAP_KIT. The Containerfile fails
# the build if any of those dirs is missing.
context: openova-src
context: openova-src/products/catalyst/bootstrap/ui
file: openova-src/products/catalyst/bootstrap/ui/Containerfile
push: true
tags: |
@ -192,81 +147,10 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Build context is the public openova repo root (openova-src/), not just
# products/catalyst/bootstrap/api/, because the runtime image bundles the
# canonical OpenTofu module from infra/hetzner/. The Containerfile's
# COPY paths are written relative to the repo root accordingly. Without
# this, /infra/hetzner/ is missing inside the image and every Launch
# fails with `stage tofu module: open /infra/hetzner: no such file or
# directory`.
- name: Build API image (test)
- name: Build and push API image
uses: docker/build-push-action@v6
with:
context: openova-src
file: openova-src/products/catalyst/bootstrap/api/Containerfile
push: false
load: true
tags: ${{ env.API_IMAGE }}:test
# Smoke test — the catalyst-api Pod is the OpenTofu runner, so the .tf
# sources MUST be present at /infra/hetzner/ inside the image. Anything
# less ships a broken image that fails on every Launch with `stage tofu
# module: open /infra/hetzner: no such file or directory`. Failure of
# this step fails the build.
- name: Smoke test API — verify infra/hetzner/ is bundled
run: |
set -euo pipefail
LISTING=$(docker run --rm --entrypoint sh ${{ env.API_IMAGE }}:test \
-c 'ls -la /infra/hetzner/')
echo "$LISTING"
for f in main.tf variables.tf outputs.tf versions.tf \
cloudinit-control-plane.tftpl cloudinit-worker.tftpl ; do
if ! echo "$LISTING" | grep -q " ${f}\$"; then
echo "Smoke test FAILED: /infra/hetzner/${f} missing from image"
exit 1
fi
echo "Smoke test OK: /infra/hetzner/${f} present"
done
echo "All API smoke tests passed."
# tofu CLI smoke test — the runtime image bundles the OpenTofu CLI
# because internal/provisioner execs `tofu init / plan / apply` (see
# internal/provisioner/provisioner.go runTofu()). Without the binary
# every Launch SSE stream returns:
# tofu init: exec: "tofu": executable file not found in $PATH
# We assert (a) `tofu version` succeeds inside the image and (b) the
# output matches the EXPECTED_TOFU_VERSION pinned here, which must
# stay in lockstep with the TOFU_VERSION ARG in the Containerfile.
# When you bump the version in the Containerfile, bump it here too.
- name: Smoke test API — verify OpenTofu CLI is installed
env:
EXPECTED_TOFU_VERSION: 1.11.6
run: |
set -euo pipefail
OUT=$(docker run --rm --entrypoint sh ${{ env.API_IMAGE }}:test \
-c 'tofu version')
echo "$OUT"
if ! echo "$OUT" | grep -q "^OpenTofu v${EXPECTED_TOFU_VERSION}\$"; then
echo "Smoke test FAILED: expected 'OpenTofu v${EXPECTED_TOFU_VERSION}', got:"
echo "$OUT"
exit 1
fi
echo "Smoke test OK: OpenTofu v${EXPECTED_TOFU_VERSION} present on PATH."
# Re-assert the binary is executable for the actual runtime UID
# (65534, set in api-deployment.yaml securityContext.runAsUser).
# `--user` overrides the image USER directive, simulating the K8s
# securityContext: a missing exec bit or wrong owner here would
# surface as a Launch failure in production, never in CI, so we
# gate it at build time.
docker run --rm --user 65534:65534 --entrypoint sh \
${{ env.API_IMAGE }}:test -c 'tofu version | head -1'
echo "Smoke test OK: tofu executable as UID 65534."
- name: Push API image
uses: docker/build-push-action@v6
with:
context: openova-src
context: openova-src/products/catalyst/bootstrap/api
file: openova-src/products/catalyst/bootstrap/api/Containerfile
push: true
tags: |

View File

@ -1,112 +0,0 @@
name: Cosmetic + step-flow regression guards
# Narrow, fast Playwright suite that protects the Catalyst bootstrap UI
# against the specific cosmetic + step-flow defects the user has called
# out repeatedly. Lives ALONGSIDE (not inside) the broader Group L
# Playwright smoke at .github/workflows/playwright-smoke.yaml — the two
# suites are independently triggered, both run on PRs that touch UI
# files. See docs/UI-REGRESSION-GUARDS.md for the test-to-complaint map.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4a (GitHub Actions is the only
# build path), this workflow does NOT build any container images — it
# only runs UI regression guards against a freshly-installed dev tree.
#
# Triggers:
# - pull_request when files under products/catalyst/bootstrap/ui/**
# change, OR core/console/** (canonical sidebar / appdetail /
# jobspage references) change, OR this workflow itself changes.
# - push to main on the same paths so a rolled-back regression on
# main also triggers a fresh run.
# - workflow_dispatch for ad-hoc runs.
on:
push:
branches: [main]
paths:
- 'products/catalyst/bootstrap/ui/**'
- 'core/console/**'
- '.github/workflows/cosmetic-guards.yaml'
- 'docs/UI-REGRESSION-GUARDS.md'
pull_request:
paths:
- 'products/catalyst/bootstrap/ui/**'
- 'core/console/**'
- '.github/workflows/cosmetic-guards.yaml'
- 'docs/UI-REGRESSION-GUARDS.md'
workflow_dispatch:
jobs:
guards:
name: Playwright cosmetic + step-flow guards
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: products/catalyst/bootstrap/ui/package-lock.json
- name: Install Catalyst UI dependencies
working-directory: products/catalyst/bootstrap/ui
run: npm ci
- name: Install Playwright browser (chromium)
working-directory: products/catalyst/bootstrap/ui
run: npx playwright install --with-deps chromium
- name: Boot Catalyst UI in background
working-directory: products/catalyst/bootstrap/ui
env:
HOST: 0.0.0.0
run: |
# Vite binds the port from vite.config.ts (server.port = 5173)
# under base /sovereign/. Tests reach the wizard at
# http://localhost:5173/sovereign/wizard.
nohup npm run dev > /tmp/catalyst-ui-dev.log 2>&1 &
echo $! > /tmp/catalyst-ui.pid
- name: Wait for Catalyst UI to be ready
run: |
for i in $(seq 1 60); do
if curl -sf -o /dev/null http://localhost:5173/sovereign/wizard; then
echo "UI ready after ${i}s"
exit 0
fi
sleep 1
done
echo "UI failed to start in 60s — log follows:"
cat /tmp/catalyst-ui-dev.log || true
exit 1
- name: Run cosmetic + step-flow regression guards
working-directory: products/catalyst/bootstrap/ui
env:
PLAYWRIGHT_HOST: http://localhost:5173
PLAYWRIGHT_BASEPATH: /sovereign
# --grep filters by the @cosmetic-guard annotation that every
# test in the suite carries. If a future test in the same file
# is added without the tag, this command will skip it — that's
# by design (the tag IS the contract).
run: npx playwright test e2e/cosmetic-guards.spec.ts --grep "@cosmetic-guard" --reporter=list
- name: Stop Catalyst UI
if: always()
run: |
if [ -f /tmp/catalyst-ui.pid ]; then
kill "$(cat /tmp/catalyst-ui.pid)" 2>/dev/null || true
fi
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: cosmetic-guards-report
path: |
products/catalyst/bootstrap/ui/playwright-report/
products/catalyst/bootstrap/ui/test-results/
retention-days: 7

View File

@ -13,8 +13,6 @@ on:
- 'platform/**/blueprint.yaml'
- 'platform/**/chart/**'
- 'clusters/**'
- 'scripts/check-bootstrap-deps.sh'
- 'scripts/expected-bootstrap-deps.yaml'
- '.github/workflows/test-bootstrap-kit.yaml'
branches: [main]
pull_request:
@ -23,39 +21,14 @@ on:
- 'platform/**/blueprint.yaml'
- 'platform/**/chart/**'
- 'clusters/**'
- 'scripts/check-bootstrap-deps.sh'
- 'scripts/expected-bootstrap-deps.yaml'
- '.github/workflows/test-bootstrap-kit.yaml'
workflow_dispatch:
jobs:
dependency-graph-audit:
# Audit the bootstrap-kit dependency graph against the expected DAG declared
# in scripts/expected-bootstrap-deps.yaml. Mechanically verifies every HR's
# spec.dependsOn matches the design contract in
# docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2 + §3, and detects cycles. Runs on
# every PR that touches a bootstrap-kit HR or the audit data files. Owned by
# W2.K0; consumed by W2.K1-K4 PRs to validate slot 15-48 additions.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install yq
run: |
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
yq --version
- name: Run bootstrap-kit dependency audit
run: bash scripts/check-bootstrap-deps.sh
manifest-validation:
# Static-only validation: blueprint.yaml + chart Chart.yaml + clusters/_template
# parsing + dependency order check. Runs on every push.
runs-on: ubuntu-latest
needs: dependency-graph-audit
defaults:
run:
working-directory: tests/e2e/bootstrap-kit

View File

@ -1,84 +0,0 @@
name: Test — Strategy flip regression (RollingUpdate -> Recreate)
# Defends the Catalyst chart against the contabo-mkt outage of
# 2026-04-29. See docs/CHART-AUTHORING.md §"Strategy flips on
# existing Deployments" for the full failure-mode analysis. The
# integration test runner at tests/integration/strategy-flip.sh
# encodes the contract; this workflow gives it a kind cluster and
# kubectl.
#
# This is a chart-authoring gate. It runs whenever:
# - the catalyst-api Deployment manifest changes (the canonical
# resource that triggered the regression)
# - the strategy-flip integration test changes
# - the chart-authoring documentation changes
#
# Keep the trigger list tight; a chart-author touching unrelated
# files should not pay this gate's cold-start cost.
on:
push:
paths:
- 'products/catalyst/chart/templates/api-deployment.yaml'
- 'tests/integration/strategy-flip.yaml'
- 'tests/integration/strategy-flip.sh'
- 'docs/CHART-AUTHORING.md'
- '.github/workflows/test-strategy-flip.yaml'
branches: [main]
pull_request:
paths:
- 'products/catalyst/chart/templates/api-deployment.yaml'
- 'tests/integration/strategy-flip.yaml'
- 'tests/integration/strategy-flip.sh'
- 'docs/CHART-AUTHORING.md'
- '.github/workflows/test-strategy-flip.yaml'
workflow_dispatch:
jobs:
strategy-flip-regression:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up kind cluster
uses: helm/kind-action@v1
with:
# Pinned kind version so a kind release that ships with a
# different K8s server cannot silently change the regression's
# error string. Bumping the kind version is a deliberate
# chart-authoring decision — re-run the local test, update
# the assertion ConfigMap if needed, and PR together.
cluster_name: strategy-flip-test
version: v0.25.0
# K8s 1.30+ enforces strict-decoding on apps/v1 Deployment by
# default — that is the version where `$patch: replace`
# inline in a base resource started failing CREATE with
# "unknown field" errors. Locking to 1.30.6 ensures the test
# exercises the same decoding path Flux's contabo-mkt cluster
# runs (k3s 1.30 series).
node_image: kindest/node:v1.30.6
- name: Verify kind cluster ready
run: |
kubectl cluster-info
kubectl get nodes
kubectl version --short || kubectl version
- name: Run strategy-flip integration test
run: bash tests/integration/strategy-flip.sh
- name: Show kubectl events on failure
if: failure()
run: |
# Surface anything the test runner did not already print.
# The test cleans up its namespaces in a trap, so this only
# fires when the runner errored mid-flight before the trap
# had a chance to log full context.
echo "=== test namespace events ==="
kubectl get events -n strategy-flip-test --sort-by=.lastTimestamp 2>&1 || true
echo "=== fresh-install namespace events ==="
kubectl get events -n strategy-flip-fresh --sort-by=.lastTimestamp 2>&1 || true
echo "=== chart manifest under test ==="
cat products/catalyst/chart/templates/api-deployment.yaml || true

14
.gitignore vendored
View File

@ -1,14 +0,0 @@
# Helm dependency-build artifacts. Generated by `helm dependency build` in CI
# and locally for validation. Subcharts are downloaded into chart/charts/ and
# locked via chart/Chart.lock; both are reproducible from chart/Chart.yaml's
# `dependencies:` block, so we don't track them.
**/charts/*.tgz
platform/*/chart/charts/
platform/*/chart/Chart.lock
products/*/chart/charts/
products/*/chart/Chart.lock
# Node + dev artifacts (untracked already, listed here for clarity).
**/node_modules/
**/dist/
**/.astro/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

View File

@ -5,13 +5,13 @@
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
# kube-system is built into every Kubernetes cluster — never re-declare it.
# Earlier revisions of 01-cilium.yaml AND 05-sealed-secrets.yaml both
# declared it, which collided when kustomize tried to merge the two:
# "may not add resource with an already registered id:
# Namespace.v1.[noGrp]/kube-system.[noNs]"
# This Blueprint installs Cilium INTO kube-system; the HelmRelease's
# targetNamespace field below is sufficient.
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-cilium
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -36,40 +34,14 @@ spec:
chart:
spec:
chart: bp-cilium
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cilium
namespace: flux-system
# Event-driven install: Helm completes when manifests apply, not when
# cilium-agent reaches Ready (agent waits for envoyconfig CRDs that the
# SAME chart installs — legitimate slow-Ready). Replaces blanket
# spec.timeout: 15m band-aid from PR #221.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cilium:
# Enable L7 proxy so Cilium's chart installs the
# ciliumenvoyconfigs / ciliumclusterwideenvoyconfigs CRDs that the
# cilium-agent waits for at startup. Without this, agent crash-loops
# forever and the node.cilium.io/agent-not-ready taint never lifts.
l7Proxy: true
prometheus:
enabled: false
serviceMonitor:
enabled: false
hubble:
metrics:
enabled: null
serviceMonitor:
enabled: false
relay:
enabled: false
ui:
enabled: false

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: cert-manager
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-cert-manager
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,27 +36,14 @@ spec:
chart:
spec:
chart: bp-cert-manager
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cert-manager
namespace: flux-system
# Event-driven install: cert-manager installs CRDs + 3 deployments
# (controller, webhook, cainjector). Webhook readiness depends on the
# cainjector mutating the Secret — multi-minute path on cold start.
# Helm install completes when manifests apply; subsequent dependsOn
# checks Ready=True independently. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cert-manager:
prometheus:
enabled: false
servicemonitor:
enabled: false

View File

@ -3,35 +3,14 @@
# Wrapper chart: platform/flux/chart/
# Catalyst-curated values: platform/flux/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# DOUBLE-INSTALL SAFETY (omantel.omani.works incident, 2026-04-29)
# ----------------------------------------------------------------
# Cloud-init pre-installs Flux core via
# curl https://github.com/fluxcd/flux2/releases/download/v2.4.0/install.yaml
# so that this very HelmRelease can be reconciled. helm-controller then
# runs `helm install` for bp-flux on top of the already-running Flux. If
# the chart's subchart `flux2` version disagrees with the cloud-init
# install (different upstream Flux release), CRD `storedVersions`
# mismatches → Helm install fails → rollback → rollback DELETES the
# running Flux controllers → cluster has no GitOps engine and is
# unrecoverable in-place.
#
# Mitigations applied here:
# 1. bp-flux:1.1.2 pins the `flux2` subchart at 2.14.1 (= appVersion
# 2.4.0) which matches cloud-init's v2.4.0 install.yaml.
# 2. spec.upgrade.preserveValues: true — never silently overwrite
# operator overlays on upgrade.
# controller adopts the cloud-init-installed objects rather than
# re-creating, so install is non-destructive when objects already
# exist with matching apiVersion/kind/name.
# See docs/RUNBOOK-PROVISIONING.md §"bp-flux double-install".
---
apiVersion: v1
kind: Namespace
metadata:
name: flux-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -41,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-flux
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -59,30 +36,14 @@ spec:
chart:
spec:
chart: bp-flux
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-flux
namespace: flux-system
# Event-driven install: bp-flux adopts the cloud-init-installed Flux
# controllers; the helm-controller pod that reconciles THIS HR is itself
# a target of the chart, so blocking on Ready=True is structurally
# impossible. disableWait avoids the deadlock. Replaces PR #221 timeout.
install:
disableWait: true
# Adopt cloud-init-installed Flux objects rather than fail on
# ownership conflict (the objects exist before the HelmRelease ever
# reconciles). Without this, the very first reconcile would error
# with "object already exists" on every Flux controller Deployment.
remediation:
retries: 3
upgrade:
disableWait: true
# Keep operator-supplied values (e.g. resource overrides applied via
# helm-controller out-of-band, or dry-run patches during incident
# response) on chart upgrades. Without this, every upgrade would
# reset the chart to default values, masking operator state.
preserveValues: true
# Match install behaviour — adopt rather than fail on conflict.
remediation:
retries: 3

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: crossplane-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-crossplane
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -31,7 +29,6 @@ metadata:
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: crossplane
targetNamespace: crossplane-system
dependsOn:
@ -39,7 +36,7 @@ spec:
chart:
spec:
chart: bp-crossplane
version: 1.1.3
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-crossplane

View File

@ -5,9 +5,13 @@
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
# kube-system is built into every Kubernetes cluster — never re-declare it.
# See 01-cilium.yaml for the full incident note. Sealed-Secrets installs
# INTO kube-system; the HelmRelease's targetNamespace below is sufficient.
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
@ -16,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-sealed-secrets
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -34,18 +36,14 @@ spec:
chart:
spec:
chart: bp-sealed-secrets
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-sealed-secrets
namespace: flux-system
# Event-driven install: single-replica controller + CRD; install
# completes when manifests apply. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: spire-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-spire
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,23 +36,14 @@ spec:
chart:
spec:
chart: bp-spire
version: 1.1.4
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-spire
namespace: flux-system
# Event-driven install: Helm completes when manifests apply, not when
# pods reach Ready. spire-server StatefulSet has a multi-minute Ready
# path (controller-manager waits for CRD informer cache sync, which is
# itself triggered by the spire-crds subchart's CRD install). Flux's
# `dependsOn` on downstream HRs (bp-nats-jetstream, bp-openbao) checks
# Ready=True on this HR independently, so disableWait is the correct
# shape — replaces the blanket spec.timeout: 15m band-aid from PR #221.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: nats-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-nats-jetstream
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,20 +36,14 @@ spec:
chart:
spec:
chart: bp-nats-jetstream
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-nats-jetstream
namespace: flux-system
# Event-driven install: NATS StatefulSet with JetStream raft initialisation
# — quorum formation across N replicas is legitimately multi-minute on
# cold start. Helm install completes when manifests apply; downstream
# dependsOn checks Ready=True independently. Replaces PR #221 timeout.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: openbao
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-openbao
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,21 +36,14 @@ spec:
chart:
spec:
chart: bp-openbao
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-openbao
namespace: flux-system
# Event-driven install: OpenBao 3-node Raft cluster requires manual
# unseal via `bao operator init` — pods stay sealed (Ready=False) until
# an operator runs the unseal flow. Blocking Helm install on Ready=True
# is structurally wrong for a sealed-by-default secret backend.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -10,7 +10,7 @@ kind: Namespace
metadata:
name: keycloak
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-keycloak
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,21 +36,14 @@ spec:
chart:
spec:
chart: bp-keycloak
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-keycloak
namespace: flux-system
# Event-driven install: Keycloak DB schema migration + realm import is
# legitimately multi-minute on first install (PostgreSQL backend +
# 100+ Liquibase changesets). Helm install completes when manifests
# apply; downstream dependsOn checks Ready=True independently.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -11,7 +11,7 @@ kind: Namespace
metadata:
name: gitea
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -21,9 +21,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-gitea
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -39,31 +37,25 @@ spec:
chart:
spec:
chart: bp-gitea
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-gitea
namespace: flux-system
# Event-driven install: Gitea PostgreSQL DB init + admin user creation +
# public Blueprint catalog mirror seeding is legitimately multi-minute.
# Helm install completes when manifests apply; downstream dependsOn
# checks Ready=True independently. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
global:
sovereignFQDN: ${SOVEREIGN_FQDN}
# gitea hostname is gitea.${SOVEREIGN_FQDN}. The DNS A record
sovereignFQDN: SOVEREIGN_FQDN_PLACEHOLDER
# gitea hostname is gitea.SOVEREIGN_FQDN_PLACEHOLDER. The DNS A record
# was already published by the Phase-0 catalyst-dns helper.
ingress:
hosts:
- host: gitea.${SOVEREIGN_FQDN}
- host: gitea.SOVEREIGN_FQDN_PLACEHOLDER
paths:
- path: /
pathType: Prefix

View File

@ -1,11 +1,11 @@
# bp-catalyst-platform — Catalyst Blueprint #13 of 13. The umbrella
# bp-catalyst-platform — Catalyst Blueprint #11 of 11. The umbrella
# Blueprint that brings up the Catalyst control plane: console, marketplace,
# admin, catalog-svc, projector, provisioning, environment-controller,
# blueprint-controller, billing.
#
# Per docs/ARCHITECTURE.md §11 (Catalyst-on-Catalyst): once this is Ready,
# the Sovereign is fully self-sufficient — sovereign-admin can log into
# console.otech.omani.works and proceed with Phase 2 day-1 setup.
# console.SOVEREIGN_FQDN_PLACEHOLDER and proceed with Phase 2 day-1 setup.
#
# Wrapper chart: products/catalyst/chart/
@ -15,7 +15,7 @@ kind: Namespace
metadata:
name: catalyst-system
labels:
catalyst.openova.io/sovereign: otech.omani.works
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@ -25,9 +25,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-catalyst-platform
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -43,22 +41,15 @@ spec:
chart:
spec:
chart: bp-catalyst-platform
version: 1.1.8
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-catalyst-platform
namespace: flux-system
# Event-driven install: umbrella chart deploys ~10 Catalyst services
# (console, marketplace, admin, catalog-svc, projector, provisioning,
# environment-controller, blueprint-controller, billing). Inter-service
# readiness via OTel/NATS subjects is multi-minute and not Helm's
# concern. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
# Per-Sovereign overrides for the umbrella — sovereign-FQDN-derived hostnames
@ -66,14 +57,14 @@ spec:
# OTel endpoints, NATS subjects) lives in products/catalyst/chart/values.yaml.
values:
global:
sovereignFQDN: otech.omani.works
sovereignFQDN: SOVEREIGN_FQDN_PLACEHOLDER
ingress:
hosts:
console:
host: console.otech.omani.works
host: console.SOVEREIGN_FQDN_PLACEHOLDER
admin:
host: admin.otech.omani.works
host: admin.SOVEREIGN_FQDN_PLACEHOLDER
marketplace:
host: otech.omani.works
host: SOVEREIGN_FQDN_PLACEHOLDER
api:
host: api.otech.omani.works
host: api.SOVEREIGN_FQDN_PLACEHOLDER

View File

@ -1,104 +0,0 @@
# bp-powerdns — Catalyst Blueprint #11 of 13. Per-Sovereign authoritative
# DNS. Every Sovereign owns its own PowerDNS Authoritative server with the
# Sovereign's zones loaded into a CNPG-backed gpgsql backend; per-zone
# DNSSEC (ECDSAP256SHA256), lua-records for geo + health-checked failover,
# and a dnsdist companion for query rate-limiting + DDoS posture.
#
# Architectural anchor — per-Sovereign PowerDNS, NOT a shared instance:
# This file was added so each Sovereign has its own bp-powerdns release
# alongside bp-external-dns (#12). The original Phase-0 dial-tone
# PowerDNS used to live as a singleton in the openova-system namespace
# on contabo-mkt; #168 ("per-Sovereign PowerDNS zones") flipped that
# model so a Sovereign's zones never leave the Sovereign's own cluster.
# bp-external-dns's `dependsOn: [bp-powerdns]` therefore must be
# satisfied by an in-cluster sibling release — this file IS that
# sibling.
#
# Wrapper chart: platform/powerdns/chart/
# Catalyst-curated values: platform/powerdns/chart/values.yaml
# - 3 PowerDNS Authoritative replicas behind dnsdist
# - CNPG-managed `pdns-pg` Postgres cluster for zone storage
# - DNSSEC ON by default (ECDSAP256SHA256)
# - Lua-records ON (geo + health-checked failover patterns)
# - REST API at pdns.<sovereign>/api behind Traefik basicAuth
#
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — the REST API ingress requests a TLS cert from
# the cluster's letsencrypt-prod ClusterIssuer; without bp-cert-manager
# reconciled first the Certificate resource sits Pending.
#
# Hard-but-implicit dependencies (CRDs, NOT sibling Blueprints):
# - postgresql.cnpg.io/v1.Cluster — provided by the CNPG operator. The
# chart's templates/cnpg-cluster.yaml renders this CR; if CNPG isn't
# yet installed on the Sovereign, the HelmRelease will wait. CNPG is
# a fixture of Catalyst-Zero (FABRIC group, componentGroups.ts `cnpg`)
# and is installed alongside this kit by the bootstrap installer.
# - traefik.io/v1alpha1.Middleware — Traefik is the Catalyst-Zero
# ingress controller and is a fixture of every Sovereign.
#
# Per-Sovereign overrides intentionally NOT set here:
# - Zones, API basic-auth credentials, anycast Floating-IP target — all
# of those are provisioned by Crossplane / pool-domain-manager (PDM)
# after the cluster comes up, NOT baked into the bootstrap kit. This
# file installs the chart with defaults so the Sovereign has a working
# authoritative DNS surface that PDM can then load zones into.
---
apiVersion: v1
kind: Namespace
metadata:
name: powerdns
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-powerdns
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-powerdns
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: powerdns
targetNamespace: powerdns
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-powerdns
version: 1.1.3
sourceRef:
kind: HelmRepository
name: bp-powerdns
namespace: flux-system
# disableWait: a Sovereign without bp-cnpg yet reconciled has no
# `pdns-pg-app` Secret (the chart's CNPG Cluster template is gated
# behind the `postgresql.cnpg.io/v1` CRD via Capabilities.APIVersions
# check — see chart/templates/cnpg-cluster.yaml). Without disableWait,
# Helm's `--wait` would hold until the powerdns Deployment is Ready,
# which can't happen until CNPG comes up and synthesises the Secret.
# The HelmRelease itself reports Ready as soon as the manifests apply
# cleanly; runtime convergence (powerdns pods becoming Ready once
# CNPG lands) is observed via kubectl, not gated on Helm.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,80 +0,0 @@
# bp-external-dns — Catalyst Blueprint #12 of 13. Per-Sovereign DNS sync —
# ExternalDNS reconciles Service/Ingress hostnames into the per-Sovereign
# PowerDNS authoritative server via the native `pdns` provider. Geo +
# health-checked failover responses are owned by PowerDNS lua-records,
# NOT by ExternalDNS.
#
# Wrapper chart: platform/external-dns/chart/
#
# dependsOn:
# - bp-cert-manager — ExternalDNS HelmRelease only after TLS issuers
# are reconciled, so any cert-manager-fronted webhook endpoints in
# downstream overlays come up cleanly.
# - bp-powerdns — native `pdns` provider points at the in-cluster
# bp-powerdns Service and reads the `powerdns-api-credentials` Secret
# it renders. Without bp-powerdns the ExternalDNS pod CrashLoops
# trying to dial a non-existent DNS API.
---
apiVersion: v1
kind: Namespace
metadata:
name: external-dns
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-external-dns
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-external-dns
namespace: flux-system
spec:
interval: 15m
releaseName: external-dns
targetNamespace: external-dns
dependsOn:
- name: bp-cert-manager
- name: bp-powerdns
chart:
spec:
chart: bp-external-dns
version: 1.1.2
sourceRef:
kind: HelmRepository
name: bp-external-dns
namespace: flux-system
# Event-driven install: ExternalDNS pod readiness depends on a
# successful initial reconcile against the per-Sovereign PowerDNS API
# (which itself stabilises after pdns-pg CNPG bootstraps) — legitimate
# slow-Ready cascade. Helm install completes when manifests apply.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
# Per-Sovereign overrides — txtOwnerId MUST be the Sovereign FQDN so two
# Sovereigns sharing a parent zone don't fight over the same record set.
# domainFilters narrow the zones ExternalDNS will manage; per-Sovereign
# cluster overlays patch this with the actual zone list.
values:
external-dns:
txtOwnerId: ${SOVEREIGN_FQDN}
txtPrefix: _externaldns.
domainFilters:
- ${SOVEREIGN_FQDN}

View File

@ -1,79 +0,0 @@
# bp-catalyst-platform — Catalyst Blueprint #13 of 13. The umbrella
# Blueprint that brings up the Catalyst control plane: console, marketplace,
# admin, catalog-svc, projector, provisioning, environment-controller,
# blueprint-controller, billing.
#
# Per docs/ARCHITECTURE.md §11 (Catalyst-on-Catalyst): once this is Ready,
# the Sovereign is fully self-sufficient — sovereign-admin can log into
# console.${SOVEREIGN_FQDN} and proceed with Phase 2 day-1 setup.
#
# Wrapper chart: products/catalyst/chart/
---
apiVersion: v1
kind: Namespace
metadata:
name: catalyst-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-catalyst-platform
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-catalyst-platform
namespace: flux-system
spec:
interval: 15m
releaseName: catalyst-platform
targetNamespace: catalyst-system
dependsOn:
- name: bp-gitea
chart:
spec:
chart: bp-catalyst-platform
version: 1.1.8
sourceRef:
kind: HelmRepository
name: bp-catalyst-platform
namespace: flux-system
# Event-driven install: umbrella chart deploys ~10 Catalyst services
# (console, marketplace, admin, catalog-svc, projector, provisioning,
# environment-controller, blueprint-controller, billing). Inter-service
# readiness via OTel/NATS subjects is multi-minute and not Helm's
# concern. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
# Per-Sovereign overrides for the umbrella — sovereign-FQDN-derived hostnames
# for console/admin/api. All chart-level Catalyst service config (image refs,
# OTel endpoints, NATS subjects) lives in products/catalyst/chart/values.yaml.
values:
global:
sovereignFQDN: ${SOVEREIGN_FQDN}
ingress:
hosts:
console:
host: console.${SOVEREIGN_FQDN}
admin:
host: admin.${SOVEREIGN_FQDN}
marketplace:
host: ${SOVEREIGN_FQDN}
api:
host: api.${SOVEREIGN_FQDN}

View File

@ -1,58 +0,0 @@
# bp-crossplane-claims — Catalyst Day-2 CRUD XRDs (compose.openova.io/v1alpha1)
# + default Hetzner-backed Compositions. Split out of bp-crossplane at
# release 1.1.3 to resolve intra-chart CRD-ordering: a single Helm release
# cannot install a CRD AND a CR of that CRD's kind in one apply pass —
# the apiserver rejects the CR because the CRD is not yet registered.
#
# Wrapper chart: platform/crossplane-claims/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-crossplane is Ready (dependsOn below) — at which point
# the apiextensions.crossplane.io/v1 CRDs that the upstream
# crossplane subchart registers are live.
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-crossplane-claims
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-crossplane-claims
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: crossplane-claims
targetNamespace: crossplane-system
# bp-crossplane installs the apiextensions.crossplane.io/v1 CRDs
# (CompositeResourceDefinition, Composition) via the upstream subchart's
# CRD payload. We MUST wait until that HelmRelease reports Ready=True
# before applying the XRDs+Compositions in this chart, otherwise the
# apiserver returns:
# no matches for kind "CompositeResourceDefinition" in version
# "apiextensions.crossplane.io/v1" -- ensure CRDs are installed first
dependsOn:
- name: bp-crossplane
chart:
spec:
chart: bp-crossplane-claims
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-crossplane-claims
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3

View File

@ -1,75 +0,0 @@
# bp-external-secrets — Catalyst bootstrap-kit Blueprint, W2.K1 slot 15.
# External Secrets Operator (ESO). Day-2 secret pipeline from OpenBao →
# Kubernetes Secret materialization. Takes over from bp-sealed-secrets
# (slot 05, transient) once OpenBao is Ready and ESO is reconciling.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §1.2 (Tier 0/3 — host-layer
# secret distribution) and §2.3 (W2.K1 dependency graph): ESO is sequenced
# at slot 15 (first slot in the W2.K1 contiguous range) but its
# dependsOn is [bp-openbao(08), bp-cert-manager(02)] — Flux gates install
# on Ready=True for both, regardless of slot order. ESO's webhook
# requests a TLS cert from the cluster's letsencrypt-prod / cluster-ca
# ClusterIssuer; without bp-cert-manager Ready, the Certificate stays
# Pending and the operator never reaches Running.
#
# Wrapper chart: platform/external-secrets/chart/
# Catalyst-curated values: platform/external-secrets/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: external-secrets-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-external-secrets
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-external-secrets
namespace: flux-system
spec:
interval: 15m
releaseName: external-secrets
targetNamespace: external-secrets-system
# ESO depends on:
# - bp-openbao(08): the secret backend ESO pulls from. ClusterSecretStore
# resources reference the in-cluster OpenBao service; without OpenBao
# Ready those CRs land but every ExternalSecret reconcile fails.
# - bp-cert-manager(02): ESO's admission webhook needs a TLS cert.
# bp-cert-manager provides the ClusterIssuer that signs it.
dependsOn:
- name: bp-openbao
- name: bp-cert-manager
chart:
spec:
chart: bp-external-secrets
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-external-secrets
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3 (Flux
# dependsOn is the gate, not Helm timeout). Replaces blanket
# spec.timeout: 15m band-aid pattern from PR #221, removed in PR #250.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-cnpg — Catalyst bootstrap-kit Blueprint, W2.K1 slot 16.
# CloudNativePG operator. Per-Sovereign Postgres-as-a-service via
# postgresql.cnpg.io/v1.Cluster CRs. Required by every PG-backed bootstrap
# component: bp-powerdns (pdns-pg-app), bp-keycloak HA, bp-gitea metadata,
# bp-langfuse, bp-grafana config DB, bp-temporal, bp-matrix synapse,
# bp-llm-gateway, bp-bge, bp-nemo-guardrails, bp-openmeter (cnpg profile),
# pool-domain-manager (PDM).
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) — cnpg gates the entire downstream observability/AI/relay
# stack; placing it in the bootstrap-kit (instead of as an Application
# Blueprint) is what unblocks the omantel-1 day-1 control plane.
#
# Resolves issue #254 (bp-powerdns pod stuck in CreateContainerConfigError
# because pdns-pg-app Secret is generated by a CNPG Cluster CR; without
# the operator the secret never materializes).
#
# Wrapper chart: platform/cnpg/chart/
# Catalyst-curated values: platform/cnpg/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: cnpg-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-cnpg
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-cnpg
namespace: flux-system
spec:
interval: 15m
releaseName: cnpg
targetNamespace: cnpg-system
# CNPG only needs Flux Ready (its own CRDs ship in the same chart;
# consumers of postgresql.cnpg.io/v1.Cluster gate themselves on bp-cnpg).
dependsOn:
- name: bp-flux
chart:
spec:
chart: bp-cnpg
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cnpg
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,62 +0,0 @@
# bp-valkey — Catalyst bootstrap-kit Blueprint, W2.K1 slot 17.
# Redis-compatible cache (Valkey is the BSD-licensed Redis fork). Used by
# Catalyst control-plane services for ephemeral session/state, and by
# downstream Apps that need a Redis wire-protocol cache.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) — Valkey runs alongside CNPG so Apps can pick the right
# storage shape per workload (relational vs cache).
#
# Wrapper chart: platform/valkey/chart/
# Catalyst-curated values: platform/valkey/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: valkey
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-valkey
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-valkey
namespace: flux-system
spec:
interval: 15m
releaseName: valkey
targetNamespace: valkey
# Valkey is a self-contained cache — only needs Flux Ready.
dependsOn:
- name: bp-flux
chart:
spec:
chart: bp-valkey
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-valkey
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-seaweedfs — Catalyst bootstrap-kit Blueprint, W2.K1 slot 18.
# S3-compatible object storage encapsulation. Pre-requisite for every
# blob-storage consumer in the bootstrap-kit:
# - bp-velero (slot 34) — backups
# - bp-loki (slot 22) — log chunks
# - bp-mimir (slot 23) — metric blocks
# - bp-tempo (slot 24) — trace blocks
# - bp-harbor (slot 19) — registry blob storage
# and downstream Apps that need cheap S3-compatible blob storage.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) and PROVISIONING-PLAN.md §3 — every Sovereign owns its own
# SeaweedFS so blob data never leaves the Sovereign's cluster.
#
# Wrapper chart: platform/seaweedfs/chart/
# Catalyst-curated values: platform/seaweedfs/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: seaweedfs
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-seaweedfs
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-seaweedfs
namespace: flux-system
spec:
interval: 15m
releaseName: seaweedfs
targetNamespace: seaweedfs
# SeaweedFS depends on:
# - bp-flux(03): standard GitOps prerequisite.
# - bp-cert-manager(02): the S3 Filer endpoint terminates TLS via a
# Certificate the chart requests from the cluster ClusterIssuer.
dependsOn:
- name: bp-flux
- name: bp-cert-manager
chart:
spec:
chart: bp-seaweedfs
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-seaweedfs
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,69 +0,0 @@
# bp-harbor — Catalyst bootstrap-kit Blueprint, W2.K1 slot 19.
# Per-Sovereign OCI registry. Mirrors blueprint chart artifacts and
# container images so the Sovereign isn't dependent on ghcr.io for
# day-2 image pulls; also hosts Org-private images per Application.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §6.7 — Harbor sits in the
# storage cohort (W2.K1) rather than apps cohort because it is a
# consumer of CNPG (registry metadata DB) and SeaweedFS (blob backend),
# and its presence gates Cosign signing in bp-sigstore (slot 32) and
# image pinning across all later HRs.
#
# Wrapper chart: platform/harbor/chart/
# Catalyst-curated values: platform/harbor/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: harbor
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-harbor
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-harbor
namespace: flux-system
spec:
interval: 15m
releaseName: harbor
targetNamespace: harbor
# Harbor depends on:
# - bp-cnpg(16): registry metadata DB (postgresql.cnpg.io/v1.Cluster).
# - bp-seaweedfs(18): registry blob backend (S3-compatible).
# - bp-cert-manager(02): registry endpoint TLS via ClusterIssuer.
dependsOn:
- name: bp-cnpg
- name: bp-seaweedfs
- name: bp-cert-manager
chart:
spec:
chart: bp-harbor
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-harbor
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,74 +0,0 @@
# bp-opentelemetry — Catalyst Blueprint #20 (W2.K2 Observability batch).
# OpenTelemetry Collector — pipeline source for the LGTM stack
# (Loki / Mimir / Tempo). Receives OTLP from workloads and fans out
# logs → Loki, metrics → Mimir, traces → Tempo.
#
# Wrapper chart: platform/opentelemetry/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cert-manager is Ready (the Collector's webhook /
# OTLP-HTTPS receiver request TLS certs from the cluster
# ClusterIssuer).
#
# dependsOn:
# - bp-cert-manager (slot 02) — TLS for OTLP-HTTPS receiver and any
# Collector webhooks.
#
# Hard-but-implicit dependencies (CRDs, NOT sibling Blueprints):
# - opentelemetry.io/v1beta1.OpenTelemetryCollector — provided by the
# OpenTelemetry Operator subchart bundled in this Blueprint.
#
# disableWait: the OTel Collector chart deploys multiple components
# (operator, collector DaemonSet/Deployment, instrumentation CR). Helm
# `--wait` would block on every Pod becoming Ready, which can't happen
# until downstream backends (Loki/Mimir/Tempo, slots 2224) are up to
# accept exports. The HelmRelease itself reports Ready as soon as
# manifests apply cleanly; runtime convergence is observed via kubectl.
---
apiVersion: v1
kind: Namespace
metadata:
name: opentelemetry
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-opentelemetry
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-opentelemetry
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: opentelemetry
targetNamespace: opentelemetry
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-opentelemetry
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-opentelemetry
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-alloy — Catalyst Blueprint #21 (W2.K2 Observability batch).
# Grafana Alloy — the unified telemetry collector for the LGTM stack.
# Runs as a DaemonSet on every node; tails container logs, scrapes
# Prometheus metrics, and forwards traces. Co-resident with bp-opentelemetry
# (slot 20) — Alloy handles host-level collection (kubelet, journald,
# node_exporter) while OTel handles app-level OTLP.
#
# Wrapper chart: platform/alloy/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-opentelemetry is Ready (Alloy's default config
# forwards OTLP to the Collector's gRPC endpoint).
#
# dependsOn:
# - bp-opentelemetry (slot 20) — Alloy forwards OTLP to the Collector.
# Without the Collector Service in place, Alloy retries forever on a
# non-existent upstream.
#
# disableWait: Alloy is a DaemonSet — Helm `--wait` would block on
# every node's Alloy Pod becoming Ready. On larger Sovereigns this can
# legitimately take >5min during a cold-start image pull; the HelmRelease
# reports Ready when manifests apply, runtime convergence observed via
# kubectl.
---
apiVersion: v1
kind: Namespace
metadata:
name: alloy
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-alloy
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-alloy
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: alloy
targetNamespace: alloy
dependsOn:
- name: bp-opentelemetry
chart:
spec:
chart: bp-alloy
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-alloy
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-loki — Catalyst Blueprint #22 (W2.K2 Observability batch).
# Grafana Loki — log storage tier of the LGTM stack. Default deployment
# shape is SingleBinary (one Loki StatefulSet) — minimum operational
# cost for a Solo Sovereign; per-Sovereign overlays scale to
# distributor/ingester/querier StatefulSets when load warrants.
#
# Wrapper chart: platform/loki/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Loki chunks/index live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Loki uses SeaweedFS S3 for chunks and
# index storage. Without SeaweedFS Ready, Loki cannot persist logs.
#
# disableWait: Loki SingleBinary becomes Ready only after creating the
# S3 bucket and writing the first WAL block — both of which require
# bp-seaweedfs to be fully reconciled (not just HelmRelease=Ready).
# Helm `--wait` would block waiting for the StatefulSet rollout, which
# the HelmRelease cannot influence.
---
apiVersion: v1
kind: Namespace
metadata:
name: loki
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-loki
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-loki
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: loki
targetNamespace: loki
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-loki
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-loki
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-mimir — Catalyst Blueprint #23 (W2.K2 Observability batch).
# Grafana Mimir — metrics storage tier of the LGTM stack. SeaweedFS-backed
# blocks-storage; Prometheus-compatible remote-write endpoint.
#
# Wrapper chart: platform/mimir/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Mimir blocks live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Mimir blocks-storage uses SeaweedFS S3.
# Without SeaweedFS Ready, Mimir compactor/ingester StatefulSets
# cannot persist metric blocks.
#
# disableWait: Mimir is a multi-component distributed system (distributor,
# ingester, querier, store-gateway, compactor, alertmanager, ruler).
# Helm `--wait` would block on every StatefulSet rollout, which can
# legitimately take >5min during a cold-start. The HelmRelease reports
# Ready when manifests apply; runtime convergence is observed via
# kubectl rollout status.
---
apiVersion: v1
kind: Namespace
metadata:
name: mimir
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-mimir
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-mimir
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: mimir
targetNamespace: mimir
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-mimir
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-mimir
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,66 +0,0 @@
# bp-tempo — Catalyst Blueprint #24 (W2.K2 Observability batch).
# Grafana Tempo — distributed tracing storage tier of the LGTM stack.
# Default deployment shape is single-binary (one Tempo StatefulSet) —
# minimum operational cost for a Solo Sovereign.
#
# Wrapper chart: platform/tempo/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Tempo trace blocks live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Tempo blocks live on SeaweedFS S3.
# Without SeaweedFS Ready, Tempo cannot persist spans.
#
# disableWait: Tempo single-binary StatefulSet becomes Ready after
# creating the S3 bucket and initialising the WAL — both require
# bp-seaweedfs to be fully reconciled. Helm `--wait` would block on
# the rollout, which the HelmRelease cannot influence.
---
apiVersion: v1
kind: Namespace
metadata:
name: tempo
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-tempo
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-tempo
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: tempo
targetNamespace: tempo
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-tempo
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-tempo
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,75 +0,0 @@
# bp-grafana — Catalyst Blueprint #25 (W2.K2 Observability batch).
# Grafana — visualization layer of the LGTM stack. Pairs with bp-loki
# (logs), bp-mimir (metrics), bp-tempo (traces); CNPG-backed Postgres
# for dashboard/folder/alert state; Keycloak OIDC for SSO.
#
# Wrapper chart: platform/grafana/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cnpg, bp-loki, bp-mimir, bp-tempo, bp-keycloak are
# all Ready.
#
# dependsOn:
# - bp-cnpg (slot 16) — Postgres backend for Grafana state.
# - bp-loki (slot 22) — datasource for logs.
# - bp-mimir (slot 23) — datasource for metrics.
# - bp-tempo (slot 24) — datasource for traces.
# - bp-keycloak (slot 09) — OIDC IdP for SSO.
#
# disableWait: Grafana waits for its CNPG-managed `grafana-app` Secret
# (synthesised by bp-cnpg via the chart's Cluster CR), and for upstream
# datasource endpoints to answer. Helm `--wait` would block on the
# Deployment rollout, which the HelmRelease cannot influence; runtime
# convergence is observed via kubectl rollout status.
---
apiVersion: v1
kind: Namespace
metadata:
name: grafana
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-grafana
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-grafana
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: grafana
targetNamespace: grafana
dependsOn:
- name: bp-cnpg
- name: bp-loki
- name: bp-mimir
- name: bp-tempo
- name: bp-keycloak
chart:
spec:
chart: bp-grafana
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-grafana
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,84 +0,0 @@
# bp-langfuse — Catalyst Blueprint #26 (W2.K2 Observability batch).
# Langfuse — LLM observability platform (traces, evaluations, prompt
# management, cost attribution). Hooks into the Catalyst LLM gateway
# (slot 40) once W2.K4 lands. CNPG-backed Postgres; Keycloak OIDC SSO.
#
# Wrapper chart: platform/langfuse/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cnpg, bp-keycloak, bp-cert-manager are all Ready.
#
# dependsOn:
# - bp-cnpg (slot 16) — Postgres backend for Langfuse traces /
# prompts / evaluations.
# - bp-keycloak (slot 09) — OIDC IdP for SSO.
# - bp-cert-manager (slot 02) — TLS for the Langfuse Ingress.
#
# disableWait: Langfuse waits for its CNPG-managed `langfuse-app` Secret
# and for upstream Bitnami subcharts to be filtered out at template time
# (the chart sets `postgresql.deploy=false`, `redis.deploy=false`,
# `clickhouse.deploy=false`, `s3.deploy=false` to route to bp-cnpg /
# bp-valkey / bp-clickhouse / bp-seaweedfs respectively). Helm `--wait`
# would block on the Deployment rollout, which the HelmRelease cannot
# influence.
#
# Forward-prep notice — issue #215 (bp-langfuse:1.0.0 GHCR publish 500):
# At the time this HR file was authored, bp-langfuse:1.0.0 had not
# published to oci://ghcr.io/openova-io due to a Helm v3.16 + GHCR
# manifest interaction with langfuse's nested OCI subchart deps. W1.G
# is the concurrent track fixing the publish path. Until that lands,
# this HelmRelease will fail to install with a chart-pull error; this
# is expected and tracked in #215. The HR file is committed now so
# the moment the artifact is published, Flux reconciles the SeaweedFS-
# /CNPG-/Keycloak-Ready Sovereign to bring Langfuse online without a
# second deploy gate.
---
apiVersion: v1
kind: Namespace
metadata:
name: langfuse
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-langfuse
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-langfuse
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: langfuse
targetNamespace: langfuse
dependsOn:
- name: bp-cnpg
- name: bp-keycloak
- name: bp-cert-manager
chart:
spec:
chart: bp-langfuse
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-langfuse
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,73 +0,0 @@
# bp-kyverno — Catalyst bootstrap-kit Blueprint #27 (W2.K3, Tier 7 — Security/Policy).
# Kubernetes-native admission policy engine. Validating/mutating/generating
# admission control via ClusterPolicy/Policy CRDs. HA mode with separate
# admission/background/cleanup/reports controllers. The first guardrail
# downstream Catalyst Apps land behind once the platform is bootstrapped.
#
# Wrapper chart: platform/kyverno/chart/ (umbrella over upstream
# kyverno/kyverno chart, Catalyst-curated values under the `kyverno:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Kyverno admission webhooks need a working CNI + Service
# mesh substrate to receive AdmissionReview requests from the apiserver.
# Cilium is the root of the Catalyst-Zero DAG; until it is Ready the
# apiserver→webhook path is not reachable and Kyverno install is racy.
#
# No further dependsOn: Kyverno installs its own CRDs and does not require
# cert-manager (it auto-generates admission webhook TLS via its built-in
# certificate controller).
---
apiVersion: v1
kind: Namespace
metadata:
name: kyverno
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-kyverno
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-kyverno
namespace: flux-system
spec:
interval: 15m
releaseName: kyverno
targetNamespace: kyverno
dependsOn:
- name: bp-cilium
chart:
spec:
chart: bp-kyverno
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-kyverno
namespace: flux-system
# Event-driven install: Kyverno HA mode brings up four controller
# Deployments (admission, background, cleanup, reports) plus the
# admission webhook TLS bootstrap. Pod Ready is multi-minute on a
# cold cluster; Helm `--wait` would hold the HR's Ready=True signal
# past the point where downstream HRs could legitimately reconcile.
# disableWait lets Flux mark this Ready as soon as manifests apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,67 +0,0 @@
# bp-reloader — Catalyst bootstrap-kit Blueprint #28 (W2.K3, Tier 7 — Security/Policy).
# Stakater Reloader watches ConfigMap/Secret changes and triggers rolling
# restarts of dependent Deployments/StatefulSets/DaemonSets that opt in
# via annotations. The secret/configmap-rotation glue across Catalyst —
# bp-* workloads pick up rotated TLS material and rotated bootstrap
# credentials without manual rollouts.
#
# Wrapper chart: platform/reloader/chart/ (umbrella over upstream
# stakater/reloader chart, Catalyst-curated values under the `reloader:`
# key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn: (none) — Reloader is independent infrastructure. It only
# watches Kubernetes API resources and triggers rollouts; it does not
# require any sibling Blueprint. Listed at slot 28 for numeric grouping
# with the security/policy cohort but Flux will install it as soon as
# the cluster is reachable.
---
apiVersion: v1
kind: Namespace
metadata:
name: reloader
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-reloader
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-reloader
namespace: flux-system
spec:
interval: 15m
releaseName: reloader
targetNamespace: reloader
chart:
spec:
chart: bp-reloader
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-reloader
namespace: flux-system
# Event-driven install (Catalyst convention) — Reloader's single
# Deployment Ready path is fast in practice but disableWait keeps the
# HR Ready signal aligned with manifest apply rather than runtime
# convergence, matching the rest of the bootstrap-kit.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,66 +0,0 @@
# bp-vpa — Catalyst bootstrap-kit Blueprint #29 (W2.K3, Tier 7 — Security/Policy).
# Vertical Pod Autoscaler. Per-host-cluster automated resource right-sizing
# (Recommender + Updater + Admission Controller). Pairs with HPA/KEDA on
# the horizontal axis. Provides recommendations even if not auto-applying;
# Catalyst-default mode is `Off` (recommend only) until SRE opts a
# workload into `Auto` via VerticalPodAutoscaler resources.
#
# Wrapper chart: platform/vpa/chart/ (umbrella over upstream
# autoscaler/vertical-pod-autoscaler chart, Catalyst-curated values under
# the `vertical-pod-autoscaler:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn: (none) — VPA is independent infrastructure. The Recommender
# reads from metrics-server (a k3s fixture) and Prometheus/Mimir when
# present, but neither is a hard sibling-Blueprint dep at install time;
# the controllers tolerate metrics endpoints appearing late. Listed at
# slot 29 for numeric grouping with the security/policy cohort.
---
apiVersion: v1
kind: Namespace
metadata:
name: vpa
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-vpa
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-vpa
namespace: flux-system
spec:
interval: 15m
releaseName: vpa
targetNamespace: vpa
chart:
spec:
chart: bp-vpa
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-vpa
namespace: flux-system
# Event-driven install: VPA brings up three Deployments (recommender,
# updater, admission-controller) plus admission webhook TLS bootstrap.
# disableWait keeps Flux's Ready signal aligned with manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-trivy — Catalyst bootstrap-kit Blueprint #30 (W2.K3, Tier 7 — Security/Policy).
# Trivy Operator. Static-scanning half of the Catalyst security stack:
# vulnerability + misconfiguration scanning of running workloads, images,
# RBAC, and rendered manifests. Pairs with bp-falco (runtime, slot 31)
# and bp-kyverno (admission, slot 27).
#
# Wrapper chart: platform/trivy/chart/ (umbrella over upstream
# aquasecurity/trivy-operator chart, Catalyst-curated values under the
# `trivy-operator:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — Trivy Operator's admission webhook (and its
# ConfigAuditReport mutating-webhook in HA mode) requires a TLS cert
# from the cluster's letsencrypt-prod / internal CA ClusterIssuer
# before the apiserver will route AdmissionReview traffic. Without
# bp-cert-manager Ready, the Certificate resource sits Pending and
# the webhook serves stale or no certs.
---
apiVersion: v1
kind: Namespace
metadata:
name: trivy-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-trivy
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-trivy
namespace: flux-system
spec:
interval: 15m
releaseName: trivy
targetNamespace: trivy-system
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-trivy
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-trivy
namespace: flux-system
# Event-driven install: Trivy Operator pulls a multi-hundred-MB
# vulnerability database on first run; pod Ready is dominated by
# initial DB hydration, not manifest apply. disableWait lets Flux
# mark this Ready as soon as manifests apply; runtime convergence
# (DB hydration, first scan reports landing) is observed via kubectl.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,69 +0,0 @@
# bp-falco — Catalyst bootstrap-kit Blueprint #31 (W2.K3, Tier 7 — Security/Policy).
# Runtime threat detection (CNCF Graduated). eBPF kernel-level syscall
# monitoring for container escapes, privilege escalation, anomalous
# behavior. Runs as a DaemonSet on every host of the Sovereign;
# Falcosidekick fans events into the SIEM pipeline (Loki/JetStream).
#
# Wrapper chart: platform/falco/chart/ (umbrella over upstream
# falcosecurity/falco chart, Catalyst-curated values under the `falco:`
# key — modern_ebpf driver, falcosidekick enabled).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Falco's modern_ebpf probe loads alongside Cilium's
# eBPF programs on the same kernel hook points. Bringing Falco up
# before the CNI is finalised has produced flaky probe-load races
# in field testing; sequencing after bp-cilium Ready avoids that.
---
apiVersion: v1
kind: Namespace
metadata:
name: falco
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-falco
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-falco
namespace: flux-system
spec:
interval: 15m
releaseName: falco
targetNamespace: falco
dependsOn:
- name: bp-cilium
chart:
spec:
chart: bp-falco
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-falco
namespace: flux-system
# Event-driven install: Falco's DaemonSet rolls out per-node and the
# eBPF probe load is sensitive to kernel headers / module presence.
# Per-node Ready is properly observed via DaemonSet status, not via
# Helm `--wait`. disableWait keeps Flux's signal aligned with
# manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-sigstore — Catalyst bootstrap-kit Blueprint #32 (W2.K3, Tier 7 — Security/Policy).
# Sigstore Policy Controller. Admission gate for signed-image enforcement —
# verifies Cosign signatures + attestations on container images before
# admission. Pairs with bp-harbor (registry, slot 19) and the CI-side
# Cosign signing path to close the supply-chain trust loop on the
# Sovereign.
#
# Wrapper chart: platform/sigstore/chart/ (umbrella over upstream
# sigstore/policy-controller chart, Catalyst-curated values under the
# `policy-controller:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — Policy Controller is an admission webhook; the
# apiserver requires a valid TLS cert on the webhook Service before
# it will route AdmissionReview traffic. Without bp-cert-manager
# Ready, the Certificate sits Pending and admission fails open or
# fails closed (depending on FailurePolicy), neither of which is
# the intended security posture.
---
apiVersion: v1
kind: Namespace
metadata:
name: sigstore-system
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-sigstore
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-sigstore
namespace: flux-system
spec:
interval: 15m
releaseName: sigstore
targetNamespace: sigstore-system
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-sigstore
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-sigstore
namespace: flux-system
# Event-driven install: Policy Controller bootstraps its admission
# webhook TLS via cert-manager and reaches Ready only after the
# Certificate is issued + bound. disableWait avoids holding the HR
# signal on a runtime-convergence event.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-syft-grype — Catalyst bootstrap-kit Blueprint #33 (W2.K3, Tier 7 — Security/Policy).
# Anchore Syft + Grype as a scheduled CronJob. SBOM generation (Syft)
# paired with vulnerability matching (Grype) — the offline / scheduled
# half of the supply-chain stack. Anchore does not publish a Helm chart
# for the open-source CLIs, so this Blueprint is a scratch chart that
# wires the official ghcr.io/anchore/syft and ghcr.io/anchore/grype
# containers into a CronJob that scans the Sovereign's image inventory.
#
# Wrapper chart: platform/syft-grype/chart/ (Catalyst-authored scratch
# chart — no upstream subchart).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — the result-export sidecar publishes SBOMs over
# mTLS to the central scan-result store; cert-manager issues the
# workload's TLS material via the cluster's ClusterIssuer.
---
apiVersion: v1
kind: Namespace
metadata:
name: syft-grype
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-syft-grype
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-syft-grype
namespace: flux-system
spec:
interval: 15m
releaseName: syft-grype
targetNamespace: syft-grype
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-syft-grype
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-syft-grype
namespace: flux-system
# Event-driven install: the Blueprint is mostly a CronJob + RBAC
# surface. There is no long-running Deployment whose Ready=True is
# meaningful — disableWait is the correct shape so Flux marks Ready
# as soon as manifests apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,72 +0,0 @@
# bp-velero — Catalyst bootstrap-kit Blueprint #34 (W2.K3, Tier 7 — Security/Policy).
# Per-host-cluster backup engine. Catalyst-Zero pins backups to SeaweedFS
# (the unified S3 layer, slot 18) so backup data never leaves the
# Sovereign at install time; per-Sovereign archival to a cloud backend
# is wired in post-bootstrap via Crossplane.
#
# Wrapper chart: platform/velero/chart/ (umbrella over upstream
# vmware-tanzu/velero chart, Catalyst-curated values under the `velero:`
# key — `seaweedfs` BackupStorageLocation provider, no cloud plugin
# pinned at install time).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-seaweedfs — Velero's BackupStorageLocation points at the
# in-cluster SeaweedFS S3 endpoint (`seaweedfs.seaweedfs.svc:8333`)
# and reads the `seaweedfs-s3-credentials` Secret SeaweedFS renders
# during install. Without bp-seaweedfs Ready, the BSL Phase sits
# `Unavailable` and Velero's first reconcile fails — every backup
# CR queues with the same error until the dep lands.
---
apiVersion: v1
kind: Namespace
metadata:
name: velero
labels:
catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-velero
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-velero
namespace: flux-system
spec:
interval: 15m
releaseName: velero
targetNamespace: velero
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-velero
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-velero
namespace: flux-system
# Event-driven install: Velero's Deployment Ready depends on the
# node-agent DaemonSet rolling out and the BackupStorageLocation
# reaching `Available` — both runtime-convergence events that Flux
# observes via the BSL CR phase, not via Helm `--wait`. disableWait
# keeps the HR's Ready signal aligned with manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,70 +0,0 @@
# bp-coraza — Catalyst bootstrap-kit Blueprint #35 (W2.K4 — Tier 8: edge).
# OWASP-licensed Web Application Firewall, ModSecurity-rule-compatible.
# Speaks the HAProxy SPOE protocol; sits in front of Cilium Gateway / HAProxy
# fronts to enforce WAF policies on inbound traffic to Sovereign-facing
# services (keycloak, grafana, stalwart, marketplace).
#
# Wrapper chart: platform/coraza/chart/
# Catalyst-curated values: platform/coraza/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Coraza enforces L7 policy via Cilium L7 proxy / Gateway
# API; Cilium must be Ready (CNI + Gateway controller) before WAF
# evaluation hooks become reachable.
# - bp-cert-manager — Issuers must be reconciled so any TLS-fronted SPOA
# listeners (per-Sovereign overlays) come up with valid certs.
#
# install/upgrade.disableWait: true — Coraza-spoa Deployment Ready signal
# is event-driven via the Flux dependsOn graph (downstream HRs check
# Ready=True on this HR). Per session-2026-04-30 architectural decision,
# we never use blanket `spec.timeout: Nm` watchdogs.
---
apiVersion: v1
kind: Namespace
metadata:
name: coraza
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-coraza
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-coraza
namespace: flux-system
spec:
interval: 15m
releaseName: coraza
targetNamespace: coraza
dependsOn:
- name: bp-cilium
- name: bp-cert-manager
chart:
spec:
chart: bp-coraza
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-coraza
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -15,28 +15,4 @@ resources:
- 08-openbao.yaml
- 09-keycloak.yaml
- 10-gitea.yaml
- 11-powerdns.yaml
- 12-external-dns.yaml
- 13-bp-catalyst-platform.yaml
- 14-crossplane-claims.yaml
- 15-external-secrets.yaml
- 16-cnpg.yaml
- 17-valkey.yaml
- 18-seaweedfs.yaml
- 19-harbor.yaml
- 20-opentelemetry.yaml
- 21-alloy.yaml
- 22-loki.yaml
- 23-mimir.yaml
- 24-tempo.yaml
- 25-grafana.yaml
- 26-langfuse.yaml
- 27-kyverno.yaml
- 28-reloader.yaml
- 29-vpa.yaml
- 30-trivy.yaml
- 31-falco.yaml
- 32-sigstore.yaml
- 33-syft-grype.yaml
- 34-velero.yaml
- 35-coraza.yaml
- 11-bp-catalyst-platform.yaml

View File

@ -1,19 +1,10 @@
# Shared Sovereign Flux Kustomization root.
# Per-Sovereign Flux Kustomization root.
#
# Issue #218: this directory is the canonical bootstrap tree for EVERY
# Sovereign. The Sovereign's FQDN is interpolated into the manifests at
# Flux apply time via the cloud-init Kustomization's
# `postBuild.substitute.SOVEREIGN_FQDN` envsubst hook (see
# infra/hetzner/cloudinit-control-plane.tftpl). No per-Sovereign copy
# of this tree is committed to the repo before provisioning.
#
# The Sovereign's k3s control plane (cloud-init bootstrap) installs
# Flux core, then applies a GitRepository selecting this _template
# tree (`!/clusters/_template`) plus two Kustomizations whose `path`
# fields point at `clusters/_template/bootstrap-kit` and
# `clusters/_template/infrastructure`. Flux's envsubst replaces
# `${SOVEREIGN_FQDN}` in the rendered manifests with the Sovereign's
# FQDN. From there Flux owns everything.
# Copied from clusters/_template/ → clusters/<sovereign-fqdn>/ at provisioning
# time, with SOVEREIGN_FQDN_PLACEHOLDER substituted. The Sovereign's k3s
# control plane (cloud-init bootstrap) installs Flux core, then applies a
# GitRepository pointing at this Sovereign's directory. From there Flux owns
# everything.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

View File

@ -5,13 +5,13 @@
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
# kube-system is built into every Kubernetes cluster — never re-declare it.
# Earlier revisions of 01-cilium.yaml AND 05-sealed-secrets.yaml both
# declared it, which collided when kustomize tried to merge the two:
# "may not add resource with an already registered id:
# Namespace.v1.[noGrp]/kube-system.[noNs]"
# This Blueprint installs Cilium INTO kube-system; the HelmRelease's
# targetNamespace field below is sufficient.
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-cilium
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -36,40 +34,14 @@ spec:
chart:
spec:
chart: bp-cilium
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cilium
namespace: flux-system
# Event-driven install: Helm completes when manifests apply, not when
# cilium-agent reaches Ready (agent waits for envoyconfig CRDs that the
# SAME chart installs — legitimate slow-Ready). Replaces blanket
# spec.timeout: 15m band-aid from PR #221.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cilium:
# Enable L7 proxy so Cilium's chart installs the
# ciliumenvoyconfigs / ciliumclusterwideenvoyconfigs CRDs that the
# cilium-agent waits for at startup. Without this, agent crash-loops
# forever and the node.cilium.io/agent-not-ready taint never lifts.
l7Proxy: true
prometheus:
enabled: false
serviceMonitor:
enabled: false
hubble:
metrics:
enabled: null
serviceMonitor:
enabled: false
relay:
enabled: false
ui:
enabled: false

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-cert-manager
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,27 +36,14 @@ spec:
chart:
spec:
chart: bp-cert-manager
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cert-manager
namespace: flux-system
# Event-driven install: cert-manager installs CRDs + 3 deployments
# (controller, webhook, cainjector). Webhook readiness depends on the
# cainjector mutating the Secret — multi-minute path on cold start.
# Helm install completes when manifests apply; subsequent dependsOn
# checks Ready=True independently. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cert-manager:
prometheus:
enabled: false
servicemonitor:
enabled: false

View File

@ -3,28 +3,7 @@
# Wrapper chart: platform/flux/chart/
# Catalyst-curated values: platform/flux/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# DOUBLE-INSTALL SAFETY (omantel.omani.works incident, 2026-04-29)
# ----------------------------------------------------------------
# Cloud-init pre-installs Flux core via
# curl https://github.com/fluxcd/flux2/releases/download/v2.4.0/install.yaml
# so that this very HelmRelease can be reconciled. helm-controller then
# runs `helm install` for bp-flux on top of the already-running Flux. If
# the chart's subchart `flux2` version disagrees with the cloud-init
# install (different upstream Flux release), CRD `storedVersions`
# mismatches → Helm install fails → rollback → rollback DELETES the
# running Flux controllers → cluster has no GitOps engine and is
# unrecoverable in-place.
#
# Mitigations applied here:
# 1. bp-flux:1.1.2 pins the `flux2` subchart at 2.14.1 (= appVersion
# 2.4.0) which matches cloud-init's v2.4.0 install.yaml.
# 2. spec.upgrade.preserveValues: true — never silently overwrite
# operator overlays on upgrade.
# controller adopts the cloud-init-installed objects rather than
# re-creating, so install is non-destructive when objects already
# exist with matching apiVersion/kind/name.
# See docs/RUNBOOK-PROVISIONING.md §"bp-flux double-install".
---
apiVersion: v1
kind: Namespace
@ -41,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-flux
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -59,30 +36,14 @@ spec:
chart:
spec:
chart: bp-flux
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-flux
namespace: flux-system
# Event-driven install: bp-flux adopts the cloud-init-installed Flux
# controllers; the helm-controller pod that reconciles THIS HR is itself
# a target of the chart, so blocking on Ready=True is structurally
# impossible. disableWait avoids the deadlock. Replaces PR #221 timeout.
install:
disableWait: true
# Adopt cloud-init-installed Flux objects rather than fail on
# ownership conflict (the objects exist before the HelmRelease ever
# reconciles). Without this, the very first reconcile would error
# with "object already exists" on every Flux controller Deployment.
remediation:
retries: 3
upgrade:
disableWait: true
# Keep operator-supplied values (e.g. resource overrides applied via
# helm-controller out-of-band, or dry-run patches during incident
# response) on chart upgrades. Without this, every upgrade would
# reset the chart to default values, masking operator state.
preserveValues: true
# Match install behaviour — adopt rather than fail on conflict.
remediation:
retries: 3

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-crossplane
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -31,7 +29,6 @@ metadata:
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: crossplane
targetNamespace: crossplane-system
dependsOn:
@ -39,7 +36,7 @@ spec:
chart:
spec:
chart: bp-crossplane
version: 1.1.3
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-crossplane

View File

@ -5,9 +5,13 @@
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
# kube-system is built into every Kubernetes cluster — never re-declare it.
# See 01-cilium.yaml for the full incident note. Sealed-Secrets installs
# INTO kube-system; the HelmRelease's targetNamespace below is sufficient.
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
@ -16,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-sealed-secrets
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -34,18 +36,14 @@ spec:
chart:
spec:
chart: bp-sealed-secrets
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-sealed-secrets
namespace: flux-system
# Event-driven install: single-replica controller + CRD; install
# completes when manifests apply. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-spire
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,23 +36,14 @@ spec:
chart:
spec:
chart: bp-spire
version: 1.1.4
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-spire
namespace: flux-system
# Event-driven install: Helm completes when manifests apply, not when
# pods reach Ready. spire-server StatefulSet has a multi-minute Ready
# path (controller-manager waits for CRD informer cache sync, which is
# itself triggered by the spire-crds subchart's CRD install). Flux's
# `dependsOn` on downstream HRs (bp-nats-jetstream, bp-openbao) checks
# Ready=True on this HR independently, so disableWait is the correct
# shape — replaces the blanket spec.timeout: 15m band-aid from PR #221.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-nats-jetstream
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,20 +36,14 @@ spec:
chart:
spec:
chart: bp-nats-jetstream
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-nats-jetstream
namespace: flux-system
# Event-driven install: NATS StatefulSet with JetStream raft initialisation
# — quorum formation across N replicas is legitimately multi-minute on
# cold start. Helm install completes when manifests apply; downstream
# dependsOn checks Ready=True independently. Replaces PR #221 timeout.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-openbao
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,21 +36,14 @@ spec:
chart:
spec:
chart: bp-openbao
version: 1.1.1
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-openbao
namespace: flux-system
# Event-driven install: OpenBao 3-node Raft cluster requires manual
# unseal via `bao operator init` — pods stay sealed (Ready=False) until
# an operator runs the unseal flow. Blocking Helm install on Ready=True
# is structurally wrong for a sealed-by-default secret backend.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -20,9 +20,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-keycloak
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -38,21 +36,14 @@ spec:
chart:
spec:
chart: bp-keycloak
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-keycloak
namespace: flux-system
# Event-driven install: Keycloak DB schema migration + realm import is
# legitimately multi-minute on first install (PostgreSQL backend +
# 100+ Liquibase changesets). Helm install completes when manifests
# apply; downstream dependsOn checks Ready=True independently.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -21,9 +21,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-gitea
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -39,21 +37,15 @@ spec:
chart:
spec:
chart: bp-gitea
version: 1.1.2
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-gitea
namespace: flux-system
# Event-driven install: Gitea PostgreSQL DB init + admin user creation +
# public Blueprint catalog mirror seeding is legitimately multi-minute.
# Helm install completes when manifests apply; downstream dependsOn
# checks Ready=True independently. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:

View File

@ -1,4 +1,4 @@
# bp-catalyst-platform — Catalyst Blueprint #13 of 13. The umbrella
# bp-catalyst-platform — Catalyst Blueprint #11 of 11. The umbrella
# Blueprint that brings up the Catalyst control plane: console, marketplace,
# admin, catalog-svc, projector, provisioning, environment-controller,
# blueprint-controller, billing.
@ -25,9 +25,7 @@ metadata:
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
url: oci://ghcr.io/openova-io/bp-catalyst-platform
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@ -43,22 +41,15 @@ spec:
chart:
spec:
chart: bp-catalyst-platform
version: 1.1.8
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-catalyst-platform
namespace: flux-system
# Event-driven install: umbrella chart deploys ~10 Catalyst services
# (console, marketplace, admin, catalog-svc, projector, provisioning,
# environment-controller, blueprint-controller, billing). Inter-service
# readiness via OTel/NATS subjects is multi-minute and not Helm's
# concern. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
# Per-Sovereign overrides for the umbrella — sovereign-FQDN-derived hostnames

View File

@ -1,104 +0,0 @@
# bp-powerdns — Catalyst Blueprint #11 of 13. Per-Sovereign authoritative
# DNS. Every Sovereign owns its own PowerDNS Authoritative server with the
# Sovereign's zones loaded into a CNPG-backed gpgsql backend; per-zone
# DNSSEC (ECDSAP256SHA256), lua-records for geo + health-checked failover,
# and a dnsdist companion for query rate-limiting + DDoS posture.
#
# Architectural anchor — per-Sovereign PowerDNS, NOT a shared instance:
# This file was added so each Sovereign has its own bp-powerdns release
# alongside bp-external-dns (#12). The original Phase-0 dial-tone
# PowerDNS used to live as a singleton in the openova-system namespace
# on contabo-mkt; #168 ("per-Sovereign PowerDNS zones") flipped that
# model so a Sovereign's zones never leave the Sovereign's own cluster.
# bp-external-dns's `dependsOn: [bp-powerdns]` therefore must be
# satisfied by an in-cluster sibling release — this file IS that
# sibling.
#
# Wrapper chart: platform/powerdns/chart/
# Catalyst-curated values: platform/powerdns/chart/values.yaml
# - 3 PowerDNS Authoritative replicas behind dnsdist
# - CNPG-managed `pdns-pg` Postgres cluster for zone storage
# - DNSSEC ON by default (ECDSAP256SHA256)
# - Lua-records ON (geo + health-checked failover patterns)
# - REST API at pdns.<sovereign>/api behind Traefik basicAuth
#
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — the REST API ingress requests a TLS cert from
# the cluster's letsencrypt-prod ClusterIssuer; without bp-cert-manager
# reconciled first the Certificate resource sits Pending.
#
# Hard-but-implicit dependencies (CRDs, NOT sibling Blueprints):
# - postgresql.cnpg.io/v1.Cluster — provided by the CNPG operator. The
# chart's templates/cnpg-cluster.yaml renders this CR; if CNPG isn't
# yet installed on the Sovereign, the HelmRelease will wait. CNPG is
# a fixture of Catalyst-Zero (FABRIC group, componentGroups.ts `cnpg`)
# and is installed alongside this kit by the bootstrap installer.
# - traefik.io/v1alpha1.Middleware — Traefik is the Catalyst-Zero
# ingress controller and is a fixture of every Sovereign.
#
# Per-Sovereign overrides intentionally NOT set here:
# - Zones, API basic-auth credentials, anycast Floating-IP target — all
# of those are provisioned by Crossplane / pool-domain-manager (PDM)
# after the cluster comes up, NOT baked into the bootstrap kit. This
# file installs the chart with defaults so the Sovereign has a working
# authoritative DNS surface that PDM can then load zones into.
---
apiVersion: v1
kind: Namespace
metadata:
name: powerdns
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-powerdns
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-powerdns
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: powerdns
targetNamespace: powerdns
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-powerdns
version: 1.1.3
sourceRef:
kind: HelmRepository
name: bp-powerdns
namespace: flux-system
# disableWait: a Sovereign without bp-cnpg yet reconciled has no
# `pdns-pg-app` Secret (the chart's CNPG Cluster template is gated
# behind the `postgresql.cnpg.io/v1` CRD via Capabilities.APIVersions
# check — see chart/templates/cnpg-cluster.yaml). Without disableWait,
# Helm's `--wait` would hold until the powerdns Deployment is Ready,
# which can't happen until CNPG comes up and synthesises the Secret.
# The HelmRelease itself reports Ready as soon as the manifests apply
# cleanly; runtime convergence (powerdns pods becoming Ready once
# CNPG lands) is observed via kubectl, not gated on Helm.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,80 +0,0 @@
# bp-external-dns — Catalyst Blueprint #12 of 13. Per-Sovereign DNS sync —
# ExternalDNS reconciles Service/Ingress hostnames into the per-Sovereign
# PowerDNS authoritative server via the native `pdns` provider. Geo +
# health-checked failover responses are owned by PowerDNS lua-records,
# NOT by ExternalDNS.
#
# Wrapper chart: platform/external-dns/chart/
#
# dependsOn:
# - bp-cert-manager — ExternalDNS HelmRelease only after TLS issuers
# are reconciled, so any cert-manager-fronted webhook endpoints in
# downstream overlays come up cleanly.
# - bp-powerdns — native `pdns` provider points at the in-cluster
# bp-powerdns Service and reads the `powerdns-api-credentials` Secret
# it renders. Without bp-powerdns the ExternalDNS pod CrashLoops
# trying to dial a non-existent DNS API.
---
apiVersion: v1
kind: Namespace
metadata:
name: external-dns
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-external-dns
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-external-dns
namespace: flux-system
spec:
interval: 15m
releaseName: external-dns
targetNamespace: external-dns
dependsOn:
- name: bp-cert-manager
- name: bp-powerdns
chart:
spec:
chart: bp-external-dns
version: 1.1.2
sourceRef:
kind: HelmRepository
name: bp-external-dns
namespace: flux-system
# Event-driven install: ExternalDNS pod readiness depends on a
# successful initial reconcile against the per-Sovereign PowerDNS API
# (which itself stabilises after pdns-pg CNPG bootstraps) — legitimate
# slow-Ready cascade. Helm install completes when manifests apply.
# Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
# Per-Sovereign overrides — txtOwnerId MUST be the Sovereign FQDN so two
# Sovereigns sharing a parent zone don't fight over the same record set.
# domainFilters narrow the zones ExternalDNS will manage; per-Sovereign
# cluster overlays patch this with the actual zone list.
values:
external-dns:
txtOwnerId: omantel.omani.works
txtPrefix: _externaldns.
domainFilters:
- omantel.omani.works

View File

@ -1,58 +0,0 @@
# bp-crossplane-claims — Catalyst Day-2 CRUD XRDs (compose.openova.io/v1alpha1)
# + default Hetzner-backed Compositions. Split out of bp-crossplane at
# release 1.1.3 to resolve intra-chart CRD-ordering: a single Helm release
# cannot install a CRD AND a CR of that CRD's kind in one apply pass —
# the apiserver rejects the CR because the CRD is not yet registered.
#
# Wrapper chart: platform/crossplane-claims/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-crossplane is Ready (dependsOn below) — at which point
# the apiextensions.crossplane.io/v1 CRDs that the upstream
# crossplane subchart registers are live.
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-crossplane-claims
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-crossplane-claims
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: crossplane-claims
targetNamespace: crossplane-system
# bp-crossplane installs the apiextensions.crossplane.io/v1 CRDs
# (CompositeResourceDefinition, Composition) via the upstream subchart's
# CRD payload. We MUST wait until that HelmRelease reports Ready=True
# before applying the XRDs+Compositions in this chart, otherwise the
# apiserver returns:
# no matches for kind "CompositeResourceDefinition" in version
# "apiextensions.crossplane.io/v1" -- ensure CRDs are installed first
dependsOn:
- name: bp-crossplane
chart:
spec:
chart: bp-crossplane-claims
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-crossplane-claims
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3

View File

@ -1,75 +0,0 @@
# bp-external-secrets — Catalyst bootstrap-kit Blueprint, W2.K1 slot 15.
# External Secrets Operator (ESO). Day-2 secret pipeline from OpenBao →
# Kubernetes Secret materialization. Takes over from bp-sealed-secrets
# (slot 05, transient) once OpenBao is Ready and ESO is reconciling.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §1.2 (Tier 0/3 — host-layer
# secret distribution) and §2.3 (W2.K1 dependency graph): ESO is sequenced
# at slot 15 (first slot in the W2.K1 contiguous range) but its
# dependsOn is [bp-openbao(08), bp-cert-manager(02)] — Flux gates install
# on Ready=True for both, regardless of slot order. ESO's webhook
# requests a TLS cert from the cluster's letsencrypt-prod / cluster-ca
# ClusterIssuer; without bp-cert-manager Ready, the Certificate stays
# Pending and the operator never reaches Running.
#
# Wrapper chart: platform/external-secrets/chart/
# Catalyst-curated values: platform/external-secrets/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: external-secrets-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-external-secrets
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-external-secrets
namespace: flux-system
spec:
interval: 15m
releaseName: external-secrets
targetNamespace: external-secrets-system
# ESO depends on:
# - bp-openbao(08): the secret backend ESO pulls from. ClusterSecretStore
# resources reference the in-cluster OpenBao service; without OpenBao
# Ready those CRs land but every ExternalSecret reconcile fails.
# - bp-cert-manager(02): ESO's admission webhook needs a TLS cert.
# bp-cert-manager provides the ClusterIssuer that signs it.
dependsOn:
- name: bp-openbao
- name: bp-cert-manager
chart:
spec:
chart: bp-external-secrets
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-external-secrets
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3 (Flux
# dependsOn is the gate, not Helm timeout). Replaces blanket
# spec.timeout: 15m band-aid pattern from PR #221, removed in PR #250.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-cnpg — Catalyst bootstrap-kit Blueprint, W2.K1 slot 16.
# CloudNativePG operator. Per-Sovereign Postgres-as-a-service via
# postgresql.cnpg.io/v1.Cluster CRs. Required by every PG-backed bootstrap
# component: bp-powerdns (pdns-pg-app), bp-keycloak HA, bp-gitea metadata,
# bp-langfuse, bp-grafana config DB, bp-temporal, bp-matrix synapse,
# bp-llm-gateway, bp-bge, bp-nemo-guardrails, bp-openmeter (cnpg profile),
# pool-domain-manager (PDM).
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) — cnpg gates the entire downstream observability/AI/relay
# stack; placing it in the bootstrap-kit (instead of as an Application
# Blueprint) is what unblocks the omantel-1 day-1 control plane.
#
# Resolves issue #254 (bp-powerdns pod stuck in CreateContainerConfigError
# because pdns-pg-app Secret is generated by a CNPG Cluster CR; without
# the operator the secret never materializes).
#
# Wrapper chart: platform/cnpg/chart/
# Catalyst-curated values: platform/cnpg/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: cnpg-system
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-cnpg
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-cnpg
namespace: flux-system
spec:
interval: 15m
releaseName: cnpg
targetNamespace: cnpg-system
# CNPG only needs Flux Ready (its own CRDs ship in the same chart;
# consumers of postgresql.cnpg.io/v1.Cluster gate themselves on bp-cnpg).
dependsOn:
- name: bp-flux
chart:
spec:
chart: bp-cnpg
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-cnpg
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,62 +0,0 @@
# bp-valkey — Catalyst bootstrap-kit Blueprint, W2.K1 slot 17.
# Redis-compatible cache (Valkey is the BSD-licensed Redis fork). Used by
# Catalyst control-plane services for ephemeral session/state, and by
# downstream Apps that need a Redis wire-protocol cache.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) — Valkey runs alongside CNPG so Apps can pick the right
# storage shape per workload (relational vs cache).
#
# Wrapper chart: platform/valkey/chart/
# Catalyst-curated values: platform/valkey/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: valkey
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-valkey
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-valkey
namespace: flux-system
spec:
interval: 15m
releaseName: valkey
targetNamespace: valkey
# Valkey is a self-contained cache — only needs Flux Ready.
dependsOn:
- name: bp-flux
chart:
spec:
chart: bp-valkey
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-valkey
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-seaweedfs — Catalyst bootstrap-kit Blueprint, W2.K1 slot 18.
# S3-compatible object storage encapsulation. Pre-requisite for every
# blob-storage consumer in the bootstrap-kit:
# - bp-velero (slot 34) — backups
# - bp-loki (slot 22) — log chunks
# - bp-mimir (slot 23) — metric blocks
# - bp-tempo (slot 24) — trace blocks
# - bp-harbor (slot 19) — registry blob storage
# and downstream Apps that need cheap S3-compatible blob storage.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §2.3 (Tier 5 storage+DB
# foundation) and PROVISIONING-PLAN.md §3 — every Sovereign owns its own
# SeaweedFS so blob data never leaves the Sovereign's cluster.
#
# Wrapper chart: platform/seaweedfs/chart/
# Catalyst-curated values: platform/seaweedfs/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: seaweedfs
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-seaweedfs
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-seaweedfs
namespace: flux-system
spec:
interval: 15m
releaseName: seaweedfs
targetNamespace: seaweedfs
# SeaweedFS depends on:
# - bp-flux(03): standard GitOps prerequisite.
# - bp-cert-manager(02): the S3 Filer endpoint terminates TLS via a
# Certificate the chart requests from the cluster ClusterIssuer.
dependsOn:
- name: bp-flux
- name: bp-cert-manager
chart:
spec:
chart: bp-seaweedfs
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-seaweedfs
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,69 +0,0 @@
# bp-harbor — Catalyst bootstrap-kit Blueprint, W2.K1 slot 19.
# Per-Sovereign OCI registry. Mirrors blueprint chart artifacts and
# container images so the Sovereign isn't dependent on ghcr.io for
# day-2 image pulls; also hosts Org-private images per Application.
#
# Per docs/BOOTSTRAP-KIT-EXPANSION-PLAN.md §6.7 — Harbor sits in the
# storage cohort (W2.K1) rather than apps cohort because it is a
# consumer of CNPG (registry metadata DB) and SeaweedFS (blob backend),
# and its presence gates Cosign signing in bp-sigstore (slot 32) and
# image pinning across all later HRs.
#
# Wrapper chart: platform/harbor/chart/
# Catalyst-curated values: platform/harbor/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: harbor
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-harbor
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-harbor
namespace: flux-system
spec:
interval: 15m
releaseName: harbor
targetNamespace: harbor
# Harbor depends on:
# - bp-cnpg(16): registry metadata DB (postgresql.cnpg.io/v1.Cluster).
# - bp-seaweedfs(18): registry blob backend (S3-compatible).
# - bp-cert-manager(02): registry endpoint TLS via ClusterIssuer.
dependsOn:
- name: bp-cnpg
- name: bp-seaweedfs
- name: bp-cert-manager
chart:
spec:
chart: bp-harbor
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-harbor
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,74 +0,0 @@
# bp-opentelemetry — Catalyst Blueprint #20 (W2.K2 Observability batch).
# OpenTelemetry Collector — pipeline source for the LGTM stack
# (Loki / Mimir / Tempo). Receives OTLP from workloads and fans out
# logs → Loki, metrics → Mimir, traces → Tempo.
#
# Wrapper chart: platform/opentelemetry/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cert-manager is Ready (the Collector's webhook /
# OTLP-HTTPS receiver request TLS certs from the cluster
# ClusterIssuer).
#
# dependsOn:
# - bp-cert-manager (slot 02) — TLS for OTLP-HTTPS receiver and any
# Collector webhooks.
#
# Hard-but-implicit dependencies (CRDs, NOT sibling Blueprints):
# - opentelemetry.io/v1beta1.OpenTelemetryCollector — provided by the
# OpenTelemetry Operator subchart bundled in this Blueprint.
#
# disableWait: the OTel Collector chart deploys multiple components
# (operator, collector DaemonSet/Deployment, instrumentation CR). Helm
# `--wait` would block on every Pod becoming Ready, which can't happen
# until downstream backends (Loki/Mimir/Tempo, slots 2224) are up to
# accept exports. The HelmRelease itself reports Ready as soon as
# manifests apply cleanly; runtime convergence is observed via kubectl.
---
apiVersion: v1
kind: Namespace
metadata:
name: opentelemetry
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-opentelemetry
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-opentelemetry
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: opentelemetry
targetNamespace: opentelemetry
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-opentelemetry
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-opentelemetry
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-alloy — Catalyst Blueprint #21 (W2.K2 Observability batch).
# Grafana Alloy — the unified telemetry collector for the LGTM stack.
# Runs as a DaemonSet on every node; tails container logs, scrapes
# Prometheus metrics, and forwards traces. Co-resident with bp-opentelemetry
# (slot 20) — Alloy handles host-level collection (kubelet, journald,
# node_exporter) while OTel handles app-level OTLP.
#
# Wrapper chart: platform/alloy/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-opentelemetry is Ready (Alloy's default config
# forwards OTLP to the Collector's gRPC endpoint).
#
# dependsOn:
# - bp-opentelemetry (slot 20) — Alloy forwards OTLP to the Collector.
# Without the Collector Service in place, Alloy retries forever on a
# non-existent upstream.
#
# disableWait: Alloy is a DaemonSet — Helm `--wait` would block on
# every node's Alloy Pod becoming Ready. On larger Sovereigns this can
# legitimately take >5min during a cold-start image pull; the HelmRelease
# reports Ready when manifests apply, runtime convergence observed via
# kubectl.
---
apiVersion: v1
kind: Namespace
metadata:
name: alloy
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-alloy
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-alloy
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: alloy
targetNamespace: alloy
dependsOn:
- name: bp-opentelemetry
chart:
spec:
chart: bp-alloy
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-alloy
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-loki — Catalyst Blueprint #22 (W2.K2 Observability batch).
# Grafana Loki — log storage tier of the LGTM stack. Default deployment
# shape is SingleBinary (one Loki StatefulSet) — minimum operational
# cost for a Solo Sovereign; per-Sovereign overlays scale to
# distributor/ingester/querier StatefulSets when load warrants.
#
# Wrapper chart: platform/loki/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Loki chunks/index live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Loki uses SeaweedFS S3 for chunks and
# index storage. Without SeaweedFS Ready, Loki cannot persist logs.
#
# disableWait: Loki SingleBinary becomes Ready only after creating the
# S3 bucket and writing the first WAL block — both of which require
# bp-seaweedfs to be fully reconciled (not just HelmRelease=Ready).
# Helm `--wait` would block waiting for the StatefulSet rollout, which
# the HelmRelease cannot influence.
---
apiVersion: v1
kind: Namespace
metadata:
name: loki
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-loki
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-loki
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: loki
targetNamespace: loki
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-loki
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-loki
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-mimir — Catalyst Blueprint #23 (W2.K2 Observability batch).
# Grafana Mimir — metrics storage tier of the LGTM stack. SeaweedFS-backed
# blocks-storage; Prometheus-compatible remote-write endpoint.
#
# Wrapper chart: platform/mimir/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Mimir blocks live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Mimir blocks-storage uses SeaweedFS S3.
# Without SeaweedFS Ready, Mimir compactor/ingester StatefulSets
# cannot persist metric blocks.
#
# disableWait: Mimir is a multi-component distributed system (distributor,
# ingester, querier, store-gateway, compactor, alertmanager, ruler).
# Helm `--wait` would block on every StatefulSet rollout, which can
# legitimately take >5min during a cold-start. The HelmRelease reports
# Ready when manifests apply; runtime convergence is observed via
# kubectl rollout status.
---
apiVersion: v1
kind: Namespace
metadata:
name: mimir
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-mimir
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-mimir
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: mimir
targetNamespace: mimir
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-mimir
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-mimir
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,66 +0,0 @@
# bp-tempo — Catalyst Blueprint #24 (W2.K2 Observability batch).
# Grafana Tempo — distributed tracing storage tier of the LGTM stack.
# Default deployment shape is single-binary (one Tempo StatefulSet) —
# minimum operational cost for a Solo Sovereign.
#
# Wrapper chart: platform/tempo/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-seaweedfs is Ready (Tempo trace blocks live on S3).
#
# dependsOn:
# - bp-seaweedfs (slot 18) — Tempo blocks live on SeaweedFS S3.
# Without SeaweedFS Ready, Tempo cannot persist spans.
#
# disableWait: Tempo single-binary StatefulSet becomes Ready after
# creating the S3 bucket and initialising the WAL — both require
# bp-seaweedfs to be fully reconciled. Helm `--wait` would block on
# the rollout, which the HelmRelease cannot influence.
---
apiVersion: v1
kind: Namespace
metadata:
name: tempo
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-tempo
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-tempo
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: tempo
targetNamespace: tempo
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-tempo
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-tempo
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,75 +0,0 @@
# bp-grafana — Catalyst Blueprint #25 (W2.K2 Observability batch).
# Grafana — visualization layer of the LGTM stack. Pairs with bp-loki
# (logs), bp-mimir (metrics), bp-tempo (traces); CNPG-backed Postgres
# for dashboard/folder/alert state; Keycloak OIDC for SSO.
#
# Wrapper chart: platform/grafana/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cnpg, bp-loki, bp-mimir, bp-tempo, bp-keycloak are
# all Ready.
#
# dependsOn:
# - bp-cnpg (slot 16) — Postgres backend for Grafana state.
# - bp-loki (slot 22) — datasource for logs.
# - bp-mimir (slot 23) — datasource for metrics.
# - bp-tempo (slot 24) — datasource for traces.
# - bp-keycloak (slot 09) — OIDC IdP for SSO.
#
# disableWait: Grafana waits for its CNPG-managed `grafana-app` Secret
# (synthesised by bp-cnpg via the chart's Cluster CR), and for upstream
# datasource endpoints to answer. Helm `--wait` would block on the
# Deployment rollout, which the HelmRelease cannot influence; runtime
# convergence is observed via kubectl rollout status.
---
apiVersion: v1
kind: Namespace
metadata:
name: grafana
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-grafana
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-grafana
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: grafana
targetNamespace: grafana
dependsOn:
- name: bp-cnpg
- name: bp-loki
- name: bp-mimir
- name: bp-tempo
- name: bp-keycloak
chart:
spec:
chart: bp-grafana
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-grafana
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,84 +0,0 @@
# bp-langfuse — Catalyst Blueprint #26 (W2.K2 Observability batch).
# Langfuse — LLM observability platform (traces, evaluations, prompt
# management, cost attribution). Hooks into the Catalyst LLM gateway
# (slot 40) once W2.K4 lands. CNPG-backed Postgres; Keycloak OIDC SSO.
#
# Wrapper chart: platform/langfuse/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane, AFTER
# bp-cnpg, bp-keycloak, bp-cert-manager are all Ready.
#
# dependsOn:
# - bp-cnpg (slot 16) — Postgres backend for Langfuse traces /
# prompts / evaluations.
# - bp-keycloak (slot 09) — OIDC IdP for SSO.
# - bp-cert-manager (slot 02) — TLS for the Langfuse Ingress.
#
# disableWait: Langfuse waits for its CNPG-managed `langfuse-app` Secret
# and for upstream Bitnami subcharts to be filtered out at template time
# (the chart sets `postgresql.deploy=false`, `redis.deploy=false`,
# `clickhouse.deploy=false`, `s3.deploy=false` to route to bp-cnpg /
# bp-valkey / bp-clickhouse / bp-seaweedfs respectively). Helm `--wait`
# would block on the Deployment rollout, which the HelmRelease cannot
# influence.
#
# Forward-prep notice — issue #215 (bp-langfuse:1.0.0 GHCR publish 500):
# At the time this HR file was authored, bp-langfuse:1.0.0 had not
# published to oci://ghcr.io/openova-io due to a Helm v3.16 + GHCR
# manifest interaction with langfuse's nested OCI subchart deps. W1.G
# is the concurrent track fixing the publish path. Until that lands,
# this HelmRelease will fail to install with a chart-pull error; this
# is expected and tracked in #215. The HR file is committed now so
# the moment the artifact is published, Flux reconciles the SeaweedFS-
# /CNPG-/Keycloak-Ready Sovereign to bring Langfuse online without a
# second deploy gate.
---
apiVersion: v1
kind: Namespace
metadata:
name: langfuse
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-langfuse
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-langfuse
namespace: flux-system
spec:
interval: 15m
timeout: 15m
releaseName: langfuse
targetNamespace: langfuse
dependsOn:
- name: bp-cnpg
- name: bp-keycloak
- name: bp-cert-manager
chart:
spec:
chart: bp-langfuse
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-langfuse
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,73 +0,0 @@
# bp-kyverno — Catalyst bootstrap-kit Blueprint #27 (W2.K3, Tier 7 — Security/Policy).
# Kubernetes-native admission policy engine. Validating/mutating/generating
# admission control via ClusterPolicy/Policy CRDs. HA mode with separate
# admission/background/cleanup/reports controllers. The first guardrail
# downstream Catalyst Apps land behind once the platform is bootstrapped.
#
# Wrapper chart: platform/kyverno/chart/ (umbrella over upstream
# kyverno/kyverno chart, Catalyst-curated values under the `kyverno:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Kyverno admission webhooks need a working CNI + Service
# mesh substrate to receive AdmissionReview requests from the apiserver.
# Cilium is the root of the Catalyst-Zero DAG; until it is Ready the
# apiserver→webhook path is not reachable and Kyverno install is racy.
#
# No further dependsOn: Kyverno installs its own CRDs and does not require
# cert-manager (it auto-generates admission webhook TLS via its built-in
# certificate controller).
---
apiVersion: v1
kind: Namespace
metadata:
name: kyverno
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-kyverno
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-kyverno
namespace: flux-system
spec:
interval: 15m
releaseName: kyverno
targetNamespace: kyverno
dependsOn:
- name: bp-cilium
chart:
spec:
chart: bp-kyverno
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-kyverno
namespace: flux-system
# Event-driven install: Kyverno HA mode brings up four controller
# Deployments (admission, background, cleanup, reports) plus the
# admission webhook TLS bootstrap. Pod Ready is multi-minute on a
# cold cluster; Helm `--wait` would hold the HR's Ready=True signal
# past the point where downstream HRs could legitimately reconcile.
# disableWait lets Flux mark this Ready as soon as manifests apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,67 +0,0 @@
# bp-reloader — Catalyst bootstrap-kit Blueprint #28 (W2.K3, Tier 7 — Security/Policy).
# Stakater Reloader watches ConfigMap/Secret changes and triggers rolling
# restarts of dependent Deployments/StatefulSets/DaemonSets that opt in
# via annotations. The secret/configmap-rotation glue across Catalyst —
# bp-* workloads pick up rotated TLS material and rotated bootstrap
# credentials without manual rollouts.
#
# Wrapper chart: platform/reloader/chart/ (umbrella over upstream
# stakater/reloader chart, Catalyst-curated values under the `reloader:`
# key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn: (none) — Reloader is independent infrastructure. It only
# watches Kubernetes API resources and triggers rollouts; it does not
# require any sibling Blueprint. Listed at slot 28 for numeric grouping
# with the security/policy cohort but Flux will install it as soon as
# the cluster is reachable.
---
apiVersion: v1
kind: Namespace
metadata:
name: reloader
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-reloader
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-reloader
namespace: flux-system
spec:
interval: 15m
releaseName: reloader
targetNamespace: reloader
chart:
spec:
chart: bp-reloader
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-reloader
namespace: flux-system
# Event-driven install (Catalyst convention) — Reloader's single
# Deployment Ready path is fast in practice but disableWait keeps the
# HR Ready signal aligned with manifest apply rather than runtime
# convergence, matching the rest of the bootstrap-kit.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,66 +0,0 @@
# bp-vpa — Catalyst bootstrap-kit Blueprint #29 (W2.K3, Tier 7 — Security/Policy).
# Vertical Pod Autoscaler. Per-host-cluster automated resource right-sizing
# (Recommender + Updater + Admission Controller). Pairs with HPA/KEDA on
# the horizontal axis. Provides recommendations even if not auto-applying;
# Catalyst-default mode is `Off` (recommend only) until SRE opts a
# workload into `Auto` via VerticalPodAutoscaler resources.
#
# Wrapper chart: platform/vpa/chart/ (umbrella over upstream
# autoscaler/vertical-pod-autoscaler chart, Catalyst-curated values under
# the `vertical-pod-autoscaler:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn: (none) — VPA is independent infrastructure. The Recommender
# reads from metrics-server (a k3s fixture) and Prometheus/Mimir when
# present, but neither is a hard sibling-Blueprint dep at install time;
# the controllers tolerate metrics endpoints appearing late. Listed at
# slot 29 for numeric grouping with the security/policy cohort.
---
apiVersion: v1
kind: Namespace
metadata:
name: vpa
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-vpa
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-vpa
namespace: flux-system
spec:
interval: 15m
releaseName: vpa
targetNamespace: vpa
chart:
spec:
chart: bp-vpa
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-vpa
namespace: flux-system
# Event-driven install: VPA brings up three Deployments (recommender,
# updater, admission-controller) plus admission webhook TLS bootstrap.
# disableWait keeps Flux's Ready signal aligned with manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-trivy — Catalyst bootstrap-kit Blueprint #30 (W2.K3, Tier 7 — Security/Policy).
# Trivy Operator. Static-scanning half of the Catalyst security stack:
# vulnerability + misconfiguration scanning of running workloads, images,
# RBAC, and rendered manifests. Pairs with bp-falco (runtime, slot 31)
# and bp-kyverno (admission, slot 27).
#
# Wrapper chart: platform/trivy/chart/ (umbrella over upstream
# aquasecurity/trivy-operator chart, Catalyst-curated values under the
# `trivy-operator:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — Trivy Operator's admission webhook (and its
# ConfigAuditReport mutating-webhook in HA mode) requires a TLS cert
# from the cluster's letsencrypt-prod / internal CA ClusterIssuer
# before the apiserver will route AdmissionReview traffic. Without
# bp-cert-manager Ready, the Certificate resource sits Pending and
# the webhook serves stale or no certs.
---
apiVersion: v1
kind: Namespace
metadata:
name: trivy-system
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-trivy
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-trivy
namespace: flux-system
spec:
interval: 15m
releaseName: trivy
targetNamespace: trivy-system
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-trivy
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-trivy
namespace: flux-system
# Event-driven install: Trivy Operator pulls a multi-hundred-MB
# vulnerability database on first run; pod Ready is dominated by
# initial DB hydration, not manifest apply. disableWait lets Flux
# mark this Ready as soon as manifests apply; runtime convergence
# (DB hydration, first scan reports landing) is observed via kubectl.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,69 +0,0 @@
# bp-falco — Catalyst bootstrap-kit Blueprint #31 (W2.K3, Tier 7 — Security/Policy).
# Runtime threat detection (CNCF Graduated). eBPF kernel-level syscall
# monitoring for container escapes, privilege escalation, anomalous
# behavior. Runs as a DaemonSet on every host of the Sovereign;
# Falcosidekick fans events into the SIEM pipeline (Loki/JetStream).
#
# Wrapper chart: platform/falco/chart/ (umbrella over upstream
# falcosecurity/falco chart, Catalyst-curated values under the `falco:`
# key — modern_ebpf driver, falcosidekick enabled).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Falco's modern_ebpf probe loads alongside Cilium's
# eBPF programs on the same kernel hook points. Bringing Falco up
# before the CNI is finalised has produced flaky probe-load races
# in field testing; sequencing after bp-cilium Ready avoids that.
---
apiVersion: v1
kind: Namespace
metadata:
name: falco
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-falco
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-falco
namespace: flux-system
spec:
interval: 15m
releaseName: falco
targetNamespace: falco
dependsOn:
- name: bp-cilium
chart:
spec:
chart: bp-falco
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-falco
namespace: flux-system
# Event-driven install: Falco's DaemonSet rolls out per-node and the
# eBPF probe load is sensitive to kernel headers / module presence.
# Per-node Ready is properly observed via DaemonSet status, not via
# Helm `--wait`. disableWait keeps Flux's signal aligned with
# manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,71 +0,0 @@
# bp-sigstore — Catalyst bootstrap-kit Blueprint #32 (W2.K3, Tier 7 — Security/Policy).
# Sigstore Policy Controller. Admission gate for signed-image enforcement —
# verifies Cosign signatures + attestations on container images before
# admission. Pairs with bp-harbor (registry, slot 19) and the CI-side
# Cosign signing path to close the supply-chain trust loop on the
# Sovereign.
#
# Wrapper chart: platform/sigstore/chart/ (umbrella over upstream
# sigstore/policy-controller chart, Catalyst-curated values under the
# `policy-controller:` key).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — Policy Controller is an admission webhook; the
# apiserver requires a valid TLS cert on the webhook Service before
# it will route AdmissionReview traffic. Without bp-cert-manager
# Ready, the Certificate sits Pending and admission fails open or
# fails closed (depending on FailurePolicy), neither of which is
# the intended security posture.
---
apiVersion: v1
kind: Namespace
metadata:
name: sigstore-system
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-sigstore
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-sigstore
namespace: flux-system
spec:
interval: 15m
releaseName: sigstore
targetNamespace: sigstore-system
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-sigstore
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-sigstore
namespace: flux-system
# Event-driven install: Policy Controller bootstraps its admission
# webhook TLS via cert-manager and reaches Ready only after the
# Certificate is issued + bound. disableWait avoids holding the HR
# signal on a runtime-convergence event.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,68 +0,0 @@
# bp-syft-grype — Catalyst bootstrap-kit Blueprint #33 (W2.K3, Tier 7 — Security/Policy).
# Anchore Syft + Grype as a scheduled CronJob. SBOM generation (Syft)
# paired with vulnerability matching (Grype) — the offline / scheduled
# half of the supply-chain stack. Anchore does not publish a Helm chart
# for the open-source CLIs, so this Blueprint is a scratch chart that
# wires the official ghcr.io/anchore/syft and ghcr.io/anchore/grype
# containers into a CronJob that scans the Sovereign's image inventory.
#
# Wrapper chart: platform/syft-grype/chart/ (Catalyst-authored scratch
# chart — no upstream subchart).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cert-manager — the result-export sidecar publishes SBOMs over
# mTLS to the central scan-result store; cert-manager issues the
# workload's TLS material via the cluster's ClusterIssuer.
---
apiVersion: v1
kind: Namespace
metadata:
name: syft-grype
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-syft-grype
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-syft-grype
namespace: flux-system
spec:
interval: 15m
releaseName: syft-grype
targetNamespace: syft-grype
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-syft-grype
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-syft-grype
namespace: flux-system
# Event-driven install: the Blueprint is mostly a CronJob + RBAC
# surface. There is no long-running Deployment whose Ready=True is
# meaningful — disableWait is the correct shape so Flux marks Ready
# as soon as manifests apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,72 +0,0 @@
# bp-velero — Catalyst bootstrap-kit Blueprint #34 (W2.K3, Tier 7 — Security/Policy).
# Per-host-cluster backup engine. Catalyst-Zero pins backups to SeaweedFS
# (the unified S3 layer, slot 18) so backup data never leaves the
# Sovereign at install time; per-Sovereign archival to a cloud backend
# is wired in post-bootstrap via Crossplane.
#
# Wrapper chart: platform/velero/chart/ (umbrella over upstream
# vmware-tanzu/velero chart, Catalyst-curated values under the `velero:`
# key — `seaweedfs` BackupStorageLocation provider, no cloud plugin
# pinned at install time).
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-seaweedfs — Velero's BackupStorageLocation points at the
# in-cluster SeaweedFS S3 endpoint (`seaweedfs.seaweedfs.svc:8333`)
# and reads the `seaweedfs-s3-credentials` Secret SeaweedFS renders
# during install. Without bp-seaweedfs Ready, the BSL Phase sits
# `Unavailable` and Velero's first reconcile fails — every backup
# CR queues with the same error until the dep lands.
---
apiVersion: v1
kind: Namespace
metadata:
name: velero
labels:
catalyst.openova.io/sovereign: omantel.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-velero
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-velero
namespace: flux-system
spec:
interval: 15m
releaseName: velero
targetNamespace: velero
dependsOn:
- name: bp-seaweedfs
chart:
spec:
chart: bp-velero
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-velero
namespace: flux-system
# Event-driven install: Velero's Deployment Ready depends on the
# node-agent DaemonSet rolling out and the BackupStorageLocation
# reaching `Available` — both runtime-convergence events that Flux
# observes via the BSL CR phase, not via Helm `--wait`. disableWait
# keeps the HR's Ready signal aligned with manifest apply.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -1,70 +0,0 @@
# bp-coraza — Catalyst bootstrap-kit Blueprint #35 (W2.K4 — Tier 8: edge).
# OWASP-licensed Web Application Firewall, ModSecurity-rule-compatible.
# Speaks the HAProxy SPOE protocol; sits in front of Cilium Gateway / HAProxy
# fronts to enforce WAF policies on inbound traffic to Sovereign-facing
# services (keycloak, grafana, stalwart, marketplace).
#
# Wrapper chart: platform/coraza/chart/
# Catalyst-curated values: platform/coraza/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# dependsOn:
# - bp-cilium — Coraza enforces L7 policy via Cilium L7 proxy / Gateway
# API; Cilium must be Ready (CNI + Gateway controller) before WAF
# evaluation hooks become reachable.
# - bp-cert-manager — Issuers must be reconciled so any TLS-fronted SPOA
# listeners (per-Sovereign overlays) come up with valid certs.
#
# install/upgrade.disableWait: true — Coraza-spoa Deployment Ready signal
# is event-driven via the Flux dependsOn graph (downstream HRs check
# Ready=True on this HR). Per session-2026-04-30 architectural decision,
# we never use blanket `spec.timeout: Nm` watchdogs.
---
apiVersion: v1
kind: Namespace
metadata:
name: coraza
labels:
catalyst.openova.io/sovereign: SOVEREIGN_FQDN_PLACEHOLDER
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-coraza
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-coraza
namespace: flux-system
spec:
interval: 15m
releaseName: coraza
targetNamespace: coraza
dependsOn:
- name: bp-cilium
- name: bp-cert-manager
chart:
spec:
chart: bp-coraza
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-coraza
namespace: flux-system
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -15,28 +15,4 @@ resources:
- 08-openbao.yaml
- 09-keycloak.yaml
- 10-gitea.yaml
- 11-powerdns.yaml
- 12-external-dns.yaml
- 13-bp-catalyst-platform.yaml
- 14-crossplane-claims.yaml
- 15-external-secrets.yaml
- 16-cnpg.yaml
- 17-valkey.yaml
- 18-seaweedfs.yaml
- 19-harbor.yaml
- 20-opentelemetry.yaml
- 21-alloy.yaml
- 22-loki.yaml
- 23-mimir.yaml
- 24-tempo.yaml
- 25-grafana.yaml
- 26-langfuse.yaml
- 27-kyverno.yaml
- 28-reloader.yaml
- 29-vpa.yaml
- 30-trivy.yaml
- 31-falco.yaml
- 32-sigstore.yaml
- 33-syft-grype.yaml
- 34-velero.yaml
- 35-coraza.yaml
- 11-bp-catalyst-platform.yaml

View File

@ -1,75 +0,0 @@
# bp-cilium — Catalyst bootstrap-kit Blueprint. CNI must come first; k3s started with --flannel-backend=none precisely so Cilium can take over.
#
# Wrapper chart: platform/cilium/chart/
# Catalyst-curated values: platform/cilium/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
# kube-system is built into every Kubernetes cluster — never re-declare it.
# Earlier revisions of 01-cilium.yaml AND 05-sealed-secrets.yaml both
# declared it, which collided when kustomize tried to merge the two:
# "may not add resource with an already registered id:
# Namespace.v1.[noGrp]/kube-system.[noNs]"
# This Blueprint installs Cilium INTO kube-system; the HelmRelease's
# targetNamespace field below is sufficient.
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-cilium
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-cilium
namespace: flux-system
spec:
interval: 15m
releaseName: cilium
targetNamespace: kube-system
chart:
spec:
chart: bp-cilium
version: 1.1.1
sourceRef:
kind: HelmRepository
name: bp-cilium
namespace: flux-system
# Event-driven install: Helm completes when manifests apply, not when
# cilium-agent reaches Ready (agent waits for envoyconfig CRDs that the
# SAME chart installs — legitimate slow-Ready). Replaces blanket
# spec.timeout: 15m band-aid from PR #221.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cilium:
# Enable L7 proxy so Cilium's chart installs the
# ciliumenvoyconfigs / ciliumclusterwideenvoyconfigs CRDs that the
# cilium-agent waits for at startup. Without this, agent crash-loops
# forever and the node.cilium.io/agent-not-ready taint never lifts.
l7Proxy: true
prometheus:
enabled: false
serviceMonitor:
enabled: false
hubble:
metrics:
enabled: null
serviceMonitor:
enabled: false
relay:
enabled: false
ui:
enabled: false

View File

@ -1,64 +0,0 @@
# bp-cert-manager — Catalyst bootstrap-kit Blueprint. TLS for everything below — Lets Encrypt issuer with Dynadot DNS-01 (omani.works pool) or HTTP-01 (BYO domains).
#
# Wrapper chart: platform/cert-manager/chart/
# Catalyst-curated values: platform/cert-manager/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
labels:
catalyst.openova.io/sovereign: otech.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-cert-manager
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-cert-manager
namespace: flux-system
spec:
interval: 15m
releaseName: cert-manager
targetNamespace: cert-manager
dependsOn:
- name: bp-cilium
chart:
spec:
chart: bp-cert-manager
version: 1.1.1
sourceRef:
kind: HelmRepository
name: bp-cert-manager
namespace: flux-system
# Event-driven install: cert-manager installs CRDs + 3 deployments
# (controller, webhook, cainjector). Webhook readiness depends on the
# cainjector mutating the Secret — multi-minute path on cold start.
# Helm install completes when manifests apply; subsequent dependsOn
# checks Ready=True independently. Replaces PR #221 spec.timeout: 15m.
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3
values:
cert-manager:
prometheus:
enabled: false
servicemonitor:
enabled: false

View File

@ -1,88 +0,0 @@
# bp-flux — Catalyst bootstrap-kit Blueprint. Host-level Flux. Per-vcluster Flux is bootstrapped later by environment-controller.
#
# Wrapper chart: platform/flux/chart/
# Catalyst-curated values: platform/flux/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# DOUBLE-INSTALL SAFETY (omantel.omani.works incident, 2026-04-29)
# ----------------------------------------------------------------
# Cloud-init pre-installs Flux core via
# curl https://github.com/fluxcd/flux2/releases/download/v2.4.0/install.yaml
# so that this very HelmRelease can be reconciled. helm-controller then
# runs `helm install` for bp-flux on top of the already-running Flux. If
# the chart's subchart `flux2` version disagrees with the cloud-init
# install (different upstream Flux release), CRD `storedVersions`
# mismatches → Helm install fails → rollback → rollback DELETES the
# running Flux controllers → cluster has no GitOps engine and is
# unrecoverable in-place.
#
# Mitigations applied here:
# 1. bp-flux:1.1.2 pins the `flux2` subchart at 2.14.1 (= appVersion
# 2.4.0) which matches cloud-init's v2.4.0 install.yaml.
# 2. spec.upgrade.preserveValues: true — never silently overwrite
# operator overlays on upgrade.
# controller adopts the cloud-init-installed objects rather than
# re-creating, so install is non-destructive when objects already
# exist with matching apiVersion/kind/name.
# See docs/RUNBOOK-PROVISIONING.md §"bp-flux double-install".
---
apiVersion: v1
kind: Namespace
metadata:
name: flux-system
labels:
catalyst.openova.io/sovereign: otech.omani.works
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-flux
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-flux
namespace: flux-system
spec:
interval: 15m
releaseName: flux
targetNamespace: flux-system
dependsOn:
- name: bp-cert-manager
chart:
spec:
chart: bp-flux
version: 1.1.2
sourceRef:
kind: HelmRepository
name: bp-flux
namespace: flux-system
# Event-driven install: bp-flux adopts the cloud-init-installed Flux
# controllers; the helm-controller pod that reconciles THIS HR is itself
# a target of the chart, so blocking on Ready=True is structurally
# impossible. disableWait avoids the deadlock. Replaces PR #221 timeout.
install:
disableWait: true
# Adopt cloud-init-installed Flux objects rather than fail on
# ownership conflict (the objects exist before the HelmRelease ever
# reconciles). Without this, the very first reconcile would error
# with "object already exists" on every Flux controller Deployment.
remediation:
retries: 3
upgrade:
disableWait: true
# Keep operator-supplied values (e.g. resource overrides applied via
# helm-controller out-of-band, or dry-run patches during incident
# response) on chart upgrades. Without this, every upgrade would
# reset the chart to default values, masking operator state.
preserveValues: true
# Match install behaviour — adopt rather than fail on conflict.
remediation:
retries: 3

Some files were not shown because too many files have changed in this diff Show More