Files
factory/vibe/guidebooks/lab-ecosystem/naming-conventions.md
Gabriel Radureau 7647a68cdc docs(vibe): bootstrap vibe/ knowledge tree + ecosystem AGENTS.md
Add a root AGENTS.md (ecosystem map of factory/tools/cms + agent operating
rules + the persona cohort & workflow) and a new vibe/ knowledge base for LLM
agents, modeled on tree-docs conventions and the factory house style.

vibe/ folders (each with a README hub + contribution rules):
- ADR/      optimized MADR-lite; canonical home going forward (doc/adr stays historical)
- PRD/      one subfolder per PRD, mandatory STATUS.md, QA strategy for big ones
- investigations/  single INV-NNN-slug.md, or stub + folder w/ notebooks
- guidebooks/      tree-docs maps; lab-ecosystem guidebook of factory+tools+cms
- runbooks/        [AGENT]/[HUMAN] step procedures (EN; doc/runbooks stays FR)
- shareouts/       dated FR handouts (decks/mp4)

Seed content (first ADR + PRD): a safe, production-like environment to rehearse
risky changes and recovery without touching real prod — local-only sandbox
(k3d + arm64 VMs) with a hard prod/sandbox isolation boundary. Includes
INV-001 (prod blast-radius couplings), the ecosystem guidebook, and a FR shareout.

Conventions enforced: no-tombstone rule, breadcrumb spine, bidirectional
cross-links, theme:base mermaid (MCP-validated) + ordered-list-after-diagram.
Built with a Workflow + persona cohort; 24 files, zero dead links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:52:37 +02:00

8.4 KiB

vibe > Guidebooks > Lab ecosystem > Naming conventions (the <app> join key)

Naming conventions — the <app> join key

Status: 🟢 Active Last Updated: 2026-06-23 Related: Lab ecosystem · Factory brick · Secrets & Vault · PRD — isolation boundary Upstream (source of truth): doc/runbooks/new-web-app/conventions.md (French, authoritative)

TL;DR

Every application on the platform is pinned to one kebab-case identifier — <app> (e.g. erp, webapp, url-shortener, dance-lessons-coach). That single string is reused verbatim, with no transformation, as the name of the app's Gitea repo, its PostgreSQL database and role, its Vault roles and policies, its Kubernetes namespace and ServiceAccount, its ArgoCD Application, its OpenTofu state prefix, and its DNS records. The bricks of the stack do not point at each other through explicit configuration; they wire together by guessing each other's names from <app>. Pick the name once, get it right, and the whole chain self-assembles. One typo anywhere, and the chain breaks silently.

What <app> is

<app> is a lowercase, kebab-case slug. It is the join key of the entire platform — the one value that lets a dozen otherwise-independent systems agree on which resources belong to the same application without ever exchanging a config pointer. The canonical, authoritative definition (in French) lives in the runbook: doc/runbooks/new-web-app/conventions.md. This page is the English concept summary inside the ecosystem guidebook.

The mapping — one name, every system

The table below shows how each system derives its identifier from <app>, with the erp application as the worked example.

System Identifier derived from <app> Example (erp)
Gitea repository arcodange-org/<app> arcodange-org/erp
PostgreSQL database <app> erp
PostgreSQL owner role (non-login) <app>_role erp_role
Vault dynamic DB role postgres/creds/<app> postgres/creds/erp
Vault Kubernetes auth role <app> erp
Vault runtime policy (pod) <app> erp
Vault CI/ops policy <app>-ops erp-ops
Vault CI JWT role (Gitea OIDC) gitea_cicd_<app> gitea_cicd_erp
Vault KV config path kvv2/<app>/config kvv2/erp/config
Kubernetes namespace <app> erp
Kubernetes ServiceAccount <app> erp
ArgoCD Application <app> erp
OpenTofu state prefix (GCS) <app>/main erp/main
Internal DNS <app>.arcodange.lab erp.arcodange.lab
Public DNS <app>.arcodange.fr erp.arcodange.fr

Note

The _role suffix (PG owner role) and the -ops suffix (Vault CI policy/identity group) are the only two systematic transformations of <app>. Everything else uses the bare slug. Note the suffix style differs: PostgreSQL uses an underscore (erp_role) because hyphens are awkward in SQL identifiers, whereas Vault and Kubernetes use a hyphen (erp-ops).

Why uniformity is structuring

