Architecture

How Kubo reconciles a Stack

The flow

A Stack manifest lands in the cluster (from kubectl, ArgoCD, or the kubify-website SaaS portal). The kubo operator watches every Stack CR, pulls its OCI bundle, and installs components as Helm releases in dependency order — propagating secrets and reconciling Istio routing along the way.

flowchart TD
    WEB["kubify-website (SaaS portal)
Customer signs up or deploys a stack"] subgraph K8S["Kubernetes cluster (AKS)"] direction TB subgraph SYS["kubo-system namespace"] OP["kubo operator
(controller-manager)"] CRDS["Stack CRD · StackDefinition CRD"] PULL["acr-pull-secret · stack-a-secrets"] end subgraph TEN["stack-a namespace (tenant)"] STACK["Stack CR (stack-a)"] HELM["Helm releases
(one per declared component)"] SECRETS["Propagated secrets
(from kubo-system)"] VS["Istio VirtualService
(routing)"] end end OCI["OCI registries (ghcr.io)
product bundle: charts + images + values, versioned"] WEB -- "kubectl apply / ArgoCD Stack manifest" --> STACK STACK -- "watched by" --> OP OP -- "reads CRD schema" --> CRDS OP -- "pulls OCI artifacts" --> OCI OP -- "Helm install/upgrade" --> HELM OP -- "copies secrets (secretsRef)" --> SECRETS OP -- "reconciles" --> VS PULL -- "secretsRef propagation" --> SECRETS

The reconcile loop

Every change to a Stack — a new bundle tag, a values override, a pause, or a manual reconcile trigger — re-enters the same loop. Components are planned into dependency waves, deployed in order, and status rolls back up into the Stack CR.

flowchart LR
    EVT["Stack event
(spec change, bundle tag,
reconcile trigger, un-pause)"] PLAN["Plan dependency waves
(topological order over
dependsOn / dependsOnReady)"] DEPLOY{"Deployment mode"} DIRECT["Direct
embedded Helm engine:
install / upgrade in order"] FLUX["Flux
compile to HelmReleases,
helm-controller deploys"] STATUS["Roll up per-component phase,
revision & conditions
into Stack status"] PRUNE["Prune releases dropped
from the spec"] EVT --> PLAN --> DEPLOY DEPLOY -- "Direct" --> DIRECT DEPLOY -- "Flux" --> FLUX DIRECT --> STATUS FLUX --> STATUS STATUS --> PRUNE --> EVT

Shared operators, tenant workloads

Cluster-scoped operators (Vault, Spark, Istio, …) install once into a shared operators namespace and watch every tenant namespace. Tenant charts never duplicate them — a tenant Stack declares which operators it needs, and the operator wires credentials, secrets, and routing across namespace boundaries.

flowchart TD
    subgraph OPS["operators namespace (shared, one per cluster)"]
        VAULT["Vault"]
        SPARK["Spark"]
        ISTIO["Istio"]
    end
    subgraph TA["tenant namespace"]
        STACK["Stack
operators: vault ✓ istio ✓"] SECRETS["tenant secrets
(seeded from kubo-system)"] APPS["app workloads"] end STACK -- "enables" --> VAULT STACK -- "enables" --> ISTIO VAULT -- "watches tenant ns" --> SECRETS ISTIO -- "watches tenant ns" --> APPS

Deployment modes

Direct

The operator's embedded Helm engine installs and upgrades releases itself, in dependency order. No extra moving parts — simplest path, full status detail per component.

spec.mode: Direct

Flux

The operator compiles each component into a Flux HelmRelease and hands execution to helm-controller. Choose it when the cluster already standardizes on Flux for GitOps continuity.

spec.mode: Flux

Where everything lives

LocationContentsRole
kubo-systemoperator (controller-manager), Stack + StackDefinition CRDs, seeded secrets, pull secretscontrol plane — watches every Stack, propagates credentials
operators nsVault, Spark, Istio, cert-manager, …cluster-scoped services, installed once, watching all tenants
tenant nsStack CR, Helm releases, propagated secrets, Istio VirtualServicesthe running product — one namespace per tenant
OCI registryproduct bundles (charts + images + values, versioned)the delivery artifact — portable across clusters and orgs

Bundle once, deploy anywhere

Bundles are plain OCI artifacts on any registry — ghcr.io included. Because a Stack references a bundle by URL, the same artifact deploys to any cluster: dev, staging, or a customer's AKS estate. Composition is by reference, so products can build on shared bundles without vendoring each other's charts.

Shared operators

Vault, Spark, Istio and friends install once per cluster into the operators namespace and watch every tenant namespace. Tenants get the operators they declare — never duplicate controllers, never cross-tenant coupling.