Add base Docker Compose projects under platform-infra/stacks, using the Phase 1 ZFS platform datasets and shared networks: - bootstrap-networks.sh: idempotent creation of shared `proxy` (edge) and `platform` (internal) Docker networks. - traefik: edge router with TLS/ACME (HTTP-01), global HTTP->HTTPS redirect, file-based security-headers/TLS middlewares, and a basic-auth dashboard. Docker access via a least-privilege tecnativa/docker-socket-proxy instead of mounting docker.sock directly. - mariadb: shared instance on tank/platform/mariadb, utf8mb4, tuned; not published to the host (internal `platform` network only). - forgejo: Git + container registry over HTTPS (SSH disabled to avoid extra inbound ports), SQLite backend, data on tank/platform/forgejo. Each stack ships a committed .env.example (real .env is git-ignored) and the stacks README documents bring-up order, secrets, and verification. Also add root CLAUDE.md so fresh sessions orient from files cheaply: philosophy, doc pointers, naming quick-ref, repo map, commands, agreements. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.4 KiB
3.4 KiB
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. This file is just the orientation map so a fresh session gets up to speed cheaply.
Core philosophy
- The customer is the boundary — isolation, backup, restore align on it.
- Two levels: a
customerowns manysites. Isolation/containers/DB/network are per site; grouping (one SFTP login, recursive backup, billing) is per customer. See docs/03-naming-conventions.md. - Containers isolate, they don't secure by themselves — non-root FPM, read-only rootfs, per-site networks, egress filtering, least-priv DB users.
- 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). - CLI/templates before UI. Prove the platform, then wrap it in a panel.
Status
- ✅ Phase 0 — design docs, ADRs, scaffold (docs/)
- ✅ Phase 1 — host baseline Ansible (platform-infra/ansible/)
- 🚧 Phase 2 — platform services (Traefik, MariaDB, Forgejo) in
platform-infra/stacks/ - ⬜ Phases 3–8 — site templates, provisioning CLI, backup/DR, observability, migration, panel
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+siteids;slug = <customer>-<site>(Docker/DB key).- ZFS:
tank/customers/<customer>/<site>/web; platform ontank/platform/*. - DB:
db_<customer>_<site>+ useru_<customer>_<site>on shared MariaDB. - Networks: shared
proxy(edge) +platform(DB); per-site<slug>_net.
Common commands
# 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 indocs/adr/. - Never commit plaintext secrets. Only
*.enc.*(SOPS/age) are allowed;.envis git-ignored (.env.exampleis 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.