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>
99 lines
3.6 KiB
Markdown
99 lines
3.6 KiB
Markdown
# 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-]`, 3–32 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).
|