platform/CLAUDE.md
Bart Van Geyt 0ade1c740f Phase 5: backup/DR automation (backup Ansible role)
Implements the two decoupled backup streams from docs/06 as an idempotent
Ansible role wired into the host playbook:

- Files: sanoid takes/prunes ZFS snapshots per policy (sanoid_datasets) on
  its packaged timer; syncoid replicates offsite (heleos-zfs-offsite),
  enabled only when zfs_offsite_target is set.
- DB: heleos-db-backup (nightly systemd timer) walks the deployments dir and
  dumps each DB-backed site via `docker exec mariadb-dump` into
  db-backups/<customer>/<site>/{daily,weekly,monthly} with rotation
  (automysqlbackup-style, adapted for the containerized DB; MYSQL_PWD keeps
  the password out of the process list). heleos-db-offsite rsyncs offsite
  when db_offsite_target is set.

Streams and schedules are configured in group_vars/all.yml; offsite is
opt-in via the two target vars. Updates doc 06 (implementation note), the
ansible README, and CLAUDE.md status. YAML + templates validated by render.

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

72 lines
4.1 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.

# CLAUDE.md — heleosv2 platform
Multi-tenant web hosting platform: **container-per-site + ZFS-dataset-per-site**,
CLI-driven provisioning, decoupled per-site backup/restore. Single bare-metal
host, Docker Compose per site, no orchestrator.
> **Read `docs/` first — it is the source of truth.** Start with
> [docs/00-roadmap.md](docs/00-roadmap.md). This file is just the orientation
> map so a fresh session gets up to speed cheaply.
## Core philosophy
1. **The customer is the boundary** — isolation, backup, restore align on it.
2. **Two levels:** a `customer` owns many `site`s. Isolation/containers/DB/network
are per **site**; grouping (one SFTP login, recursive backup, billing) is per
**customer**. See [docs/03-naming-conventions.md](docs/03-naming-conventions.md).
3. **Containers isolate, they don't secure by themselves** — non-root FPM,
read-only rootfs, per-site networks, egress filtering, least-priv DB users.
4. **Each backup stream matches its change pattern** — web files via ZFS
snapshot/`send`; DB via automysqlbackup + rsync; logs via Loki. Never fold the
DB dump into the web dataset (it bloats every incremental).
5. **CLI/templates before UI.** Prove the platform, then wrap it in a panel.
## Status
- ✅ Phase 0 — design docs, ADRs, scaffold ([docs/](docs/00-roadmap.md))
- ✅ Phase 1 — host baseline Ansible ([platform-infra/ansible/](platform-infra/ansible/README.md))
- ✅ Phase 2 — platform services (Traefik, MariaDB, Forgejo) in [platform-infra/stacks/](platform-infra/stacks/README.md)
- ✅ Phase 3 — site templates & base images in [site-templates/](site-templates/README.md)
- ✅ Phase 4 — provisioning CLI `heleosctl` in [control-panel/](control-panel/README.md) (provision/deprovision/list/render/backup/restore; `--dry-run`; 22 tests)
- ✅ Phase 5 — backup/DR automation in the `backup` Ansible role (sanoid snapshots, per-DB dump timer, optional syncoid/rsync offsite)
- 🚧 Phase 6 — observability (Prometheus/Grafana + cAdvisor/node_exporter/Traefik metrics, Loki/Promtail, Uptime-Kuma)
- ⬜ Phases 78 — migration, customer panel
### Phase 4 follow-ups (not yet done)
- `reconfigure` + `rotate-secret` commands; git-commit of `deployments/` on provision; php-fpm umask for two-way SFTP editing (see control-panel/README.md).
## Repo map
| Path | Purpose |
|------|---------|
| `docs/` | Architecture, ADRs, runbooks, conventions (**authoritative**). |
| `platform-infra/ansible/` | Phase 1 host baseline (ZFS, Docker, firewall, SSH). |
| `platform-infra/stacks/` | Phase 2 base compose projects (Traefik/MariaDB/Forgejo). |
| `site-templates/` | Dockerfiles + per-profile compose templates (Phase 3). |
| `deployments/` | Rendered per-site configs `<customer>/<site>/` (GitOps state). |
| `control-panel/` | Provisioning CLI (Phase 4), panel later. |
## Naming quick-ref (see docs/03)
- `customer` + `site` ids; `slug = <customer>-<site>` (Docker/DB key).
- ZFS: `tank/customers/<customer>/<site>/web`; platform on `tank/platform/*`.
- DB: `db_<customer>_<site>` + user `u_<customer>_<site>` on shared MariaDB.
- Networks: shared `proxy` (edge) + `platform` (DB); per-site `<slug>_net`.
## Common commands
```bash
# Phase 1 — host baseline (from platform-infra/ansible, against the VM)
ansible-galaxy collection install -r requirements.yml
ansible-playbook site.yml # tags: base|zfs|docker|firewall|ssh
# Phase 2 — platform services (on the host, once Phase 1 is applied)
platform-infra/stacks/bootstrap-networks.sh
cd platform-infra/stacks/<svc> && cp .env.example .env && docker compose up -d
```
## Working agreements
- **Docs are source of truth.** When a design decision changes, update the
relevant `docs/` file and add/adjust an ADR in `docs/adr/`.
- **Never commit plaintext secrets.** Only `*.enc.*` (SOPS/age) are allowed;
`.env` is git-ignored (`.env.example` is committed). See `.gitignore`.
- **Line endings:** LF for scripts/Dockerfiles/YAML (enforced by `.gitattributes`)
— these run on Linux.
- **Commit per phase/step** with a descriptive message; the repo is the memory.
- Environment is Windows (authoring) → Linux host (runtime). Prefer the Bash tool
for POSIX one-offs.