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

99 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.com``example-com`
- `blog.example.com``blog-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-com``example_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).