The platform is a set of loosely-coupled bricks (Gitea, Postgres, Vault, k3s/ArgoCD, OpenTofu, DNS). They were deliberately built not to hold explicit references to one another. Instead, each brick reconstructs the names it needs from <app> at the moment it runs:

%%{init: {'theme':'base'}}%%
flowchart LR
    APP["&lt;app&gt;<br/>(one kebab-case slug)"]:::src

    APP --> GIT["Gitea repo<br/>arcodange-org/&lt;app&gt;"]:::brick
    APP --> PG["PostgreSQL<br/>db &lt;app&gt; · role &lt;app&gt;_role"]:::brick
    APP --> VAULT["Vault<br/>postgres/creds/&lt;app&gt;<br/>policy &lt;app&gt; · gitea_cicd_&lt;app&gt;"]:::brick
    APP --> K8S["Kubernetes<br/>namespace + SA &lt;app&gt;"]:::brick
    APP --> ARGO["ArgoCD<br/>Application &lt;app&gt;"]:::brick
    APP --> GCS["OpenTofu state<br/>&lt;app&gt;/main"]:::brick
    APP --> DNS["DNS<br/>&lt;app&gt;.arcodange.lab / .fr"]:::brick

    VAULT -.->|"GRANT &lt;app&gt;_role<br/>assumes PG role name"| PG
    K8S -.->|"VaultDynamicSecret reads<br/>postgres/creds/&lt;app&gt;"| VAULT
    ARGO -.->|"repoURL=.../&lt;app&gt;<br/>namespace=&lt;app&gt;"| GIT

    classDef src fill:#2563eb,stroke:#1e40af,color:#fff
    classDef brick fill:#059669,stroke:#047857,color:#fff
  1. The chosen slug <app> is the single input.
  2. From it, each brick names its own resource: Gitea names the repo arcodange-org/<app>; Postgres names the database <app> and its owner role <app>_role; Vault names the dynamic-creds role postgres/creds/<app>, the runtime policy <app>, and the CI JWT role gitea_cicd_<app>; Kubernetes names the namespace and ServiceAccount <app>; ArgoCD names the Application <app>; OpenTofu writes state under <app>/main; DNS publishes <app>.arcodange.lab and <app>.arcodange.fr.
  3. The dashed arrows are the cross-brick assumptions that make it work: the Vault app_roles module issues a dynamic PG user with GRANT <app>_role TO …, assuming the Postgres owner role is named exactly <app>_role; the chart's VaultDynamicSecret reads postgres/creds/<app>, assuming the Vault role is named exactly <app>; the ArgoCD Application derives repoURL=.../<app> and namespace=<app> from the slug alone, assuming the Gitea repo and the namespace match.
  4. None of these links is configured by hand. They hold purely because every brick was given the same <app> to reconstruct from.

The failure mode of a typo

Because the wiring is by name and not by explicit reference, nothing validates the join key end-to-end. A single divergence — my_app vs my-app, a stray capital (MyApp), an accidental plural (erps) — does not raise an error at creation time. The mismatched brick simply builds a resource under a name no one else looks for:

  • A Postgres owner role created as erp-role (hyphen) instead of erp_role → Vault's GRANT erp_role fails or grants nothing → the pod gets a DB user with no privileges.
  • A Gitea repo named erp-app instead of erp → ArgoCD's derived repoURL=.../erp 404s → the Application never syncs.
  • A namespace typo → the VaultDynamicSecret and ServiceAccount land in the wrong place → silent auth failure at pod start.

The symptom is always the same: a brick that looks provisioned but never connects, with no single component to blame. This is why the slug must be short, stable, and correct from the first step — there is no safety net downstream.

Choose a short, stable, lowercase kebab-case name up front and reuse it character-for-character. Never introduce variants (case, separators, plurals); nothing will warn you.

Why this makes a sandbox safe

The <app> convention is also the reason a production-like sandbox can reuse the exact same names without colliding with production. Because every brick derives its resource names from <app> and from nothing else, an entire parallel universe of the platform — its own Vault, its own Postgres instance, its own k3s namespace scope — can host an erp named identically to the production erp, provided the two universes never share a backing store. Identity comes from the environment boundary, not from the name; the name is free to repeat. This is what lets QA and recovery drills run against erp, webapp, etc. with realistic identifiers instead of mangled erp-staging-style aliases that would themselves break the name-wiring. See the PRD's isolation boundary for how that environment fence is drawn.

See also