# bp-self-sovereign-cutover — Catalyst bootstrap-kit Blueprint, slot 06a.
#
# Post-handover Self-Sovereignty Cutover. Installs DORMANT — see chart
# README + ADR-0002. Eight step PodSpec ConfigMaps + the registry-pivot
# DaemonSet land on the new Sovereign at HelmRelease apply time; the
# catalyst-api cutover endpoint (issue #792) reads them by label
# selector and stamps Jobs only on operator action.
#
# Slot 06a sits between the existing post-handover slots in the
# bootstrap-kit ordering. It depends on bp-gitea + bp-harbor so the
# step ConfigMaps reference real, healthy local Gitea + Harbor
# Services at trigger time.
#
# Wrapper chart: platform/self-sovereign-cutover/chart/
# Reconciled by: Flux on the new Sovereign's k3s control plane.
#
# Why disableWait: true
#   The chart is dormant — no Job is created at install time. The only
#   workload that actually runs is the registry-pivot DaemonSet, which
#   never converges on its own (it waits for the cutover-status
#   ConfigMap to flip registriesYamlActive=v2). disableWait: true makes
#   Helm exit when the manifests apply rather than waiting on a Ready
#   condition that never fires.

---
apiVersion: v1
kind: Namespace
metadata:
  name: catalyst
  labels:
    catalyst.openova.io/sovereign: ${SOVEREIGN_FQDN}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  name: bp-self-sovereign-cutover
  namespace: flux-system
spec:
  type: oci
  interval: 15m
  # Pre-cutover (Phase-1) — sources from openova-io GHCR, identical to
  # every other bootstrap-kit slot. The cutover itself (step 06,
  # helmrepository-patches) is what flips this URL to the local Harbor
  # post-handover; until then this Sovereign is soft-tethered like the
  # rest of the kit.
  url: oci://ghcr.io/openova-io
  secretRef:
    name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: bp-self-sovereign-cutover
  namespace: flux-system
