362a377dc3
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1ddd569789 |
fix(bp-*): observability toggles default false — break circular CRD dependency
Extends the v1.1.1 hardening that started with cilium / cert-manager /
crossplane to the remaining 8 bootstrap-kit + per-Sovereign Blueprints.
Every observability toggle in every Catalyst-curated Blueprint now ships
`false`/`null` by default; the operator opts in via a per-cluster values
overlay at clusters/<sovereign>/bootstrap-kit/* once
bp-kube-prometheus-stack reconciles.
Live failure mode that prompted this (omantel.omani.works 2026-04-29):
bp-cilium @ 1.1.0 defaulted hubble.relay/ui + prometheus.serviceMonitor
to true. The upstream Cilium 1.16.5 chart renders a
monitoring.coreos.com/v1 ServiceMonitor whose CRD ships with
kube-prometheus-stack — a tier-2 Application Blueprint that depends on
the bootstrap-kit (cilium first). Helm install fails on a fresh
Sovereign with "no matches for kind ServiceMonitor in version
monitoring.coreos.com/v1 — ensure CRDs are installed first" and every
downstream HelmRelease reports `dep is not ready`. The earlier
trustCRDsExist=true mitigation only suppresses Helm's render-time gate;
the apiserver still rejects the resource at install-time.
Per-Blueprint changes:
- bp-cilium: hubble.relay.enabled, hubble.ui.enabled → false;
hubble.metrics.enabled → null (this is the exact value that disables
the upstream metrics ServiceMonitor template branch — verified by
reading cilium 1.16.5's _hubble.tpl); hubble.metrics.serviceMonitor
.enabled → false. tests/observability-toggle.sh extended with Case 4
(default render produces no hubble-relay / hubble-ui Deployments).
- bp-flux: flux2.prometheus.podMonitor.create → false.
- bp-sealed-secrets: sealed-secrets.metrics.serviceMonitor.enabled
→ false (explicit lock; upstream already defaults false).
- bp-spire: spire.global.spire.recommendations.enabled +
recommendations.prometheus → false.
- bp-nats-jetstream: nats.promExporter.enabled +
promExporter.podMonitor.enabled → false.
- bp-openbao: openbao.injector.metrics.enabled +
openbao.serviceMonitor.enabled → false.
- bp-keycloak: keycloak.metrics.enabled + metrics.serviceMonitor.enabled
+ metrics.prometheusRule.enabled → false.
- bp-gitea: gitea.gitea.metrics.* and gitea.postgresql.metrics.*
serviceMonitor + prometheusRule → false.
- bp-powerdns: powerdns.serviceMonitor.enabled + powerdns.metrics.enabled
→ false (forward-compatibility guard; current upstream
pschichtel/powerdns 0.10.0 has no ServiceMonitor template, but a future
upstream bump cannot silently regress).
Each chart ships a tests/observability-toggle.sh that asserts the rule
in three cases (default off / explicit on opt-in / explicit off) — runs
under blueprint-release.yaml's chart-test gate (added
|
||
|
|
d34facc040 |
fix(bp-*): observability toggles default false — break circular CRD dependency
bp-cilium@1.1.0 install fails on every fresh Sovereign with:
no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1"
— ensure CRDs are installed first
Cascades to all 10 other bp-* HelmReleases ("dep is not ready") since
bp-cilium is the root of the bootstrap dep graph. Verified live on
omantel.omani.works 2026-04-29 (issue #182).
Root cause: platform/cilium/chart/values.yaml and
platform/cert-manager/chart/values.yaml hardcoded
`serviceMonitor.enabled: true`. The monitoring.coreos.com/v1 CRDs ship
with kube-prometheus-stack — an Application-tier Blueprint that itself
depends on the bootstrap-kit. Hardcoding `true` creates a circular CRD
ordering: bp-cilium wants the CRD bp-kube-prometheus-stack provides, but
bp-kube-prometheus-stack cannot install before bp-cilium.
The `trustCRDsExist=true` mitigation only suppresses Helm's render-time
gate; the apiserver still rejects the resource at install-time.
Violates INVIOLABLE-PRINCIPLES.md #4 (never hardcode): observability
toggles MUST be operator-tunable, not chart-level constants assuming an
observability tier exists.
This commit:
A. Defaults every observability toggle false in the affected wrappers:
- platform/cilium/chart/values.yaml:
cilium.prometheus.enabled: false
cilium.prometheus.serviceMonitor.enabled: false
(trustCRDsExist removed — no longer relevant)
- platform/cert-manager/chart/values.yaml:
cert-manager.prometheus.enabled: false
cert-manager.prometheus.servicemonitor.enabled: false
- platform/crossplane/chart/values.yaml:
crossplane.metrics.enabled: false
(uniformity rule — does not break install but holds the invariant)
B. Bumps affected wrapper charts 1.1.0 → 1.1.1:
- bp-cilium, bp-cert-manager, bp-crossplane (leaves)
- bp-catalyst-platform (umbrella; deps repinned to 1.1.1 for the 3)
C. Updates clusters/_template/bootstrap-kit/* and
clusters/omantel.omani.works/bootstrap-kit/* HelmRelease versions to
1.1.1 so the live Sovereign picks up the fix on Flux reconcile.
D. Adds platform/<name>/chart/tests/observability-toggle.sh under each
affected chart. Each script asserts:
- default render produces zero monitoring.coreos.com refs
- opt-in render with --set <toggle>=true succeeds and produces a
ServiceMonitor (proves the toggle is wired)
- explicit-off render succeeds and produces zero refs
Wired into .github/workflows/blueprint-release.yaml via a new
"Run chart integration tests" step that executes every chart/tests/
*.sh on every publish — a regression that re-introduces a hardcoded
`true` fails the publish job before the OCI artifact is pushed.
E. Documents the rule in docs/BLUEPRINT-AUTHORING.md §11.2
"Observability toggles must default false". References Principle #4
and provides the canonical pattern (default off in wrapper values,
opt-in via per-cluster overlay at clusters/<sovereign>/...).
Per-chart audit table (which toggle was hardcoded → new default):
| Chart | Toggle | Was | Now |
|------------------|----------------------------------------------------------|------|-------|
| bp-cilium | cilium.prometheus.enabled | true | false |
| bp-cilium | cilium.prometheus.serviceMonitor.enabled | true | false |
| bp-cert-manager | cert-manager.prometheus.enabled | true | false |
| bp-cert-manager | cert-manager.prometheus.servicemonitor.enabled | true | false |
| bp-crossplane | crossplane.metrics.enabled | true | false |
| bp-flux | (no observability hardcodes) | n/a | n/a |
| bp-sealed-secrets| (no observability hardcodes) | n/a | n/a |
| bp-spire | (no observability hardcodes) | n/a | n/a |
| bp-nats-jetstream| (no observability hardcodes) | n/a | n/a |
| bp-openbao | (no observability hardcodes) | n/a | n/a |
| bp-keycloak | (no observability hardcodes) | n/a | n/a |
| bp-gitea | (no observability hardcodes) | n/a | n/a |
| bp-powerdns | (no observability hardcodes) | n/a | n/a |
| bp-catalyst-platform | (umbrella, no values overlay) | n/a | n/a |
Local gates green:
helm dep build ✓ all 3 affected charts
helm lint ✓ all 3
helm template ✓ all 3 — 0 monitoring.coreos.com refs in default
tests/observability-toggle.sh ✓ all 9 sub-cases pass
Closes the install path for bp-cilium 1.1.1 on a fresh Sovereign;
unblocks the full bp-* dep graph.
Refs: https://github.com/openova-io/openova/issues/182
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
54418bd5c9 |
feat(blueprint-release): verify upstream subchart present at every step (build/package/push/pull)
Hardens .github/workflows/blueprint-release.yaml against the
"hollow chart" failure mode that broke Phase 1 on every Sovereign
when bp-cert-manager:1.0.0 published as an empty wrapper carrying
only a ClusterIssuer overlay with no upstream cert-manager
subchart bytes inside the OCI artifact.
Adds four structural guards on every Blueprint publish:
Guard 1 (post helm-dependency-build) — for each entry in
Chart.yaml `dependencies:`, assert chart/charts/<dep>-<ver>.tgz
OR chart/charts/<dep>/Chart.yaml exists. Zero declared deps =
explicit hollow-chart failure with a link to issue #181 and
BLUEPRINT-AUTHORING.md §11.1 in the error message.
Guard 2 (post helm-package) — `tar -tzf` the produced .tgz and
assert each declared subchart is inside <chart_name>/charts/
in the package itself, not just in the working tree.
Guard 3 (post helm-push) — `helm pull` the artifact back from
GHCR and re-verify deps survived the round-trip; catches any
registry-side stripping or path mangling.
Smoke step — `helm template` the packaged chart with default
values; render must succeed and produce non-trivial output;
rendered manifests upload as a workflow artifact for forensics
on every run (success or fail).
Uses yq (v4.44.3 pinned) for streaming YAML parsing of the
declared `dependencies:` block — awk/grep on YAML is too fragile
to be the structural guard against hollow charts.
Documents the contract in docs/BLUEPRINT-AUTHORING.md §11.1
"Umbrella shape (hard contract — CI-enforced)" — every Blueprint
chart at platform/<name>/chart/ MUST declare upstream deps under
`dependencies:`, the four CI guards above structurally enforce it,
and the verifying-an-existing-artifact recipe (`helm pull` + `tar
tzf | grep`) is documented so the contract is operator-checkable
post-publish.
Preserves the per-Blueprint matrix shape and the
`workflow_dispatch.inputs.{blueprint,tree}` contract; no changes
to any Blueprint's Chart.yaml.
Closes #181
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7cafa3c894 |
docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay
Component-level architectural correction (two changes): 1. MinIO → SeaweedFS as unified S3 encapsulation layer The old design used MinIO for in-cluster S3 plus separate cold-tier configuration scattered across consumers. The new design positions SeaweedFS as the single S3 encapsulation layer: every Catalyst component talks to one endpoint (seaweedfs.storage.svc:8333). SeaweedFS internally handles hot tier (in-cluster NVMe), warm tier (in-cluster bulk), and cold tier (transparent passthrough to cloud archival storage — Cloudflare R2 / AWS S3 / Hetzner Object Storage / etc., chosen at Sovereign provisioning). One audit/lifecycle/encryption boundary instead of N. No Catalyst component talks to cloud S3 directly anymore — Velero, CNPG WAL archive, OpenSearch snapshots, Loki/Mimir/Tempo, Iceberg, Harbor blob store, Application buckets all share one S3 surface. 2. Apache Guacamole added as Application Blueprint §4.5 Communication Clientless browser-based RDP/VNC/SSH/kubectl-exec gateway. Keycloak SSO, full session recording to SeaweedFS for compliance evidence (PSD2/DORA/SOX). Composed into bp-relay. Replaces VPN+native-client distribution for auditable remote access. Component changes: - DELETED: platform/minio/ - CREATED: platform/seaweedfs/README.md (unified S3 + cold-tier encapsulation; bucket layout; multi-region replication via shared cold backend; migration-from-MinIO section) - CREATED: platform/guacamole/README.md (clientless remote-desktop gateway; GuacamoleConnection CRD; compliance integration via session recordings) Doc updates: PLATFORM-TECH-STACK §1+§3.5+§4.5+§5+§7.4; TECHNOLOGY-FORECAST L11+mandatory+a-la-carte counts (52 → 53); ARCHITECTURE §3 topology; SECURITY §4 DB engines; SOVEREIGN-PROVISIONING §1 inputs; SRE §2.5+§7; IMPLEMENTATION-STATUS §3; BLUEPRINT-AUTHORING stateful examples; BUSINESS-STRATEGY 13 component-count anchors + Relay product line; README.md backup row; CLAUDE.md folder count. Component README updates (S3 endpoint + dependency renames): cnpg, clickhouse, flink, gitea, iceberg, harbor, grafana, livekit, kserve, milvus, opensearch, flux, stalwart, velero (substantive rewrite of velero — now writes exclusively to SeaweedFS with cold-tier auto-routing). Products: relay, fabric. UI scaffold: products/catalyst/bootstrap/ui/src/shared/constants/components.ts — minio entry replaced with seaweedfs; velero+harbor deps updated; new guacamole entry added. VALIDATION-LOG entry "Pass 104 — MinIO → SeaweedFS swap + Guacamole add" captures the encapsulation principle and adds Lesson #22: storage tier policy belongs at the encapsulation boundary, not inside every consumer. Verification: zero remaining MinIO references in canonical docs (one intentional retention in TECHNOLOGY-FORECAST L37 explaining the swap); 53 platform/ folders matching all "53 components" anchors; bp-relay composition includes guacamole. |
||
|
|
0a6179dd21 |
docs(unified-repo-model): collapse SME and corporate to one shape — Application = Gitea Repo
Architectural correction. Replaces the previous "one Gitea repo per Environment with Apps as folders" rule with a single uniform shape that scales by configuration only: - Catalyst Application = one Gitea Repo (always, regardless of scale) - Branches develop/staging/main map to dev/stg/prod environments - 5 conventional Gitea Orgs per Sovereign: catalog (public mirror), catalog-sovereign (Sovereign-curated private Blueprints), one per Catalyst Organization (with shared-blueprints + N App repos), system (sovereign-admin scope) - EnvironmentPolicy CR lives in system/catalyst-config/policies/, same shape for SME and corporate; only field values differ Removes the SME-vs-corporate dual-shape design that violated the "Application is application" invariant. Teams primitive (proposed for corporate scale) is dropped — team boundaries emerge from CODEOWNERS at the App-repo level. RE-score thresholds and EnvironmentPolicy fields are universal defaults; only their values vary per Org's policy choice. Files updated line-by-line: GLOSSARY (Application + Environment definitions, new Gitea-Orgs section, 6 component-row updates), NAMING §11.2 (Realization 7-bullet rewrite), ARCHITECTURE (§1, §3 topology, §4 write-side ASCII, §7.1+§7.2+§7.3, §8 promotion, §9 multi-App linkage), PERSONAS-AND-JOURNEYS (§2 surfaces, §4.1 Ahmed, §4.2 Layla full rewrite), BLUEPRINT-AUTHORING §1 (catalog-sovereign source location), PLATFORM-TECH-STACK §2.2+§2.3, SECURITY §3, SOVEREIGN-PROVISIONING §5+§8+§10, IMPLEMENTATION-STATUS §5, SRE §14. VALIDATION-LOG entry "Pass 103 — UNIFIED REPO MODEL REFACTOR" captures the architectural correction and acknowledges the prior 102-pass audit anchored on the wrong shape (text-shape consistency was correct; the chosen text-shape was inadequate). Lesson #21 added: text-shape audits don't substitute for architectural review. Verification: zero remaining old-model assertions in canonical docs (grep clean for 'Environment Gitea repo', '/{org}/{org}-{env_type}', 'per-Environment Gitea repos', 'applications/<app>/values', etc.). |
||
|
|
c7a2fb05ea |
docs(pass-42): vague <sovereign-gitea> placeholders in BLUEPRINT-AUTHORING + NAMING; falco clean
Recurring drift category: vague composite placeholders like
<sovereign-domain-gitea> and <sovereign-gitea> standing in for the
canonical Catalyst control-plane DNS form gitea.{location-code}.{sovereign-domain}.
These survived Pass 29's DNS sweep because they don't match Pass 29's
grep patterns (<sovereign>.<domain>, <sovereign-domain>, etc.) —
different shape entirely (single hyphenated placeholder vs multi-segment).
BLUEPRINT-AUTHORING.md §1: <sovereign-domain-gitea>/<org>/shared-blueprints/bp-<name>/
→ gitea.<location-code>.<sovereign-domain>/<org>/shared-blueprints/bp-<name>/
plus inline pointer to NAMING §5.1.
NAMING-CONVENTION.md §11.2 step 1: <sovereign-gitea>/{org}/{org}-{env_type}
abstract pattern → gitea.{location-code}.{sovereign-domain}/{org}/{org}-{env_type}.
The authoritative naming doc was teaching a non-canonical shorthand
while its example showed the canonical form — second drift instance in
§11.2 (Pass 37 fixed example URL, Pass 42 fixes abstract pattern).
BLUEPRINT-AUTHORING.md §1-§14 deep re-scan: clean apart from §1 fix.
§8 Crossplane Compositions verified — compose.openova.io/v1alpha1 is
intentionally separate from catalyst.openova.io/v1alpha1 (Crossplane
XRDs use their own group; Pass 1's unification was for Catalyst's own
CRDs only).
platform/falco/README.md: clean.
|
||
|
|
4793cab8b6 |
docs(pass-29): DNS-placeholder sweep across canonical docs
The recurring drift: Catalyst control-plane DNS placeholders that omit the
<location-code> segment, producing forms like gitea.<sovereign>,
gitea.<sovereign>.<domain>, gitea.<sovereign-domain>, keycloak.<domain>.
Per NAMING §5.1 the canonical form is
{component}.{location-code}.{sovereign-domain} (e.g. gitea.hfmp.openova.io).
The shorter forms aren't just abbreviations — they collapse the multi-region
location dimension and re-drift every time a reader reads them as obvious
shorthand.
Fixes:
- CLAUDE.md "Customer Sync" — both gitea.<sovereign>/catalog/... lines.
- docs/SOVEREIGN-PROVISIONING.md §3 DNS-records bullet (3 lines) + §5
Day-1 login line.
- docs/ARCHITECTURE.md §4 write-path Gitea label.
- docs/BLUEPRINT-AUTHORING.md §6.4 private-Blueprint Studio target.
- platform/librechat/README.md Keycloak issuer (Pass 22 marked clean and
missed this — banner scans miss YAML-block drift).
platform/nemo-guardrails/README.md verified clean.
Final grep confirms only canonical forms remain. Validation log Pass 29
entry added with the recurring-drift-pattern note for future passes.
|
||
|
|
a1f3076888 |
docs(pass-21): BLUEPRINT-AUTHORING §11 CI pipeline aligned with §2 monorepo fan-out
Pass 21 — drift-detection on BLUEPRINT-AUTHORING + platform/langfuse. One real fix. BLUEPRINT-AUTHORING.md §11 (CI pipeline): - Old version showed `on: push # branch: main # tags: vX.Y.Z` — the per-Blueprint-repo CI shape that was explicitly rejected when we locked Option A (monorepo canonical) in Pass 1. - §2 already establishes monorepo + path-matrix tag form `platform/<name>/v1.2.3` / `products/<name>/v1.2.3`. §11 should have matched §2 from the start; this slipped through previous passes. - Rewrote §11: single root-level CI, on.pull_request.paths triggers validate, on.push.tags: platform/*/v* | products/*/v* triggers build-and-sign with tag-parse → folder-detect → fan-out publish. Includes worked example: tagging `platform/wordpress/v1.3.0` builds `platform/wordpress/` and publishes ghcr.io/openova-io/bp-wordpress:1.3.0. platform/langfuse/README.md: clean. Banner correct. "Used by: OpenOva Cortex" is acceptable commercial phrasing alongside the technical bp-cortex reference. VALIDATION-LOG: Pass 21 entry added. Refs #37 |
||
|
|
8d351d7001 |
docs(iter-6-8): security/provisioning/blueprint corrections + OCI artifact naming
SECURITY (iter 6): - "Environment repo" → "Environment Gitea repo" in §3 secrets diagram. - "ChangePolicy enforces approvals" → "EnvironmentPolicy enforces approvals" in §9 SOC2 row (ChangePolicy was a fictional CRD — EnvironmentPolicy is the real one defined in ARCHITECTURE §8). - "Catalyst's compliance-controller surfaces evidence" → "evidence surfaced via Catalyst console audit views and SIEM exports" (compliance-controller wasn't defined elsewhere; this avoids inventing new components in compliance prose). SOVEREIGN-PROVISIONING (iter 7): - "vault-stored" → "stored in OpenBao on the provisioner" (Vault was replaced by OpenBao; "vault-stored" was generic English but read as a contradiction). BLUEPRINT-AUTHORING (iter 8): - OCI artifact naming locked: `ghcr.io/openova-io/bp-<name>:<semver>` where `<name>` is the folder name. The `bp-` prefix lives in the OCI artifact name (self-identifying), not the folder name. Fixed in §1, §10, §11, §13 — and propagated to README.md so the pattern is consistent across the repo. - Crossplane Composition example: `compositeTypeRef.apiVersion` changed from `bp-wordpress.openova.io/v1alpha1` (per-Blueprint group, ugly) to `compose.openova.io/v1alpha1` (shared XRD group across all Blueprints). - §11 CI pipeline final step: "publish blueprint.yaml as the manifest" → "as the OCI manifest's metadata layer" (clearer about what it does in the OCI sense). Refs #37 |
||
|
|
2c4902b409 |
docs(iter-1): add IMPLEMENTATION-STATUS, fix wrong-org refs, reconcile monorepo
First validation iteration. Three concrete corrections. 1. Add docs/IMPLEMENTATION-STATUS.md as the bridge between target architecture and current code state. Status legend (✅ / 🚧 / 📐 / ⏸) applied per-component. Catalyst control plane = mostly 📐. Component READMEs = 🚧 (README only, no Blueprint manifests yet). products/axon = ✅ (only product with real code). core/ = 📐 (just .gitkeep). 2. Status banner added to ARCHITECTURE, SECURITY, SOVEREIGN-PROVISIONING, BLUEPRINT-AUTHORING, PERSONAS-AND-JOURNEYS, PLATFORM-TECH-STACK, SRE pointing readers at IMPLEMENTATION-STATUS.md before they treat any described feature as built. GLOSSARY also references it. 3. Architectural decision (Option A — monorepo canonical): - Each platform/<name>/ and products/<name>/ folder is the source of ONE Blueprint, published as ghcr.io/openova-io/<name>:<semver> by CI fan-out from the monorepo root. - BLUEPRINT-AUTHORING.md §1, §2, §13 rewritten to match. - README.md "what's in this repo" rewritten to clarify monorepo + OCI-fan-out shape; no longer claims every directory is a Blueprint in a way that contradicts BLUEPRINT-AUTHORING. Wrong-org fixes (3 places): - docs/PERSONAS-AND-JOURNEYS.md:13 github.com/openova → openova-io - docs/BLUEPRINT-AUTHORING.md:13 github.com/openova → openova-io - docs/BLUEPRINT-AUTHORING.md:404 github.com/openova → openova-io - docs/BLUEPRINT-AUTHORING.md ghcr.io/openova/* (3 refs) → openova-io API group consistency: - All references unified to catalyst.openova.io/v1alpha1 (was mixed v1 / v1alpha1; v1alpha1 is correct since the CRDs are design-stage with no implementation). core/README.md updated to honestly describe the directory tree as "target structure with .gitkeep placeholders" rather than implying the apps/console, apps/projector, etc. binaries already exist. The legacy apps/bootstrap and apps/manager directories are acknowledged as transitional placeholders that will be removed when the new apps/ layout is scaffolded. CLAUDE.md and .claude/project-memory.md updated to put IMPLEMENTATION-STATUS.md second in the read-first ordering. Refs #37 |
||
|
|
d51a3fba4d |
docs: add canonical Catalyst documentation set
Six new docs that establish the unified Catalyst model — Sovereign as
deployed instance, Organization as multi-tenancy unit, Environment as
{org}-{env_type} scope, Application as user-facing handle, Blueprint as
unified module+template successor.
- docs/GLOSSARY.md single source of truth for terminology;
every other doc defers to it; banned terms
(tenant, operator-as-entity, module, template,
Backstage, etc.) listed with replacements.
- docs/ARCHITECTURE.md overall Catalyst architecture: control plane
vs application Blueprints, write path
(Git → Flux → K8s + Crossplane), read path
(CQRS via NATS JetStream → projector → SSE),
SPIFFE/SPIRE workload identity, OpenBao
independent Raft per region (no stretched
cluster), Keycloak per-Org (SME) vs
per-Sovereign (corporate).
- docs/PERSONAS-AND-JOURNEYS.md personas × journeys matrix; only
three first-class surfaces (UI, Git, API);
explicit removal of Terraform/Pulumi/CLI as
user-facing IaC; Application card anatomy.
- docs/SECURITY.md identity (workload + user), OpenBao + ESO
credential flow, dynamic credentials with
auto-rotation sidecar, multi-region
OpenBao (independent Raft per region with
async perf replication — explicitly NOT
stretched), rotation policy CRDs, threat
model.
- docs/SOVEREIGN-PROVISIONING.md Phase 0 (catalyst-provisioner +
OpenTofu one-shot) → Phase 1 (Crossplane
adopts) → Phase 2 (self-sufficient Catalyst
control plane); air-gap procedure;
Organization migration; decommission.
- docs/BLUEPRINT-AUTHORING.md Blueprint CRD spec, configSchema,
placementSchema, depends, manifests,
overlays; Crossplane Composition authoring
for non-K8s; signing/publishing pipeline;
public vs private (Org-scoped) visibility;
contribution path.
Refs #37
|