tnt-{project}--{namespace}
FUN-5 Identifiers
1. Introduction
Every resource in Fundament has two kinds of identity: a UUIDv7 primary key for internal use and a human-readable name for external use. This document describes the naming constraints for human-readable names and how they interact with Kubernetes.
The constraints aren’t arbitrary. They exist because Fundament resources map to Kubernetes resources, and Kubernetes has strict rules about what constitutes a valid name. Getting this wrong means a project that works fine in our API but can’t be provisioned on the cluster.
2. General Rules
Names are immutable after creation. This is a deliberate choice: names appear in Kubernetes resources, DNS records, URLs, and configuration files. Renaming a resource would require cascading changes across all of those, which is fragile and error-prone. If you need a different name, create a new resource.
Where resources interact with Kubernetes, names follow the DNS-1123 label specification: lowercase alphanumeric characters and hyphens, starting with a letter, ending with an alphanumeric character, at most 63 characters. This is the most restrictive common denominator across Kubernetes resource types.
3. Entity-Specific Constraints
3.1. Organization Name
The organization name is the most constrained identifier. It serves as the tenant identifier in URLs, API paths, and potentially DNS names.
-
Regex:
^[a-z][a-z0-9-]*[a-z0-9]$ -
Uniqueness: globally unique (soft-deleted names can be reused)
-
Immutable after creation
This regex is slightly more permissive than DNS-1123 (it doesn’t enforce the 63-character limit), but in practice organization names are short. Organizations also have a mutable alias (1-255 characters) for human-readable presentation.
3.2. Cluster Name
-
Length: 1-255 characters
-
Uniqueness: unique per organization
-
No regex constraint: cluster names don’t directly become Kubernetes resource names (the Gardener shoot name is derived separately)
3.3. Project Name
-
Regex:
^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$(DNS-1123 label) -
Max length: 30 characters
-
No
--: two consecutive hyphens are reserved as the separator in Kubernetes namespace names -
Uniqueness: unique per cluster
Project names are DNS-1123 labels because they form part of the Kubernetes namespace name (see below). The 30-character cap leaves every project at least 27 characters for its namespace names. Projects created before these two rules keep their name.
3.4. Namespace Name
-
Regex:
^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$(DNS-1123 label) -
Max length: 57 minus the length of the project name (at least 27)
-
Uniqueness: unique per project
Same constraints as project names, for the same reason; the cluster-side name tnt-<project>--<namespace> must itself fit a 63-character label.
3.5. NodePool Name
-
Length: 1-255 characters
-
Uniqueness: unique per cluster
-
No regex constraint: node pool names are passed to Gardener but don’t appear in DNS
4. Kubernetes Namespace Convention
When a namespace is provisioned on a cluster, the Kubernetes namespace name is a fixed prefix, the project name, a double dash and the namespace name:
Each part has a job:
-
tnt-marks every namespace fundament creates for a project (a tenant namespace). Nothing else on a cluster uses this prefix, so a project namespace can never take the name of a system or plugin namespace: without it, projectcertwith namespacemanagerwould becomecert-manager, the namespace the cert-manager plugin installs into, and the project’s admins would get access to its secrets. A list of forbidden names can’t prevent this, because plugins choose their namespaces and may be installed after the project namespace exists. -
--separates project and namespace. Project names may not contain it, so two different pairs never produce the same name (a-b+cgivestnt-a-b--c,a+b-cgivestnt-a--b-c).
This is the scheme Gardener uses for the same two problems: project namespaces in the garden must start with garden-, and a shoot’s control-plane namespace is shoot--{project}--{shoot}, with -- forbidden in project and shoot names (gardener#164). Gardener’s name rules apply to its own project and shoot names only; these namespaces live inside the shoot, where the Kubernetes limit of 63 characters is the only one.
Project names are unique per cluster and immutable, so the result is readable in every Kubernetes tool and stable for the namespace’s life. Some examples:
| Project | Namespace | K8s Namespace |
|---|---|---|
|
|
|
|
|
|
|
|
|
Two rules keep the scheme valid, both enforced when a namespace is created:
-
Length:
4 + len(project) + 2 + len(namespace)must not exceed 63, so a namespace can be at most57 - len(project)characters. -
Uniqueness on the cluster: projects created before
--was reserved may contain it (x--y+zandx+y--zboth givetnt-x--y--z), so a namespace whose Kubernetes name is already used by another project’s namespace on the cluster is still refused.
Namespaces created before this convention carry a hashed project prefix (<8 chars of project><4 hash chars>-<namespace>). Kubernetes namespaces can’t be renamed, so they keep that name; the cluster-worker identifies every namespace by its fundament.io/namespace-id label, not by its name.
5. Uniqueness Scoping
Names are unique within their parent scope:
-
Organization names are globally unique
-
Cluster names are unique within an organization
-
Project names are unique within a cluster
-
Namespace names are unique within a project
-
NodePool names are unique within a cluster
Soft-deleting a resource releases its name and you can create a new resource with the same name after deleting the old one. Only one active (non-deleted) resource can hold a given name within its scope. See FUN-6 for the soft-delete pattern.
6. Primary Identifiers
UUIDv7 is the canonical identifier for all resources. Names exist for human readability and Kubernetes integration, but the API uses UUIDs for all references (foreign keys, request parameters, etc.). See FUN-6 for the rationale behind UUIDv7.
The API provides GetByName endpoints for convenience (looking up a cluster by name within an organization, for example), but these are sugar on top of the UUID-based model.
7. Related
-
FUN-4: Organizational hierarchies (what resources exist and how they relate)
-
FUN-6: API-first design (UUIDv7 rationale, soft-delete pattern)