# Target Prerequisites

**UNOFFICIAL/EXPERIMENTAL**

Render parity is only the first question.

For easy charts, proving that `cub installer` renders the same Kubernetes
objects as Helm is enough to start. Serious charts also depend on things that
are already true, or must become true, in the target cluster.

This repo calls those requirements **target prerequisites**.

Examples:

- CRDs must already exist before a CRDs-off base is applied.
- A Secret must exist when the chart is configured to reuse one.
- A webhook certificate may be generated by a controller after apply.
- An APIService may only become healthy after the backing service is ready.
- A Helm startup check may need to become a post-apply observation.

Each base records its requirements in the variant file and in the generated
render intent. In the render intent, `targetFacts.declared` is the list the base
expects before deployment. `targetFacts.actions` is different: it records work
derived from a failed or blocked live run. An empty action list does not cancel
a declared Secret or CRD requirement.

The useful product claim is not only:

```text
we rendered the same YAML
```

It is:

```text
we rendered the same desired objects, named the target prerequisites,
staged or checked them, observed the live result, and recorded the boundary
of the claim
```

## Why This Is Better Than Plain Helm

Plain Helm can succeed or fail while mixing several concerns into one install
step: render, prerequisite setup, hook execution, controller readiness, and live
cluster behavior.

The catalog splits those concerns apart.

| Concern | Catalog treatment |
| --- | --- |
| Rendered object set | Compare regular Helm with `cub installer` output. |
| Prerequisites | Record target facts such as required CRDs or Secrets. |
| Lifecycle behavior | Route hooks and controller-owned fields into observations or support decisions. |
| Live result | Run local, GitOps, parity, or cub-scout observation lanes. |
| Claim boundary | Keep pass, watch, blocked, and refused rows visible. |

That is where the value starts for hard charts. YAML parity is necessary, but
it does not prove that the target was ready or that the workload became usable.

## Real Examples

### cert-manager

The default cert-manager base does not render CRDs. That is a normal Helm
shape, but the target cluster still needs the cert-manager CRDs before the
controller can be treated as working.

The catalog now records those CRDs as target facts on the default base, syncs
them into the installer package, and proves the base in the strict two-cluster
Helm-vs-installer lane.

Evidence:

- base target facts:
  [`recipes/jetstack/cert-manager/v1.20.2/variants/default/variant.yaml`](../../recipes/jetstack/cert-manager/v1.20.2/variants/default/variant.yaml)
- installer package requirement:
  [`packages/jetstack/cert-manager/v1.20.2/installer.yaml`](../../packages/jetstack/cert-manager/v1.20.2/installer.yaml)
- live parity receipt:
  [`runs/live-kind-parity/jetstack-cert-manager-default/receipt.yaml`](../../runs/live-kind-parity/jetstack-cert-manager-default/receipt.yaml)
- lifecycle observation:
  [`runs/lifecycle-observations/cert-manager-eso/jetstack-cert-manager-default/receipt.yaml`](../../runs/lifecycle-observations/cert-manager-eso/jetstack-cert-manager-default/receipt.yaml)

### Vertical Pod Autoscaler

The VPA chart uses a generated webhook TLS Secret. Treating that as invisible
Helm behavior would make the install look simpler than it is.

The catalog records the Secret as a target fact and proves both VPA bases in
the two-cluster parity lane.

Evidence:

- target fact in the recipe:
  [`recipes/autoscaler/vertical-pod-autoscaler/0.9.0/variants/default/variant.yaml`](../../recipes/autoscaler/vertical-pod-autoscaler/0.9.0/variants/default/variant.yaml)
- live default receipt:
  [`runs/live-kind-parity/autoscaler-vertical-pod-autoscaler-default/receipt.yaml`](../../runs/live-kind-parity/autoscaler-vertical-pod-autoscaler-default/receipt.yaml)
- live no-CRDs receipt:
  [`runs/live-kind-parity/autoscaler-vertical-pod-autoscaler-no-crds/receipt.yaml`](../../runs/live-kind-parity/autoscaler-vertical-pod-autoscaler-no-crds/receipt.yaml)

### OpenTelemetry Operator

The no-CRDs base is useful only if the target cluster already has the required
OpenTelemetry CRDs and cert-manager prerequisites. Those are now explicit
target facts, not hidden assumptions.

Evidence:

- no-CRDs target facts:
  [`recipes/open-telemetry/opentelemetry-operator/0.114.0/variants/no-crds/variant.yaml`](../../recipes/open-telemetry/opentelemetry-operator/0.114.0/variants/no-crds/variant.yaml)
- live no-CRDs receipt:
  [`runs/live-kind-parity/open-telemetry-opentelemetry-operator-no-crds/receipt.yaml`](../../runs/live-kind-parity/open-telemetry-opentelemetry-operator-no-crds/receipt.yaml)
- live default receipt:
  [`runs/live-kind-parity/open-telemetry-opentelemetry-operator-default/receipt.yaml`](../../runs/live-kind-parity/open-telemetry-opentelemetry-operator-default/receipt.yaml)

## How To Use This As A User

Before deploying a base, check the per-chart catalog page and the variant file.
The chart page links the matching render intent, where the declaration, rendered
objects, lifecycle routes, and Argo CD or Flux handling are kept together.

If the base lists target prerequisites:

1. Stage them before applying the base, or choose a base that includes them.
2. Keep the prerequisite source visible in the pull request or ticket.
3. Run the relevant live check after apply.
4. Do not call the install production-supported until the target scope has a
   production support decision.

The routing rule is:

| Change or requirement | Put it here |
| --- | --- |
| It changes the Helm-rendered object set. | Recipe or base variant. |
| It must already exist in the target cluster. | Target prerequisite / target fact. |
| It is a post-render environment, region, customer, target, label, approval, or observation choice. | Derived ConfigHub variant. |
| It is produced by a controller after apply. | Lifecycle observation. |
| It is cluster-dependent hook behavior. | Hook lifecycle route, observation, or support decision. |

For the aggregate view, start with
[`data/live-kind-parity/summary.md`](../../data/live-kind-parity/summary.md)
and [`data/master-catalog-matrix/summary.md`](../../data/master-catalog-matrix/summary.md).
