platform/docs/07-repo-layout-gitops.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

83 lines
3.8 KiB
Markdown

# 07 — Repository Layout & GitOps
## 1. Why a monorepo with clear boundaries
Platform code, image templates, per-customer deployment state, and secrets have
**different lifecycles and audiences**. We keep them in one repository (simple to
reason about for a small team) but as **strictly separated top-level areas**, so
they can be split into independent repos later without restructuring.
```
heleosv2/
├── docs/ # this design set (ADRs, runbooks, conventions)
├── platform-infra/ # Ansible + base compose for host & platform services
├── site-templates/ # Dockerfiles + compose templates per site profile
├── deployments/ # rendered per-customer configs (GitOps state)
└── control-panel/ # provisioning CLI now; customer panel later
```
### `platform-infra/`
Host baseline and platform services as code:
- Ansible roles: ZFS pool/datasets, Docker, nftables (Docker-aware), SSH
hardening, egress filtering, automysqlbackup, ZFS snapshot/`send` jobs.
- Base compose projects: Traefik, shared MariaDB, Forgejo + registry,
Prometheus/Grafana/Loki, Uptime-Kuma.
### `site-templates/`
The building blocks the CLI renders from:
- Dockerfiles for standard images (php-fpm non-root, nginx, static base).
- One compose **template** per profile (`static`, `redirect`, `custom-php`,
`wordpress`) with placeholders filled from `site.yaml`.
- Image builds run through CI with Trivy + gitleaks; images pushed to the
Forgejo registry.
### `deployments/`
The **GitOps state** — one directory per site (see
[03](03-naming-conventions.md) §7). Rendered `docker-compose.yml`, `.env`,
encrypted `secrets.enc.yaml`, and the declarative `site.yaml`. Committing here is
the audit trail of what is deployed. **Never commit plaintext secrets.**
### `control-panel/`
The provisioning CLI (`provision`/`reconfigure`/`deprovision`/`backup`/`restore`/
…). Reads/writes `site.yaml`, renders from `site-templates/`, writes to
`deployments/`, and drives ZFS/DB/Docker. The future web panel lives here too,
calling the same operations.
## 2. Secrets
- **SOPS + age** encrypt secrets at rest; only `*.enc.*` / `*.sops.*` files are
committed (enforced by [`.gitignore`](../.gitignore)).
- Plaintext `.env` files are git-ignored; a decrypt step materializes runtime
env just before `compose up` (and it stays out of the customer ZFS dataset).
- Per-site DB/SFTP credentials are unique and rotatable (`rotate-secret`).
- The age private key is an operator secret, stored outside the repo and part of
DR (without it, encrypted secrets are unrecoverable — back it up offline).
## 3. GitOps flow
```
edit site.yaml ─▶ CLI renders ─▶ commit deployments/<slug> ─▶ compose up -d
(intent) (from templates) (audit trail) (converge)
```
- **Source of truth:** `site.yaml` per site + the base compose in
`platform-infra`.
- **Change =** a commit in `deployments/`. History shows who deployed what, when.
- **CI** (Forgejo Actions): lint/scan templates and images; optionally validate
that `deployments/` renders cleanly from `site.yaml`. Deployment stays
operator-triggered on the single host initially (no auto-apply agent yet).
## 4. Branching
- `main` is deployable. Platform/template changes go via short-lived branches +
PR + CI (scans must pass).
- `deployments/` commits may be direct on `main` (operational changes) but still
run the render/scan checks.
## 5. When to split into multiple repos
Split when any becomes true: multiple operators needing different access to
`deployments/` vs platform code; `deployments/` history dominating the repo; or
open-sourcing `site-templates`/`control-panel` while keeping `deployments`
private. The top-level separation above makes that a clean `git filter-repo`
extraction rather than a rewrite.