Naming and tagging conventions, vendored by each infrastructure repository
  • Python 49.1%
  • HCL 30.1%
  • Open Policy Agent 18.8%
  • Makefile 1.1%
  • Shell 0.9%
Find a file
2026-10-29 20:17:25 +00:00
.forgejo/workflows test(address): a declared tag must EXIST, not merely be agreed upon 2026-10-28 20:34:18 +00:00
adoption release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
decisions release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
docs release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
examples release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
modules/naming release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
policy release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
registry release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
templates/consumer release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
tests release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
tools feat(registry): the Proxmox firewall's own IPSet names are reserved, accepted as they are (I5) 2026-10-29 17:42:16 +00:00
.gitignore infra-conventions v1.0.0 : convention de nommage et de tagging 2026-10-26 14:01:52 +00:00
CHANGELOG.md release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
CONVENTION.md release: v4.0.0 - naming.v4, and every pin follows kit.tag 2026-10-29 19:46:19 +00:00
Makefile review(v3): N1, N2 and N3 decided - the shortened style goes, the crossing gets a substrate 2026-10-29 07:44:38 +00:00
MAPPING.md review(v4): the maintainer's arbitrations after the home node's contradictory review 2026-10-29 12:02:39 +00:00
README.md review(v3): N1, N2 and N3 decided - the shortened style goes, the crossing gets a substrate 2026-10-29 07:44:38 +00:00

infra-conventions

One naming and tagging convention for every infrastructure managed as code: Scaleway (seeds-infra), Proxmox VE / PBS (home-node-infra), AWS and GCP (future ports of the same architecture), with the tooling that enforces it — registry, functional mapping between platforms, OpenTofu module, Conftest policy, Python CLI, templates for consuming repositories and adoption guides.

Read CONVENTION.md first, then MAPPING.md. Why each rule is the one it is, and what it replaced, is in decisions/.

[prefix]org-env-[target-]domain-component[-qualifier]-slug[-location][-nn]

What is in here

Path Role
registry/conventions.yaml grammar, vocabularies (org, entity, env, domain, location), tags, Kubernetes labels, VMID, allocated identifiers, exception classes, namespaces, the tenancy rule
registry/repositories.yaml what each consuming repository declares about its own substrate: the organization axis, the boundary axis, the apply rule
registry/products.yaml the product table: a product's name to its (domain, component) pair and its target code, bijective per repository
registry/capabilities.yaml 97 functional capabilities: one slug shared by every platform
registry/types/scaleway.yaml 162 types of the scaleway/scaleway v2.88.0 provider
registry/types/forgejo.yaml svalabs/forgejo: a provider that NAMES without being a cloud
registry/types/aws.yaml 1,742 types of the hashicorp/aws v6.70.0 provider (129 reviewed, 1,613 inventoried automatically)
registry/types/gcp.yaml 1,346 types of the hashicorp/google v7.46.2 provider (78 reviewed, 1,268 inventoried automatically)
registry/types/proxmox.yaml 72 resources of bpg/proxmox v0.118.0 (plus 46 deprecated aliases)
registry/types/proxmox_backup_server.yaml the PBS provider, jkossis/proxmox v1.1.1
registry/types/kubernetes.yaml hashicorp/kubernetes and helm (Kapsule, EKS, GKE, home node)
registry/types/tailscale.yaml, utility.yaml Tailscale, random/time/tls/local/null
MAPPING.md Scaleway to AWS to GCP to Proxmox to Kubernetes, service by service (generated)
modules/naming/ an OpenTofu module with no provider: names, tags, labels, VMID, and its guards
policy/ a Conftest policy over tofu show -json, its unit tests and reference plans
tools/naming.py the same logic in Python: generate, check, decode, port, audit, vmid, tags, lint-registry, gen-mapping
docs/ the adoption pages: every object of a consuming repository, its name today and its v3 name. Confronted with the registry by make artifact-sync
tests/cases.yaml, tests/portability.yaml, tests/audit/ cases shared between Python and OpenTofu; one component keeps one name across platforms; the audit's impact categories
tests/lints.py, tests/english.py, tests/address.py, tests/reservations.py the registry lints tested by mutation; the repository speaks English; every copy of the kit's address matches the one declaration; every reservation registry agrees with the formula that produces its keys
examples/ a Scaleway stack, its AWS and GCP equivalents, and a Proxmox stack, planned against the real providers
templates/consumer/ to copy into each repository: naming.tf, scripts/naming-check.sh, naming-exceptions.yaml, vmids.yaml (Proxmox), CODEOWNERS, a Forgejo workflow, an AGENTS.md section
adoption/ an adoption plan and a prompt per repository (seeds-infra, home-node-infra, aws, gcp); HANDOFF.md for the session that surveys what already exists
decisions/ what was decided, by whom, on what date, and what was measured rather than assumed

Getting started

# 1. Clone the kit (it is published, and public: no token to read it)
git clone https://code.dataseeds.io/shared/infra-conventions.git

# 2. Check it locally (tofu, conftest, python3 with pyyaml)
make check        # registry, cases, portability, audit, MAPPING up to date, tofu test (80),
                  # policy (58 unit + 11 plans), and that the repository speaks English