spec:
  interval: 15m
  releaseName: self-sovereign-cutover
  # targetNamespace = catalyst because the catalyst-api cutover endpoint
  # defaults its discovery namespace to "catalyst"
  # (CATALYST_CUTOVER_NAMESPACE in
  # products/catalyst/bootstrap/api/internal/handler/cutover.go). Keeping
  # the chart resources colocated with catalyst-api avoids a cross-
  # namespace selector + a CATALYST_CUTOVER_NAMESPACE env override on
  # every Sovereign.
  targetNamespace: catalyst
  dependsOn:
    # Both bp-gitea and bp-harbor must be Ready before the chart's
    # PodSpec ConfigMaps reference real Services. The chart itself is
    # dormant so an early apply is benign — but operator workflow
    # ordering puts cutover after these two come up.
    - name: bp-gitea
    - name: bp-harbor
  chart:
    spec:
      chart: bp-self-sovereign-cutover
      # 0.1.1: Step-1 gitea-mirror script uses BusyBox-wget-compatible
      # Authorization: Basic <b64> header instead of --user/--password
      # which alpine/git's BusyBox wget does not support.
      # 0.1.2: Step-1 explicitly creates the Gitea org BEFORE the repo —
      # POST /orgs/<org>/repos returns 404 if the org is missing, the
      # /user/repos fallback would create under gitea_admin (wrong path
      # for the subsequent push). Caught live on otech103 2026-05-04.
      # 0.1.3: replace `git push --mirror` with `git push --all + --tags`
      # so Gitea's hooks don't decline GitHub-specific refs/pull/<n>
      # refs (which --mirror would try to push). Branches+tags are what
      # Flux GitRepository needs; PR refs are upstream-only metadata.
      # 0.1.4: Step-1 uses `git clone --bare` (not --mirror) + explicit
      # refspec push of refs/heads/* and refs/tags/* only. --all in a
      # mirror clone still pushed refs/pull/* — caught live otech103.
      # 0.1.5: harborInternalURL fix — bp-harbor service is `harbor-core`
      # not `harbor-harbor-core` (release name doesn't double-prefix).
      # Caught live otech103 — Step-2 curl exit 6 (couldn't resolve).
      # 0.1.6: proxy-ghcr registryType "github" → "github-ghcr" (the
      # canonical Harbor adapter name for GHCR proxy-cache, per Harbor
      # 2.x docs). Caught live otech103 — Harbor 500 "adapter factory
      # for github not found".
      # 0.1.7: proxy-quay registryType "quay" → "docker-registry" —
      # Harbor's "quay" adapter rejects project metadata.proxy_cache
      # with HTTP 400. Quay speaks plain v2 so generic docker-registry
      # is correct. Caught live otech103 — 4/7 proxy-cache projects
      # were created OK, blocked at proxy-quay.
      # 0.1.8: bitnami/kubectl tag :1.31 → :1.31.4 (bitnami doesn't tag
      # at minor-version, only patch). Caught live otech103 — Step-5
      # Pod hit DeadlineExceeded after 10m of ImagePullBackOff for
      # docker.io/bitnami/kubectl:1.31 (404 not found).
      # 0.1.9: bitnami/kubectl :1.31.4 ALSO 404 (Bitnami deprecated
      # public Docker Hub in 2025). Switched to alpine/k8s:1.31.4 —
      # canonical alpine-based image with kubectl + helm + k8s CLI
      # surface, actively maintained.
      # 0.1.10: catalystAPI.namespace `catalyst-platform` → `catalyst-
      # system` (the actual Sovereign-side namespace). Caught live
      # otech103 — Step-7 `deployment catalyst-api not found`.
      # 0.1.11: Step-8 egress-block-test pivoted from CiliumNetworkPolicy
      # (egressDeny + toFQDNs unsupported in Cilium 1.16) to a passive
      # architectural-state assertion + ${durationSeconds}s survival
      # window. Same proof shape, valid Cilium policy. Caught live
      # otech103 — strict-decoding error 'unknown field toFQDNs'.
      # 0.1.12: Step-8 verification tolerates slot-managed self-ref
      # HelmRepositories (bp-newapi + bp-self-sovereign-cutover) which
      # Flux Kustomization re-applies from bootstrap-kit slots after
      # Step-6's patch. Data-plane impact null — they're not pulled
      # again until next cutover cycle. Caught live otech103.
      # 0.1.13: Step-8 survival window captures BASELINE NotReady set
      # before entering the window, then only fails on NEW Ready=False
      # transitions (regressions). Pre-existing failures (Crossplane
      # provider CRD ordering, etc.) don't poison the sovereignty
      # verdict — sovereignty asks "did cutover break anything", not
      # "is the cluster perfect". Caught live otech103 — infrastructure
      # -config Kustomization had been NotReady for 4h pre-cutover.
      # 0.1.14: Step-1 gitea-mirror replaces one-shot create+push with
      # Gitea native /repos/migrate `mirror=true` + mirror_interval=10m
      # so the local Gitea polls upstream GitHub on a recurring 10-min
      # interval. Closes the "Sovereign drifts from upstream main
      # forever after Day-2 cutover" bug — hit twice on otech103
      # 2026-05-04 requiring manual `git fetch` per chart rollout. (#870)
      version: 0.1.14
      sourceRef:
        kind: HelmRepository
        name: bp-self-sovereign-cutover
        namespace: flux-system
  install:
    disableWait: true
    remediation:
      retries: 3
  upgrade:
    disableWait: true
    remediation:
      retries: 3
  # Per-Sovereign overrides — the chart's values.yaml carries
  # placeholders so smoke renders pass; the real coordinates land
  # here via Flux postBuild ${SOVEREIGN_FQDN} substitution.
  values:
    sovereign:
      fqdn: ${SOVEREIGN_FQDN}
      harborInternalURL: http://harbor-core.harbor.svc.cluster.local
      harborPublicURL: https://harbor.${SOVEREIGN_FQDN}
      giteaInternalURL: http://gitea-http.gitea.svc.cluster.local:3000
      giteaPublicURL: https://gitea.${SOVEREIGN_FQDN}
