PowerDNS lua-records (`ifurlup`, `pickclosest`, `ifportup`) cover everything k8gb was doing — geo-aware response selection, health-checked failover, weighted round-robin — at the authoritative DNS layer. Eliminates a separate K8s controller, CRD set, and CoreDNS plugin from every Sovereign. Changes: - platform/k8gb/ deleted (Chart.yaml, values.yaml, blueprint.yaml never authored — only README existed) - products/catalyst/bootstrap/ui/public/component-logos/k8gb.svg deleted - componentGroups.ts: remove k8gb component (PowerDNS already there) - componentLogos.tsx: drop logo_k8gb + k8gb map entry - model.ts DEFAULT_COMPONENT_GROUPS spine: replace k8gb with powerdns - StepInfrastructure.tsx: copy refers to PowerDNS lua-records, not k8gb - provision.html: replace k8gb tile and edges with powerdns - catalog.generated.ts regenerated (now includes bp-powerdns) - docs sweep — every k8gb reference in PLATFORM-TECH-STACK, NAMING- CONVENTION, SOVEREIGN-PROVISIONING, SRE, ARCHITECTURE, GLOSSARY, COMPONENT-LOGOS, IMPLEMENTATION-STATUS, BUSINESS-STRATEGY, TECHNOLOGY-FORECAST, README, infra/hetzner/README, platform READMEs (cilium, external-dns, failover-controller, litmus, flux, opentofu) rewritten to point at PowerDNS lua-records / MULTI-REGION-DNS.md. Historical entries in VALIDATION-LOG.md preserved as audit trail. - New docs/MULTI-REGION-DNS.md — canonical reference for the lua-record patterns (ifurlup all/pickclosest/pickfirst, ifportup, pickwhashed), Application Placement → lua-record selector mapping, when to add a second Sovereign region, operational checks. Closes #171. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
509 lines
24 KiB
Markdown
509 lines
24 KiB
Markdown
# OpenOva Naming Convention
|
||
|
||
**Status:** Authoritative | **Updated:** 2026-04-27
|
||
|
||
This document defines the unified naming standard for all OpenOva infrastructure, platform, and application resources across all cloud providers, regions, and Catalyst Sovereigns. All new resources **must** follow this convention. Existing resources adopt the new names when touched.
|
||
|
||
> **Glossary**: see [`GLOSSARY.md`](GLOSSARY.md). This document deals with how to compose names from the dimensions defined there.
|
||
|
||
---
|
||
|
||
## 1. Principles
|
||
|
||
### 1.1 Dimension-Based Naming
|
||
|
||
Every name is a **composition of typed dimensions** — never free-text, never descriptive prose. Each dimension has a defined abbreviation. Names are deterministic: given the dimensions, the name is computable.
|
||
|
||
### 1.2 Don't Repeat the Parent
|
||
|
||
When an object lives inside a container that already encodes location, **do not repeat** that information.
|
||
|
||
```
|
||
Parent provides context → Child adds only what is NEW
|
||
|
||
Provider + Region (cloud account) → VPC/Network name = bb + env_type
|
||
VPC → Subnet/SG/Route name = purpose only
|
||
K8s Cluster → vcluster name = org only
|
||
Namespace = env_type + app (or app only)
|
||
vcluster → Namespace = app only (when 1 vcluster = 1 Environment)
|
||
Namespace → Secret/ConfigMap/Deployment = purpose only
|
||
|
||
No parent (global scope) → Full encoding required
|
||
DNS names, K8s contexts, Crossplane CRs, server names
|
||
```
|
||
|
||
### 1.3 Building Blocks, Not Failover Roles
|
||
|
||
Clusters are named by their **functional security zone** (building block), not by a failover role such as "primary" or "dr". Geographic redundancy is achieved by running the **same building blocks in multiple regions** — PowerDNS lua-records (`ifurlup`, `pickclosest`) handle traffic distribution authoritatively at the DNS layer. The cluster name never changes; the routing does. Calling a cluster "primary" is operationally incorrect because after failover the other region becomes active — the building block label remains stable regardless.
|
||
|
||
### 1.4 Tags Carry What Names Cannot
|
||
|
||
Resource names are kept minimal. Cloud tags and Kubernetes labels carry the **full context** for cross-cloud dashboards, billing, and compliance audits.
|
||
|
||
### 1.5 Organization Identity Lives in the vcluster Layer
|
||
|
||
In multi-tenant deployments, Organization identity is expressed through the **vcluster name** (and on the host cluster, the Kubernetes namespace that hosts that vcluster) — never embedded in resource names below. This follows Principle 1.2: the vcluster is the parent that provides Organization context.
|
||
|
||
---
|
||
|
||
## 2. Dimension Taxonomy
|
||
|
||
### 2.1 Provider
|
||
|
||
| Full name | 2-char | 1-char |
|
||
|-----------|--------|--------|
|
||
| Hetzner | `hz` | `h` |
|
||
| Huawei Cloud | `hw` | `w` |
|
||
| OCI (Oracle Cloud) | `oci` | `o` |
|
||
| AWS | `aws` | `a` |
|
||
| GCP | `gcp` | `g` |
|
||
| Azure | `az` | `z` |
|
||
| Contabo | `ct` | `c` |
|
||
|
||
### 2.2 Region
|
||
|
||
Region codes are **provider-scoped**. The same 3-char code is never reused across providers.
|
||
|
||
#### Hetzner
|
||
|
||
| Location | 3-char | 1-char | Notes |
|
||
|----------|--------|--------|-------|
|
||
| Falkenstein, DE | `fsn` | `f` | |
|
||
| Nuremberg, DE | `nbg` | `n` | |
|
||
| Helsinki, FI | `hel` | `l` | `h` reserved for Hetzner provider |
|
||
| Ashburn, VA, US | `ash` | `a` | |
|
||
| Hillsboro, OR, US | `hil` | `i` | `h` reserved → use `i` |
|
||
| Singapore, SG | `sin` | `s` | |
|
||
|
||
#### Huawei Cloud
|
||
|
||
| Location | 3-char | 1-char |
|
||
|----------|--------|--------|
|
||
| AP Southeast (Singapore) | `apse` | `p` |
|
||
| CN North (Beijing) | `cnn` | `c` |
|
||
| LA South (São Paulo) | `las` | `q` |
|
||
| ME (Riyadh) | `mer` | `r` |
|
||
|
||
#### OCI
|
||
|
||
| Location | 3-char | 1-char |
|
||
|----------|--------|--------|
|
||
| ME Dubai | `dxb` | `x` |
|
||
| EU Frankfurt | `fra` | `r` |
|
||
| AP Singapore | `sg` | `g` |
|
||
| US Ashburn | `iad` | `d` |
|
||
| AP Sydney | `syd` | `y` |
|
||
|
||
#### Contabo (legacy)
|
||
|
||
| Location | 3-char | 1-char |
|
||
|----------|--------|--------|
|
||
| EU (generic) | `eu` | `e` |
|
||
|
||
> **Collision rule**: the provider's 1-char code takes precedence. Region codes must not reuse the provider's 1-char. The table above already resolves all known collisions.
|
||
|
||
### 2.3 Building Block
|
||
|
||
Building blocks describe the **security zone** the cluster or resource belongs to. This is stable regardless of which region is serving traffic.
|
||
|
||
| Full name | 3-char | 1-char | Purpose |
|
||
|-----------|--------|--------|---------|
|
||
| Restricted Trust Zone | `rtz` | `r` | Production workloads — most restricted, no direct internet exposure |
|
||
| DMZ (edge) | `dmz` | `d` | Internet-facing — WAF, ingress controllers, WireGuard endpoints |
|
||
| Management | `mgt` | `m` | Catalyst control plane — console, projector, gitea, JetStream, OpenBao, Keycloak, etc. |
|
||
|
||
### 2.4 Env Type
|
||
|
||
Renamed from the older `{env}` to avoid collision with the user-facing **Environment** object (see §11). Values unchanged.
|
||
|
||
| Full name | 3-char | 1-char |
|
||
|-------------|--------|--------|
|
||
| Production | `prod` | `p` |
|
||
| Staging | `stg` | `s` |
|
||
| UAT | `uat` | `u` |
|
||
| Development | `dev` | `d` |
|
||
| POC | `poc` | `c` |
|
||
|
||
### 2.5 Organization
|
||
|
||
| Field | Rule |
|
||
|---|---|
|
||
| Format | Lowercase slug, hyphenated. Length 3–32 characters. Must match `^[a-z][a-z0-9-]{2,31}$`. |
|
||
| Reserved | `system`, `flux`, `crossplane`, `catalyst`, `gitea`, `kube-*`, anything matching a provider/region/bb/env_type code. |
|
||
| Examples | `acme`, `bankdhofar`, `muscatpharmacy`, `omantel-internal` |
|
||
| Source of truth | The Organization CRD on the Sovereign's management cluster. |
|
||
|
||
---
|
||
|
||
## 3. Core Patterns
|
||
|
||
All **global** objects (no containing parent that encodes location) use the full pattern:
|
||
|
||
```
|
||
{provider}-{region}-{bb}-{env_type}
|
||
```
|
||
|
||
All **scoped** objects use only what the parent does not already provide — see §4.
|
||
|
||
The **Catalyst Environment** (logical, user-facing) uses:
|
||
|
||
```
|
||
{org}-{env_type}
|
||
```
|
||
|
||
See §11 for the Environment object definition.
|
||
|
||
---
|
||
|
||
## 4. Object-Type Reference
|
||
|
||
### 4.1 Global Objects (full encoding always required)
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| K8s cluster context | `{prov}-{reg}-{bb}-{env_type}` | `hz-fsn-rtz-prod` |
|
||
| Server / VM | `{prov}{reg}{bb}-{app}-{#}{env_type}` | `hzfsnr-k8s-1p` |
|
||
| DNS location code | `{p}{r}{b}{e}` (4 chars) | `hfrp` |
|
||
| Crossplane CR (on mgt plane) | `{prov}-{reg}-{bb}-{env_type}-{type}` | `hz-fsn-rtz-prod-vpc` |
|
||
| Flux GitRepository (Sovereign-level) | `{prov}-{reg}-{bb}-{env_type}` | `hz-fsn-rtz-prod` |
|
||
|
||
### 4.2 Within Provider + Region (don't repeat provider/region)
|
||
|
||
| Object | Pattern | Example | Parent context |
|
||
|--------|---------|---------|----------------|
|
||
| VPC / Network | `{bb}-{env_type}` | `rtz-prod`, `dmz-prod` | provider + region |
|
||
| Cloud SSH Key | `{purpose}-{env_type}` | `cluster-prod` | provider + region |
|
||
| Load Balancer | `{purpose}-{env_type}` | `ingress-prod` | provider + region |
|
||
| Floating IP / EIP | `{purpose}-{env_type}` | `ingress-prod` | provider + region |
|
||
| Object storage bucket | `{env_type}-{purpose}` | `prod-tf-state` | provider + region |
|
||
| Volume snapshot policy | `{purpose}` | `daily-7d` | provider + region |
|
||
|
||
### 4.3 Within VPC / Network (don't repeat provider/region/vpc)
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| Subnet | `{purpose}` | `workers`, `lb`, `cp` |
|
||
| Security Group / Firewall Rule | `{purpose}` | `k8s-nodes`, `lb-https` |
|
||
| Route Table | `{purpose}` | `default`, `nat` |
|
||
| NAT Gateway | `{purpose}` | `default` |
|
||
| VPC Peering / Network Attachment | `to-{target-bb}` | `to-dmz`, `to-mgt` |
|
||
|
||
### 4.4 Within K8s Cluster (host level)
|
||
|
||
The **host cluster** (`{prov}-{reg}-{bb}-{env_type}`) hosts one vcluster per Organization plus Catalyst control-plane workloads.
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| Catalyst control-plane Namespace | `catalyst-{component}` | `catalyst-projector`, `catalyst-gitea` |
|
||
| Per-Org vcluster hosting Namespace | `{org}` | `acme`, `bankdhofar` |
|
||
| Helm release (Catalyst level) | `{component}` | `external-secrets`, `cert-manager` |
|
||
| Flux Kustomization (Catalyst level) | `{scope}` | `infrastructure`, `catalyst`, `crossplane` |
|
||
| Certificate | `{domain-purpose}` | `openova-io-wildcard` |
|
||
| ServiceAccount | `{role}` | `flux-reconciler`, `projector` |
|
||
| PVC | `{purpose}` | `data`, `wal` |
|
||
|
||
### 4.5 Within Namespace (don't repeat anything above)
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| Secret | `{purpose}` | `db-credentials`, `hcloud-token` |
|
||
| ConfigMap | `{purpose}` | `app-config`, `grafana-dashboards` |
|
||
| Deployment / StatefulSet | `{component}` | `api`, `worker`, `ui` |
|
||
| Service | `{component}` | `api`, `grpc` |
|
||
| NetworkPolicy | `{rule}` | `deny-all`, `allow-ingress` |
|
||
| Ingress / IngressRoute | `{component}` | `api`, `ui` |
|
||
|
||
### 4.6 Multi-Organization on a Sovereign Management Cluster
|
||
|
||
The management cluster (`{prov}-{reg}-mgt-{env_type}`) hosts Crossplane and Catalyst components. Per-Organization isolation lives in the **vcluster** layer (see §4.7), not in resource names below it.
|
||
|
||
```
|
||
Host cluster: hz-fsn-mgt-prod
|
||
Namespace: catalyst-projector ← Catalyst control-plane component
|
||
Namespace: catalyst-gitea
|
||
Namespace: acme ← parent namespace for Org acme's resources
|
||
vcluster: acme ← (see §4.7)
|
||
Crossplane CR: hz-fsn-rtz-prod-vpc ← no Organization slug in CR name; namespace is parent
|
||
Secret: hcloud-token ← purpose only; namespace provides Org context
|
||
Namespace: bankdhofar
|
||
vcluster: bankdhofar
|
||
...
|
||
```
|
||
|
||
Organization identity is **never** embedded in the names of resources below the namespace. The namespace is the parent.
|
||
|
||
### 4.7 vcluster Naming (NEW)
|
||
|
||
The vcluster is the per-Organization control plane on a parent host cluster. One vcluster per Organization per host cluster.
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| vcluster (within host cluster) | `{org}` | `acme`, `bankdhofar`, `muscatpharmacy` |
|
||
| vcluster fully-qualified ref (cross-cluster, kubeconfig context) | `{prov}-{reg}-{bb}-{env_type}-{org}` | `hz-fsn-rtz-prod-acme`, `hz-hel-rtz-prod-acme` |
|
||
| Flux GitRepository inside vcluster | `environment` | `environment` |
|
||
| Flux Kustomization inside vcluster | `applications` | `applications` |
|
||
|
||
**Sibling vclusters** named `acme` on `hz-fsn-rtz-prod` and on `hz-hel-rtz-prod` are two physical realizations of the same logical Catalyst Environment `acme-prod` (see §11).
|
||
|
||
### 4.8 Within a vcluster (per-Application namespace)
|
||
|
||
Inside an Organization's vcluster, each Application gets its own namespace.
|
||
|
||
| Object | Pattern | Example |
|
||
|--------|---------|---------|
|
||
| Application namespace | `{app}` | `marketing-site`, `blog`, `shared-postgres` |
|
||
| All workloads, secrets, configmaps inside | `{component}` / `{purpose}` | `api`, `worker`, `db-credentials` |
|
||
|
||
---
|
||
|
||
## 5. DNS Pattern
|
||
|
||
### 5.1 Structure
|
||
|
||
Two patterns coexist depending on whether the DNS is for **Catalyst control-plane** services or for **Application** endpoints inside an Organization.
|
||
|
||
#### Catalyst control-plane DNS (Sovereign domain)
|
||
|
||
```
|
||
{component}.{location-code}.{sovereign-domain}
|
||
```
|
||
|
||
Example: `console.hfmp.openova.io`, `gitea.hfmp.openova.io`.
|
||
|
||
Used for Catalyst's own services on the management cluster of a Sovereign. The location code is a 4-character dense encoding of provider + region + building-block + env_type, derived from the 1-char columns in §2.
|
||
|
||
#### Application DNS (Environment domain)
|
||
|
||
```
|
||
{app}.{environment}.{sovereign-domain}
|
||
```
|
||
|
||
OR, for white-label Sovereigns (corporate self-host):
|
||
|
||
```
|
||
{app}.{environment}.{org-domain}
|
||
```
|
||
|
||
Examples:
|
||
- `marketing-site.acme-prod.omantel.openova.io` (acme on Omantel Sovereign)
|
||
- `marketing-site.acme-prod.acme.com` (acme on its own Sovereign with their own domain)
|
||
- `blog.acme-prod.omantel.openova.io` (second App in same Environment)
|
||
|
||
The Sovereign's `sovereign-domain` is set at provisioning time; corporate Sovereigns typically rebrand to their own domain.
|
||
|
||
### 5.2 Location Code Lookup Table
|
||
|
||
| Location code | Provider | Region | Building Block | Env Type | Example DNS |
|
||
|---------------|----------|--------|----------------|----------|-------------|
|
||
| `hfrp` | Hetzner | Falkenstein | rtz | prod | `console.hfrp.openova.io` |
|
||
| `hfrd` | Hetzner | Falkenstein | rtz | dev | `console.hfrd.openova.io` |
|
||
| `hfdp` | Hetzner | Falkenstein | dmz | prod | `ingress.hfdp.openova.io` |
|
||
| `hfmp` | Hetzner | Falkenstein | mgt | prod | `gitea.hfmp.openova.io` |
|
||
| `hlrp` | Hetzner | Helsinki | rtz | prod | `console.hlrp.openova.io` |
|
||
| `hldp` | Hetzner | Helsinki | dmz | prod | `ingress.hldp.openova.io` |
|
||
| `hnrp` | Hetzner | Nuremberg | rtz | prod | `console.hnrp.openova.io` |
|
||
| `hnmp` | Hetzner | Nuremberg | mgt | prod | `console.hnmp.openova.io` |
|
||
| `hnmd` | Hetzner | Nuremberg | mgt | dev | `console.hnmd.openova.io` |
|
||
| `harp` | Hetzner | Ashburn | rtz | prod | `console.harp.openova.io` |
|
||
| `hsrp` | Hetzner | Singapore | rtz | prod | `console.hsrp.openova.io` |
|
||
| `wprp` | Huawei | AP Southeast | rtz | prod | `console.wprp.customer.io` |
|
||
| `oxrp` | OCI | Dubai | rtz | prod | `console.oxrp.customer.io` |
|
||
| `orrp` | OCI | Frankfurt | rtz | prod | `console.orrp.customer.io` |
|
||
|
||
> To derive a code not listed: concatenate the four 1-char codes from the dimension tables in §2. If a collision exists, it will already appear in this table with a resolution. Do not invent new collision resolutions — raise a PR to extend this table.
|
||
|
||
### 5.3 Coexistence During Migration
|
||
|
||
Old names remain as CNAMEs until all consumers have migrated:
|
||
|
||
```dns
|
||
# Old name (CNAME → new)
|
||
old-service.openova.io CNAME new-service.hfmp.openova.io
|
||
|
||
# New name (real A/AAAA record)
|
||
new-service.hfmp.openova.io A <ip>
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Tags and Labels
|
||
|
||
Since resource names are minimal (a VPC named `rtz-prod`, not `hz-fsn-rtz-prod`), tags carry the full context.
|
||
|
||
### 6.1 Cloud Resource Tags (all providers)
|
||
|
||
```yaml
|
||
openova.io/provider: hetzner
|
||
openova.io/region: fsn
|
||
openova.io/building-block: rtz
|
||
openova.io/env-type: prod # renamed from environment
|
||
openova.io/cluster: hz-fsn-rtz-prod # the host cluster this resource belongs to
|
||
openova.io/managed-by: catalyst # or: crossplane, opentofu (bootstrap only), manual
|
||
```
|
||
|
||
### 6.2 Catalyst Resource Tags (vcluster + Environment context)
|
||
|
||
For workloads running **inside** a vcluster:
|
||
|
||
```yaml
|
||
openova.io/sovereign: omantel # which Sovereign hosts this
|
||
openova.io/organization: acme
|
||
openova.io/environment: acme-prod # Catalyst Environment object
|
||
openova.io/vcluster: acme # vcluster name within parent host cluster
|
||
openova.io/host-cluster: hz-fsn-rtz-prod
|
||
openova.io/application: marketing-site # Application name within the Environment
|
||
openova.io/blueprint: bp-wordpress # source Blueprint
|
||
openova.io/blueprint-version: 1.3.0
|
||
```
|
||
|
||
### 6.3 Kubernetes Resource Labels (all objects)
|
||
|
||
```yaml
|
||
metadata:
|
||
labels:
|
||
app.kubernetes.io/managed-by: flux # or: helm, kustomize
|
||
app.kubernetes.io/component: grafana
|
||
openova.io/building-block: rtz
|
||
openova.io/env-type: prod
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Multi-Region Architecture and Building Block Symmetry
|
||
|
||
Geographic redundancy is achieved by deploying **the same building blocks in multiple regions**. Both clusters carry the same building block label; neither is designated "primary" or "dr". Traffic distribution is a routing concern owned by PowerDNS lua-records (authoritative DNS-level GSLB) — not a naming concern. See [`MULTI-REGION-DNS.md`](MULTI-REGION-DNS.md) for the lua-record patterns.
|
||
|
||
```
|
||
Region A (Falkenstein) Region B (Helsinki)
|
||
──────────────────────── ──────────────────────────
|
||
hz-fsn-rtz-prod hz-hel-rtz-prod
|
||
vcluster: acme vcluster: acme ← Catalyst Environment acme-prod
|
||
vcluster: bankdhofar vcluster: bankdhofar ← Environment bankdhofar-prod
|
||
(each vcluster has its own (each vcluster has its own
|
||
Flux watching the Org's Flux watching the Org's
|
||
Application repos, branch Application repos, branch
|
||
per env_type) per env_type)
|
||
|
||
hz-fsn-dmz-prod hz-hel-dmz-prod
|
||
Ingress + WAF Ingress + WAF
|
||
WireGuard endpoint WireGuard endpoint
|
||
|
||
↕ PowerDNS authoritative DNS (per Application, lua-records)
|
||
marketing-site.acme-prod.omantel.openova.io
|
||
both regions registered — `ifurlup` selects healthy endpoint
|
||
|
||
Management (one per Sovereign, single region recommended)
|
||
────────────────────────────────────────────────────────────
|
||
hz-nbg-mgt-prod
|
||
All Catalyst control-plane components — see PLATFORM-TECH-STACK §2.
|
||
Highlights:
|
||
Gitea (5 Gitea Orgs: catalog, catalog-sovereign,
|
||
<org>-per-Catalyst-Organization, system —
|
||
each Org Gitea Org holds shared-blueprints
|
||
+ one Gitea repo per Application.
|
||
See GLOSSARY §"Gitea Orgs".)
|
||
NATS JetStream (event spine + KV; per-Org Accounts)
|
||
OpenBao (secrets — primary Raft cluster here; sibling replicas
|
||
in each workload region with async perf replication.
|
||
Each region's Raft is independent. See SECURITY §5.)
|
||
Keycloak (per-Org realms in SME-style; per-Sovereign realm in
|
||
corporate-style)
|
||
SPIRE server (workload identity)
|
||
Plus per-host-cluster infrastructure (Cilium, Flux, Crossplane,
|
||
cert-manager, Kyverno, Harbor, etc.) — see PLATFORM-TECH-STACK §1.
|
||
```
|
||
|
||
When FSN becomes unavailable, `hz-hel-rtz-prod` serves all traffic for Applications with `placement: active-active` or `active-hotstandby`. The cluster name does not change. The PowerDNS lua-record's `ifurlup` health check fails for the FSN backend and the authoritative answer drops it from the response set within the configured probe window. Recovery is a routing event, not a renaming event.
|
||
|
||
---
|
||
|
||
## 8. OpenOva Own Sovereign Naming
|
||
|
||
OpenOva's own deployed Sovereign (the one hosting our SaaS Organizations — formerly called "Nova") follows the same convention as any other Sovereign.
|
||
|
||
| Object | Current name | Canonical name | Notes |
|
||
|--------|-------------|----------------|-------|
|
||
| Bootstrap K8s context (legacy) | `contabo-mkt` | `ct-eu-mgt-prod` | Adopted as alias for the existing Contabo VPS |
|
||
| Future management cluster | — | `hz-nbg-mgt-prod` | After Hetzner migration |
|
||
| Catalyst console (current) | — | `console.cemp.openova.io` | Contabo, EU, mgt, prod |
|
||
| Catalyst console (post-migration) | — | `console.hnmp.openova.io` | Hetzner, Nuremberg, mgt, prod |
|
||
|
||
The existing cluster `contabo-mkt` is adopted as `ct-eu-mgt-prod` immediately in kubeconfig and documentation. The directory path `clusters/contabo-mkt/` migrates to `clusters/hz-nbg-mgt-prod/` when the bootstrap machine itself is migrated. The bootstrap machine remains online indefinitely as `catalyst-provisioner` (used to bootstrap further Sovereigns).
|
||
|
||
---
|
||
|
||
## 9. Migration Rules
|
||
|
||
| Phase | Action |
|
||
|-------|--------|
|
||
| **Now** | All new resources use the canonical name |
|
||
| **On touch** | When modifying an existing resource for any reason, rename it |
|
||
| **DNS** | Add new name as real record; old name becomes CNAME pointing to new |
|
||
| **K8s contexts** | Add new context alias alongside old; update scripts and CI |
|
||
| **Directory paths** | Migrate `clusters/` and `infra/` directories at migration time |
|
||
| **Tag rename** | `openova.io/environment` → `openova.io/env-type` (single relabel pass during touch) |
|
||
| **Never rename** | Kubernetes namespace names on running clusters (would require full redeploy) |
|
||
|
||
---
|
||
|
||
## 10. Quick Reference — Derivation Algorithm
|
||
|
||
To name any new resource:
|
||
|
||
1. **Identify scope**: is this a global object (no parent encoding location), a host-cluster-scoped object, a vcluster-scoped object, or a namespace-scoped object?
|
||
2. **If global**: compose `{provider}-{region}-{bb}-{env_type}` from the dimension tables.
|
||
3. **If scoped**: start from the innermost scope and add only the dimensions the parent does not already provide. Use `{purpose}` at the deepest levels.
|
||
4. **If vcluster**: use `{org}` within the host cluster; use the qualified form `{prov}-{reg}-{bb}-{env_type}-{org}` for cross-cluster references.
|
||
5. **If a Catalyst Environment**: use `{org}-{env_type}` (see §11).
|
||
6. **If DNS**: derive the 4-char location code from the 1-char columns; check the lookup table in §5.2. For Application DNS use `{app}.{environment}.{sovereign-or-org-domain}`.
|
||
7. **Always**: add the full tag set from §6 to the resource.
|
||
8. **If uncertain**: raise a PR — do not invent ad-hoc names.
|
||
|
||
---
|
||
|
||
## 11. Catalyst Environment (User-Facing Object)
|
||
|
||
The **Environment** is the user-facing scope where Applications are installed. Logical concept; one Environment can be realized by multiple vclusters across regions and building blocks.
|
||
|
||
### 11.1 Naming
|
||
|
||
```
|
||
{org}-{env_type}
|
||
```
|
||
|
||
Examples: `acme-prod`, `acme-dev`, `bankdhofar-prod`, `bankdhofar-uat`, `muscatpharmacy-prod`.
|
||
|
||
> **DR is a Placement, not an Env Type.** There is no `dr` env_type — the canonical values are `prod | stg | uat | dev | poc` (§2.4). Disaster-recovery topology is expressed by the Application's Placement spec (`active-active` / `active-hotstandby` across multiple regions) inside the `*-prod` Environment, not by a separate `*-dr` Environment.
|
||
|
||
### 11.2 Realization
|
||
|
||
An Environment is realized by:
|
||
|
||
1. **A branch inside each Application's Gitea repo.** Every Application is its own Gitea repo at `gitea.{location-code}.{sovereign-domain}/{org}/{app}` (e.g. `gitea.hfmp.acme-telecom.openova.io/acme-pharmacy/store-frontend`). Branches `develop`, `staging`, and `main` map to the `dev`, `stg`, and `prod` Environments respectively. The repo FQDN follows §5.1's Catalyst control-plane DNS pattern `{component}.{location-code}.{sovereign-domain}`. This rule is **uniform across SME and corporate** — one Application = one Gitea repo, regardless of scale.
|
||
2. **One or more vclusters** (`{org}` named on each parent host cluster). The set of host clusters realizing the Environment is determined by the Environment's Placement spec.
|
||
3. **One Flux per vcluster**, watching one branch (per env_type) across all of the Org's Application repos via N `GitRepository` sources. Each Flux applies manifests filtered to its region/building block via `kustomization.yaml` selectors.
|
||
4. **JetStream Account** at the Organization level (one per Org); subjects within the Account use the prefix `ws.{org}-{env_type}.>` for per-Environment partitioning. See [`ARCHITECTURE.md`](ARCHITECTURE.md) §5.
|
||
5. **One projector consumer-group** materializing per-Environment KV state for the console.
|
||
6. **One OpenBao path** rooted at `org/{org}/env/{env_type}/`.
|
||
7. **One `EnvironmentPolicy` CR** at `system/catalyst-config/policies/{org}-{env_type}-policy.yaml` in the Sovereign-admin's `system` Gitea Org. Owned by `sovereign-admin`; edit access optionally delegated to `org-admin` via Catalyst RBAC. SME and corporate Sovereigns use **the same CR shape** — only the field values (number of approvers, soak duration, RE-score threshold) differ per Org's policy choice.
|
||
|
||
### 11.3 Single-region vs multi-region
|
||
|
||
| Mode | Vclusters | Notes |
|
||
|---|---|---|
|
||
| Single-region | 1 vcluster on one rtz cluster | SME default. No cross-region failover. |
|
||
| Multi-region | N vclusters across regions × bb | Corporate / regulated default. PowerDNS lua-records route Application traffic across regions. |
|
||
|
||
The Environment object's spec drives which vclusters get created; `environment-controller` (the Catalyst component) reconciles them.
|
||
|
||
### 11.4 Why a separate object instead of a tag?
|
||
|
||
- It owns its own Git repo (a tag couldn't).
|
||
- It owns Placement metadata (a tag couldn't).
|
||
- It is the unit of Application install/uninstall/promotion.
|
||
- Renaming it would break Git history and Flux state — naming is therefore stable for the lifetime of the Environment.
|
||
|
||
---
|
||
|
||
*Authoritative. Cross-reference [`GLOSSARY.md`](GLOSSARY.md) for definitions.*
|