make examples     # the four examples planned for real and run through the policy (downloads the providers)

Then follow adoption/seeds-infra.md, adoption/home-node-infra.md, and adoption/aws.md or adoption/gcp.md on the day of a port. To survey what already exists and measure what bringing it into line would cost: adoption/HANDOFF.md.

The address is declared ONCE, in registry/conventions.yaml under kit, and make address confronts every copy of it in this repository with that declaration - a URL a reader types cannot interpolate a variable, so the only way fourteen files stay true through a move is a test that fails when they disagree. Change it there and nowhere else.

The repository is public (measured: an unauthenticated read answers 200), so a consumer needs no token to clone it. Make it private and that changes: the runner and every workstation would then need one (FORGEJO_READ_TOKEN in CI).

State of verification

Check Result
lint-registry 3,451 types, 97 capabilities, 0 problems
Cases shared between Python and OpenTofu 60 / 60 identical (truncation with a hash, the alias/ prefix and the path style included)
Portability 19 / 19 components carry the same name on every platform, GCP included
Audit 23 / 23 resources (Scaleway, GCP, and a captured plan carrying a real reference) placed in the right impact category
tofu test on the module 80 / 80 (60 cases, 16 guards, 4 v3 grammar cases)
conftest verify 58 / 58
Registry lints, by mutation 18 / 18 cry out (make lints)
Reference plans 11 / 11, of which 4 real and compliant (scaleway 2.88.0, hashicorp/aws 6.70.0, hashicorp/google 7.46.2, bpg/proxmox 0.118.0) and 4 real and faulty (AWS: missing names, *_prefix, a child module, a subnet with no zone; GCP: labels, names, deleting a reserved name)
English every file git tracks, 823 words, 6 named exceptions, 0 findings (make english: non-ASCII outside an allowlist, French words, and elision). The file and line counts are deliberately not quoted here: a figure that counts the document quoting it is wrong the moment the document is edited
Address 16 addresses and 25 pinned tags confronted with the single kit declaration, 0 findings (make address)
Adoption pages 1 page, 70 rows, 44 names and 11 tag keys confronted with the registry, 0 findings (make artifact-sync)

Known limits

  • GCP: 1,268 types inventoried automatically (reviewed: false), under the same rules as AWS.
  • AWS: 1,613 types inventoried automatically (reviewed: false) — the slug is inferred, the length capped at 63, and the name attribute chosen by heuristic. A review of a 45-type sample found 7 to 9 % of them wrong before the rules were tightened; the policy only WARNs on these. Review them the first time you use one.
  • Constraints are unknown for 80 Scaleway types, which publish no rule at all: where no length is known, a cautious bound of 63 characters applies. The confidence field says so.
  • Limits for the reviewed AWS types come from the API documentation wherever the provider's own code does not validate them (confidence: likely).
  • A hand-written name that is exactly at the maximum length and ends in five hexadecimal characters is indistinguishable from one the module truncated.
  • VMID uniqueness across repositories holds only if a single repository manages the Proxmox node.
  • Five standard types are too tight for the v3 grammar. The mandatory org pushes org-env-domain-component-slug past the limit of 5 of the 3451 standard types, 3 of them even without a target: an 18-character Proxmox security group keeps no component at all, and a 30-character GCP project_id or service account truncates. One portability case breaks because of it, recorded with its reason in tests/portability.yaml.
  • An allocated identifier carries no run, and a rehearsal takes another allocation. The budget is 8 characters on a Proxmox SDN object, which holds a kind letter and the seven digits of the VMID coordinate and nothing else. So the run has no room — and no need: the words a human reads live in the object's alias, which is the full standard name with the run in it. Two realisations are told apart by two numbers, and the kit makes the allocation explicit rather than derived (instance is required) while the allocation registry refuses a number already held by another name. Until 2026-10-29 those objects carried a shortened name instead, a rehearsal of them had no name at all, and the way out was thought to be a shorter run shape.
  • A namespace's visibility is declared, never verified here. namespaces.reserved.*.visibility says that shared must be publicly readable, because the promise that no consumer's CI carries a read token rests on it; namespaces.visibility_default says private for every other namespace, an org's own included — a namespace holding only private repositories loses nothing by being private, and one that is public publishes whatever it ever comes to hold. A visibility is an API answer and this suite reaches no network, so the value exists to be taken from the convention rather than rediscovered; the consumer that can test it does (stacks/forge/namespaces in seeds-infra, on the organisation and on the repository, with a signed-out call — a state saying public says what was asked for, not what was got).
  • The kit is still vendored by ?ref=, not by a local copy. Section 7.2 of the v3 specification argues for vendoring a copy per consumer — it works offline, in a worktree, with no credential, and without the bootstrap circularity of a repository that creates the forge hosting its own convention. That change alters the consumer contract and is deliberately NOT in v3.0.0: doing half of it would leave two sources of truth where there is one today.
  • PBS constraints were read from a copy of the sources dating from mid-2024 (confidence: likely).
  • The policy sees only the names that are known at plan time.