apiVersion: v2
name: bp-self-sovereign-cutover
# 0.1.28 (Fix #158, 2026-05-11): values.yaml comment cleanup — chart
# already migrated off bitnami/kubectl on 0.1.10 (alpine/k8s for kubectl,
# alpine for chroot-pivot). Comment text now reflects the platform-wide
# Fix #158 migration to bitnamilegacy/kubectl across other Blueprints.
# No functional change in this Blueprint.
#
# 0.1.29 (Fix #163, 2026-05-11, MIRROR-EVERYTHING): every default image
# reference (kubectl/git/curl/hostExec + the hardcoded alpine/k8s in
# step 06) now uses the explicit harbor.openova.io/proxy-dockerhub
# prefix per CLAUDE.md inviolable rule. The Sovereign cloudinit already
# routes containerd through this proxy at node-boot time, so the
# pre-cutover Jobs can reach the explicit URL just fine. Operator
# override via .Values.global.imageRegistry remains intact for true
# air-gap or alternate mirror deployments.
#
# 0.1.30 (TBD-C18, 2026-05-18): NEW step 09 (gitea-token-mint) —
# bootstraps a real Gitea API token at cutover and patches Secret
# `sme/provisioning-github-token.GITHUB_TOKEN`. The catalyst-platform
# chart's existing provisioning-github-token.yaml template mirrors the
# Gitea admin PASSWORD into that Secret; the SME provisioning service
# then sends `Authorization: token <PWD>` to Gitea, which 401s because
# the password is not an API token. Result on t22: voucher checkout
# completed, /jobs redirect fired, but NO Organization CR was ever
# materialised (the provisioning service kept failing the Gitea token
# auth). Step 09 fixes this end-to-end: idempotent DELETE-then-POST
# /api/v1/users/gitea_admin/tokens, validate via GET /api/v1/user,
# kubectl-patch the dest Secret with stringData, rollout-restart
# provisioning Deployment (best-effort). Order 9 (last) is fine
# functionally — none of steps 02-08 read the provisioning-github-token
# Secret, and the SME provisioning service first consumes the token at
# voucher checkout time (always postdates cutover). Order 9 avoids
# renumbering 01..08 which would invalidate operator history.
#
# 0.1.31 (TBD-C19, 2026-05-18): step-06 now also pivots the
# `openova-catalog` HelmRepository. The HR is rendered by the bp-
# catalyst-platform Helm chart (not directly from a bootstrap-kit slot
# file) from `.Values.catalog.helmRepository.url`. Phase-1 patches the
# live HR; new Phase-1.6 patches the parent HelmRelease's
# spec.values.catalog.helmRepository.url so the next chart reconcile
# preserves the local URL; new Phase-2.5 injects (or rewrites) the
# same override into 13-bp-catalyst-platform.yaml in the local Gitea
# repo so bootstrap-kit Kustomization reconcile doesn't revert it.
# Without this fix step-08's egress-block-test catches
# `openova-catalog` as the lone OFFENDER ~1 min after step-06
# completes (chart re-render reverts phase-1). Caught live on
# t22.omantel.biz 2026-05-18.
#
# 0.1.32 (issue #1871, 2026-05-19): Step-06 helmrepository-patches Job
# gains a NEW Phase -1 (gateway-wait) that runs BEFORE Phase-0
# (ghcr-pull auth merge) and Phase-1 (URL rewrite). The Job blocks
# until `gateway.networking.k8s.io/v1.Gateway cilium-gateway` in
# `kube-system` reports `Programmed=True`, which proves the Cilium
# Gateway has a listener on *.<SOVEREIGN_FQDN>:443 serving TLS
# against the wildcard cert — i.e. `registry.<SOVEREIGN_FQDN>` will
# answer 200, not TLS-handshake EOF. Closes the cutover↔gateway
# circular deadlock discovered on t26 zero-touch
# (99bb823cb0513f4b, A55 diagnostic JSON):
#   1) cutover Step-06 rewrote 50 HelmRepository URLs to
#      oci://registry.t26.omani.works/openova-io
#   2) source-controller pull → TLS handshake EOF
#      (Gateway not yet Programmed)
#   3) bp-catalyst-platform HR Ready=False
#   4) bootstrap-kit Ks Reconciling
#   5) sovereign-tls Ks DependencyNotReady on bootstrap-kit
#   6) Gateway never installed → loop forever
# Adding the Phase -1 wait inside the Job (rather than as a cross-
# kind dependsOn) is the only viable seam: Flux HR dependsOn can only
# reference HelmReleases, NOT Kustomizations, so the natural
# `dependsOn: sovereign-tls` (PR #1875, superseded by this version)
# never resolved — helm-controller logged "helmreleases.helm.toolkit.
# fluxcd.io \"sovereign-tls\" not found" forever (t27 A84 empirical
# test). RBAC: ClusterRole gains gateway.networking.k8s.io.gateways
# {get,list,watch}. Configurable via `.Values.gateway.{namespace,
# name,waitTimeoutSeconds}`; default 30 min timeout safely covers
# the slowest Hetzner cold-start observed (≈18 min).
#
# 0.1.33 (TBD-A37, issue #1899, 2026-05-19): NEW post-cutover continuous
# mirror re-sync CronJob (template 11-mirror-resync-cronjob.yaml). Step
# 01 (gitea-mirror) only ever runs ONCE at cutover and produces a
# STANDALONE local Gitea repo (PR #1029 — pull-mirror semantics block
# Step-06's HelmRepository URL rewrite push, so cutover migrated off
# Gitea's built-in pull-mirror). Without an ongoing re-sync, upstream
# chart bumps merged AFTER cutover never reach the Sovereign. Live
# regression on t31 2026-05-19 (A145 verifier): sandbox-controller
# stuck at image :8017700 from 2026-05-16 even though PR #1862 had
# merged 2 days earlier with the NATS consume-leg update — the
# upstream values.yaml bump never crossed the mirror seam.
#
# This chart bump adds gitea-mirror-resync CronJob (schedule */5
# default — 5 min cadence covers the chart-bump → upstream-merge →
# Sovereign-reconcile loop in ~10 min end-to-end while staying well
# under GitHub anonymous-clone rate limits). The CronJob fires the
# same idempotent bare-clone + push --mirror --force as Step 01 step
# (3); pre-cutover fires are no-ops (the script detects missing
# local repo and exits 0), post-cutover fires close the loop.
#
# Per-Sovereign overlay knobs under `.Values.mirrorResync.{schedule,
# suspended,jobTimeoutSeconds}`; defaults are canonical fresh-prov
# behaviour (active, 5m, 15m timeout). No new RBAC needed — the
# CronJob talks to GitHub anonymously + local Gitea via BasicAuth
# from the same reflector-mirrored gitea-admin-secret Step-01 mounts.
# Smoke render unaffected (CronJob lacks the cutover-step labels so
# the contract test's exactly-9-steps assertion still passes).
version: 0.1.33
description: |
  Catalyst Self-Sovereignty Cutover Blueprint. Installs DORMANT — this
  chart ships eight step ConfigMaps (PodSpec ConfigMaps, one per step),
  the registry-pivot DaemonSet, the mutable cutover-status ConfigMap,
  plus ServiceAccount/RBAC scoped to the cutover work. NO Helm
  post-install hooks fire any of the steps; the catalyst-api cutover
  endpoint (issue #792) reads each step ConfigMap by label selector,
  stamps a real batch/v1 Job from the embedded PodSpec, and updates
  the status ConfigMap as each step completes.

  Trigger semantics (post-handover, per #790 scope-correction comment):
  the operator clicks "Achieve True Sovereignty" in admin console (or
  catalyst-api auto-fires after first successful operator login on the
  new Sovereign). The handed-over Sovereign starts soft-tethered to
  harbor.openova.io for image-pull rate-limit safety, then becomes
  hard-independent only after this cutover sequence completes.

  Step inventory (label app.kubernetes.io/component=cutover-step):
    01  cutover-step-01-gitea-mirror               mode=job
        git push --mirror github.com/openova-io/openova into local Gitea.
    02  cutover-step-02-harbor-projects            mode=job
        Create 7 proxy-cache projects in local Harbor.
    03  cutover-step-03-harbor-prewarm             mode=job
        HEAD-pull every image referenced by the bootstrap-kit through
        the proxy-cache.
    04  cutover-step-04-registry-pivot             mode=daemonset-wait
        DaemonSet runs on every node + writes /etc/rancher/k3s/registries.yaml
        v2 once the status ConfigMap key registriesYamlActive flips to v2.
    05  cutover-step-05-flux-gitrepository-patch   mode=job
        Patch flux-system/openova GitRepository.url → local Gitea.
    06  cutover-step-06-helmrepository-patches     mode=job
        Patch every OCI HelmRepository → oci://harbor.<sov-fqdn>/openova-io.
    07  cutover-step-07-catalyst-api-env-patch     mode=job
        kubectl set env deploy/catalyst-api CATALYST_GITOPS_REPO_URL=...
    08  cutover-step-08-egress-block-test          mode=job
        Apply CiliumNetworkPolicy denying egress to github.com / ghcr.io
        / harbor.openova.io for 10 min, assert all reconciles stay Ready.
    09  cutover-step-09-gitea-token-mint           mode=job
        POST /api/v1/users/gitea_admin/tokens, capture .sha1, patch
        Secret sme/provisioning-github-token.GITHUB_TOKEN with the real
        API token (the chart previously mirrored the admin password
        verbatim — Gitea then 401s on `Authorization: token <PWD>`,
        blocking tenant voucher checkout at journey step 16). Rolls
        the provisioning Deployment so the new token takes effect
        immediately. (TBD-C18)

  Plus:
    self-sovereign-cutover-status                  ConfigMap
        Initial state machine state (cutoverComplete: "false" + per-step
        empty fields). catalyst-api updates this ConfigMap as each step
        advances.
    registry-pivot                                 DaemonSet
        Always-on per-node reconcile loop; idempotent.
    gitea-mirror-resync                            CronJob (post-cutover)
        Re-runs Step 01's idempotent bare-clone + push --mirror every
        5 min so upstream chart bumps merged AFTER cutover continue
        crossing the seam into local Gitea. Pre-cutover fires are
        no-ops. (TBD-A37, chart 0.1.33)
    bp-self-sovereign-cutover-runner               ServiceAccount + ClusterRole +
                                                   ClusterRoleBinding for the
                                                   stamped Jobs and the DaemonSet.

  This Blueprint legitimately ships NO upstream subchart — the entire
  payload is Catalyst-authored ConfigMaps, ServiceAccount, RBAC, and one
  DaemonSet. The blueprint-release CI hollow-chart guard reads the
  annotation below and skips the dependencies-required rule.
type: application
keywords:
  - catalyst
  - blueprint
  - self-sovereignty
  - cutover
  - gitea
  - harbor
maintainers:
  - name: OpenOva Catalyst
    email: catalyst@openova.io

annotations:
  catalyst.openova.io/no-upstream: "true"
