platform/docs/03-naming-conventions.md
Bart Van Geyt 9f819df4d9 Phase 0: architecture docs, ADRs, and repo scaffold
Establish the design foundation for the heleosv2 multi-tenant hosting
platform before any implementation code:

- Monorepo skeleton: docs/, platform-infra/, site-templates/,
  deployments/, control-panel/ with orientation READMEs.
- docs/: roadmap index, architecture + threat model, naming conventions,
  site profiles, provisioning workflow, backup & DR runbook, repo/GitOps
  layout, and the approved architecture plan.
- docs/adr/: 9 ADRs recording the rationale for single-host Compose,
  Traefik edge, nginx+fpm split, shared MariaDB, ZFS-per-customer,
  decoupled backup streams, Forgejo, CLI-first, and SFTP-only.
- Secrets hygiene: .gitignore (only *.enc.* committed) and .gitattributes
  (LF for scripts/Dockerfiles/YAML run on the Linux host).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:26:00 +02:00

3.6 KiB
Raw Blame History

03 — Naming & Conventions

Consistent, predictable names are what make CLI-driven provisioning and scripted backup/restore reliable. Every resource for a site is derivable from a single slug.

1. The slug

Each site has one canonical slug: lowercase, ASCII, [a-z0-9-], 332 chars, starting with a letter. Derived from the primary domain (dots → hyphens) or set explicitly.

  • example.comexample-com
  • blog.example.comblog-example-com

The slug is the join key across ZFS, Docker, database, and deployment config. It never changes for the life of the site (renaming = new slug + migration).

A customer may own several sites. Where a customer grouping is needed (billing, SFTP account), use a separate customer id with the same charset rules. The default is one slug per site.

2. ZFS datasets

tank/
├── customers/
│   └── <slug>/
│       └── web            # web root ONLY (bind-mounted into the site)
└── platform/
    ├── docker             # Docker data-root (images/layers/volumes)
    ├── mariadb            # shared MariaDB datadir
    ├── db-backups/<slug>  # automysqlbackup output per site
    ├── traefik            # ACME store + dynamic config
    ├── forgejo            # Git + registry data
    └── monitoring         # Prometheus/Loki data

Snapshots: tank/customers/<slug>/web@auto-YYYYMMDD-HHMM.

3. Docker resources

Resource Pattern Example
Compose project <slug> example-com
Container <slug>_<role> example-com_nginx, example-com_fpm
Per-customer network <slug>_net example-com_net
Shared edge network proxy (fixed) proxy
Platform network platform (fixed) platform (MariaDB, monitoring)
Named volume (if used) <slug>_<purpose> example-com_fpmtmp

Traefik router/service labels also key off the slug: traefik.http.routers.<slug>.rule=Host(...).

4. Database (shared MariaDB)

Resource Pattern Example
Database db_<slug_underscored> db_example_com
DB user u_<slug_underscored> u_example_com
Grants ALL PRIVILEGES ON db_<...>.* to that user only

Hyphens in the slug become underscores for MySQL identifiers (example-comexample_com). Passwords are generated, stored encrypted (SOPS/age), never reused across sites.

5. Domains & TLS

  • Primary domain drives the slug; additional aliases are listed in the site's deployment config and added to the Traefik Host rule.
  • Certificates are issued per domain automatically by Traefik/ACME; no manual naming.

6. SFTP accounts

Resource Pattern Example
SFTP user sftp_<slug_underscored> sftp_example_com
Chroot path tank/customers/<slug>/web

7. Deployment config (in deployments/)

deployments/
└── <slug>/
    ├── docker-compose.yml      # rendered from a site-templates profile
    ├── .env.example            # non-secret defaults / references
    ├── secrets.enc.yaml        # SOPS/age-encrypted secrets (committed)
    └── site.yaml               # slug, profile, domains, options (source of truth)

site.yaml is the declarative source of truth the CLI reads/writes; everything else is rendered from it.

8. Reserved words

Slugs may not be: proxy, platform, traefik, mariadb, forgejo, monitoring, docker, customers, platform-infra, site-templates, deployments, control-panel (avoids collisions with platform names).