openova/docs/NAMING-CONVENTION.md
hatiyildiz f5daac52af refactor(platform): remove k8gb — replaced by PowerDNS lua-records (#171)
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>
2026-04-29 08:51:09 +02:00

24 KiB
Raw Blame History

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. 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 332 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:

# 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)

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:

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)

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 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/environmentopenova.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 §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 for definitions.