- Add CONTRIBUTING.md: interim local-git rules (no work on main; every change a feature branch merged into main) and the target state (Gitea-hosted repos, GitOps source cutover, CI/CD via Gitea Actions running the Kyverno gate + Chainsaw conformance Job, PR merge gate). Note the self-hosted-git bootstrapping circularity + off-cluster mirror/backup mitigation. - AGENTS.md: add section 12 (version-control workflow). - docs/05: source-of-truth migration (local bootstrap -> self-hosted Gitea). - docs/13 + roadmap: reprioritize Gitea as bootstrapping-critical, onboarded early (WS-APPS step 2); nextcloud onboarding split out. - task-board: T-6.1a (gitea early), T-6.1b (source+CI migration to Gitea). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
6.1 KiB
05 — GitOps & Delivery
Serves: R-GITOPS-01..03, R-PLAT-02/04, R-ENV-02. Related: ADR-0004 (Flux).
Model
Pull-based GitOps with Flux CD. The cluster continuously reconciles its state from git. Git is the single source of truth; the cluster conforms to git, not the reverse (R-GITOPS-01). Drift is detected and corrected automatically (R-GITOPS-03).
Repository layout (the implementation repo, created in a later phase)
This spec repo (
home-network-k3s) holds intent. The implementation repo(s) hold the reconciled manifests. Proposed structure:
platform/ ← cluster-wide platform services (Flux-managed)
cilium/ ← CNI, egress gateway, network baseline
longhorn/ ← storage
cert-manager/ ← TLS issuers + ClusterIssuer
externaldns/ ← DNS automation
gateway/ ← Gateway API + listeners/TLS
monitoring/ ← kube-prometheus-stack, Loki, Alloy, Alertmanager
velero/ ← backup/DR
kubevirt/ ← KubeVirt + CDI
image-automation/ ← Flux image update automation config
apps/ ← application workloads, one dir per service
plex/ sonarr/ radarr/ jackett/ seerr/ pihole/ nextcloud/
minecraft/ unifi/ semaphore/ kasm/ gitea/ qbittorrent/
catalog/ ← ServiceDefinition entries (the onboarding surface)
clusters/
dev/ ← Flux entrypoint + dev overlay/vars
prod/ ← Flux entrypoint + prod overlay/vars (design-only)
- Kustomize overlays (or equivalent) express environment differences (R-ENV-02). Base is shared;
clusters/devandclusters/prodsupply scoped values only. - HelmReleases (Flux) wrap upstream charts (Cilium, Longhorn, cert-manager, kube-prometheus- stack, etc.); app workloads use charts where good ones exist, otherwise plain manifests.
The onboarding surface: ServiceDefinition (R-GITOPS-02)
Onboarding a service = adding one ServiceDefinition under catalog/. It is the operator-facing
abstraction that the platform compiles into the concrete resources (Deployment/VM, PVCs,
NetworkPolicies, HTTPRoute, Certificate, DNS record, ServiceMonitor, update policy). See
13-service-catalog.md for the schema. Intent lives in one place; the
platform renders the rest.
Reconciliation flow
- Operator opens a PR against the implementation repo (new/updated ServiceDefinition or platform change).
- CI validates (lint, policy checks, schema validation, dry-run/build).
- Merge to the tracked branch.
- Flux reconciles: applies manifests, reports status, corrects drift on an interval.
- Health/rollout status surfaces in the C2 UI (12) and alerts on failure (10).
Secrets in GitOps
Secrets are committed encrypted with SOPS + age; Flux decrypts them in-cluster at
reconciliation. No plaintext secret ever lands in git. See 07-security-and-secrets.md
and ADR-0007.
Automated updates interplay (R-UPD-*)
GitOps and hands-off updates coexist:
- Container image tags advance automatically via Flux image-update-automation driven by per-service image policies (semver range / stable channel) — the controller resolves and reconciles the new version with no human-authored change (R-UPD-02).
- Helm chart / manifest version bumps are proposed by Renovate with auto-merge for allowed update classes.
Full detail and the "is an automated commit a GitOps change?" nuance is in
11-automated-updates.md and ADR-0009.
Bootstrap & recovery
- Flux is bootstrapped onto a fresh cluster by the Ansible handoff (02).
- Because desired state is in git and data is on Longhorn/backups, a rebuilt cluster reconverges by re-bootstrapping Flux and restoring volumes (04).
Source-of-truth: local bootstrap → self-hosted Gitea
The platform hosts its own git, which creates a deliberate bootstrapping sequence
(../CONTRIBUTING.md):
- Bootstrap source. Initially there is no remote git on this network (the prior
home-networkGitea is gone from here). Development is local; the cluster is first reconciled from a bootstrap source (this local repo / an out-of-band source). - Gitea comes online early. Gitea is one of the first services onboarded (13)
so the platform gains its own git host at
gitea.home.keithjackson.dev. - Cut Flux over to Gitea. Once Gitea is stable, Flux's
GitRepositorysource is migrated from the bootstrap source to Gitea; the platform then reconciles from its own git. - CI/CD moves to Gitea Actions. The Kyverno L0 policy gate and the trigger for the in-cluster
Chainsaw conformance Job (14) run as Gitea Actions pipelines on
push/PR; PRs become the enforced merge gate into
main.
Circularity & DR. Gitea runs on the cluster Flux reconciles from Gitea. Mitigations:
- Keep an off-cluster mirror of the platform repo so a source-of-truth exists that outlives the cluster.
- Treat Gitea as DR-critical: its repos/DB are backed up off-cluster (04), so a cluster loss never loses the git history needed to rebuild the platform.
Guardrails
- No
kubectl applyof app state by hand — everything flows through git/Flux (R-GITOPS-01, click- ops-never per AGENTS.md). - Reconciliation errors and drift are alerting conditions (R-OBS-04).
- Base config is never forked per environment (R-ENV-02).
Acceptance gates
- Adding a ServiceDefinition PR results in a fully wired service (compute, storage, policy, TLS, DNS, monitoring) with no manual steps beyond the merge.
- Manually mutating a live resource is auto-reverted by Flux (drift correction proven).
- The same base config renders correctly for both dev and prod overlays.