Merge feature/dev-workflow-and-gitea-bootstrap into main

Establishes the feature-branch→main workflow and Gitea-early bootstrap plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
jakman85andClaude Opus 4.8 committed 2026-08-15 22:40:17 -05:00
commit 1a67ab5cfa
8 files changed
+126 -6

No files matched your search

+13
View File
@@ -146,3 +146,16 @@ This is a hard boundary (R-TEST-06):
The point: the specification is the fixed target. Agents move the implementation and its tests
toward the target; they never move the target.
## 12. Version-control workflow
Full detail in [`CONTRIBUTING.md`](CONTRIBUTING.md). The rules that bind every change:
- **No work on `main`.** `main` holds only completed, reviewed work.
- **Every change is a feature branch** (`feature/…`, `fix/…`, `docs/…`, `chore/…`), **merged into
`main`** when complete (and, for implementation, when its conformance tests pass).
- **Local git for now; Gitea soon.** There is no remote host on this network yet — the prior
`home-network` Gitea is gone from this network. **Gitea is one of the first services brought online
on the cluster**, after which repos, the GitOps source, and CI/CD move to Gitea (Gitea Actions
running the Kyverno gate + Chainsaw conformance Job, with PRs as the enforced merge gate).
- **Never commit plaintext secrets** (SOPS-encrypted only, [`docs/07-security-and-secrets.md`](docs/07-security-and-secrets.md)).
+64
View File
@@ -0,0 +1,64 @@
# Contributing & Development Workflow
How work is version-controlled for this repository. This complements the governance rules in
[`AGENTS.md`](AGENTS.md) (what agents may/may not change) — this file covers *how changes land in
git*.
## Interim state (now): local git, feature branches → main
There is **no remote git host on this network today**. (The previous `home-network` Gitea instance is
gone from this network; the infrastructure it built still runs but is not reachable here.) So for now:
- **Local git only.** The repository lives on the workstation; there is no push target yet.
- **No work on `main`.** `main` holds only reviewed, completed work. Do **not** commit work-in-
progress directly to `main`.
- **Every change is a feature branch.** Create `feature/<short-description>` (or `fix/…`, `docs/…`)
for any unit of work, commit there, then **merge into `main`** when the work is complete and — for
implementation work — its conformance tests pass ([`agents/definition-of-done.md`](agents/definition-of-done.md)).
- **Merge to `main` = "done".** Keep `main` always in a coherent, mergeable state.
### Branch naming
- `feature/<slug>` — new capability or spec area
- `fix/<slug>` — corrections
- `docs/<slug>` — documentation-only changes
- `chore/<slug>` — tooling/repo housekeeping
### Commit hygiene
- Small, single-responsibility commits; imperative subject lines.
- Reference requirement IDs / ADR numbers / task-board IDs where relevant (they're stable across
file moves).
- **Never commit plaintext secrets** — secrets are SOPS-encrypted only ([`docs/07-security-and-secrets.md`](docs/07-security-and-secrets.md));
the [`.gitignore`](.gitignore) guards common leak paths but is not a substitute for care.
## Target state (soon): Gitea-hosted, CI/CD-driven GitOps
The platform will **host its own git**. **Gitea is one of the first services brought online on the
cluster** ([`docs/13-service-catalog.md`](docs/13-service-catalog.md), roadmap Phase 6 is pulled
early — see [`roadmap/phases.md`](roadmap/phases.md)). Once Gitea is up and stable, the workflow
shifts:
1. **Repos move to Gitea** (`gitea.home.keithjackson.dev`). This local repo is pushed up as the
origin of record.
2. **GitOps source migrates** from local/bootstrap to Gitea: Flux reconciles the cluster from Gitea
([`docs/05-gitops-and-delivery.md`](docs/05-gitops-and-delivery.md)).
3. **CI/CD runs in Gitea** (Gitea Actions): the Kyverno L0 policy gate and the trigger for the
in-cluster Chainsaw conformance Job run as pipelines on push/PR
([`docs/14-testing-and-conformance.md`](docs/14-testing-and-conformance.md)); Renovate opens
update PRs ([`docs/11-automated-updates.md`](docs/11-automated-updates.md)).
4. **Pull requests become the merge gate** into `main`, with CI (policy + conformance) required
green before merge — the same no-work-in-main rule, now enforced by the platform instead of by
convention.
### The bootstrapping caveat (self-hosted git is circular)
Gitea runs *on* the cluster that Flux reconciles *from* Gitea — a chicken-and-egg. Handling:
- Bring the cluster up from a **bootstrap source** (this local repo / an out-of-band source), stand
up Gitea, then **cut Flux's source over to Gitea**.
- Because a cluster loss would take its own git host with it, **Gitea is DR-critical**: its repos are
backed up **off-cluster** ([`docs/04-storage-and-state.md`](docs/04-storage-and-state.md)), and an
off-cluster mirror of the platform repo is kept so the platform can always be rebuilt from a source
that outlives the cluster.
## What does not change
- Specs remain **human-owned and immutable to agents** ([`AGENTS.md` §11](AGENTS.md)) regardless of
where git is hosted.
- "Done" remains a **green, reproducible conformance test**, not "merged."
+1
View File
@@ -61,6 +61,7 @@ GitOps configuration promotes to production with only environment-scoped overrid
home-network-k3s/
├── README.md ← you are here
├── AGENTS.md ← rules of engagement for AI agents
├── CONTRIBUTING.md ← git workflow (feature branches → main; Gitea/CI-CD to come)
├── GLOSSARY.md ← shared vocabulary (read to avoid ambiguity)
├── docs/ ← the specification, by domain
│ ├── 00-vision-and-scope.md
+3 -1
View File
@@ -74,11 +74,13 @@ Seeded from [`../roadmap/phases.md`](../roadmap/phases.md) + [`../roadmap/workst
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-6.1 | Onboard **pihole** (DNS underpinning) | WS-APPS | R-DNS-03 | T-2.5 | — | todo |
| T-6.1a | Onboard **gitea** early (bootstrapping-critical; self-hosts GitOps source + CI/CD) | WS-APPS | R-STATE-03 | T-4.4 | — | todo |
| T-6.1b | Migrate GitOps source local→Gitea; move CI to **Gitea Actions** (Kyverno gate + Chainsaw Job trigger; PR merge gate); off-cluster repo mirror | WS-GITOPS | R-GITOPS-01, R-TEST-03 | T-6.1a | — | todo |
| T-6.2 | Onboard **plex** (bulk tier + public exposure) | WS-APPS | R-STATE-04 | T-2.6, T-6.1 | — | todo |
| T-6.3 | Onboard **qbittorrent, jackett** (VPN + kill-switch) | WS-APPS | R-NET-05/06 | T-3.4 | — | todo |
| T-6.4 | Onboard **sonarr, radarr** (VPN + arr↔downloader allows) | WS-APPS | R-NET-02/05 | T-6.3 | — | todo |
| T-6.5 | Onboard **seerr** (allows to arr + plex) | WS-APPS | R-NET-02 | T-6.2, T-6.4 | — | todo |
| T-6.6 | Onboard **nextcloud, gitea** (DR-critical + backups) | WS-APPS | R-STATE-03 | T-4.4 | — | todo |
| T-6.6 | Onboard **nextcloud** (DR-critical + backups) | WS-APPS | R-STATE-03 | T-4.4 | — | todo |
| T-6.7 | Onboard **minecraft, unifi, semaphore, kasm** | WS-APPS | app reqs | T-2.6 | — | todo |
| T-6.8 | C2 UI: Homepage root + Grafana + Headlamp + Flux status + auth | WS-C2 | R-C2-01/02 | T-4.2, T-6.* | — | todo |
+22
View File
@@ -80,6 +80,28 @@ Full detail and the "is an automated commit a GitOps change?" nuance is in
- Because desired state is in git and data is on Longhorn/backups, a rebuilt cluster reconverges by
re-bootstrapping Flux and restoring volumes ([04](04-storage-and-state.md)).
## Source-of-truth: local bootstrap → self-hosted Gitea
The platform hosts its own git, which creates a deliberate bootstrapping sequence
([`../CONTRIBUTING.md`](../CONTRIBUTING.md)):
1. **Bootstrap source.** Initially there is no remote git on this network (the prior `home-network`
Gitea is gone from here). Development is local; the cluster is first reconciled from a
**bootstrap source** (this local repo / an out-of-band source).
2. **Gitea comes online early.** **Gitea is one of the first services onboarded** ([13](13-service-catalog.md))
so the platform gains its own git host at `gitea.home.keithjackson.dev`.
3. **Cut Flux over to Gitea.** Once Gitea is stable, Flux's `GitRepository` source is migrated from
the bootstrap source to Gitea; the platform then reconciles from its own git.
4. **CI/CD moves to Gitea Actions.** The Kyverno L0 policy gate and the trigger for the in-cluster
Chainsaw conformance Job ([14](14-testing-and-conformance.md)) 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](04-storage-and-state.md)),
so a cluster loss never loses the git history needed to rebuild the platform.
## Guardrails
- No `kubectl apply` of app state by hand — everything flows through git/Flux (R-GITOPS-01, click-
ops-never per [AGENTS.md](../AGENTS.md)).
+7 -1
View File
@@ -75,7 +75,7 @@ Egress and dependencies below are **normative intent**; implementers render the
| 9 | **unifi** | container | internal | internet | config: longhorn | — | UniFi network controller |
| 10 | **semaphore** | container | internal | internet | config/db: longhorn | — | Ansible UI |
| 11 | **kasm** | container | internal | internet | config: longhorn | — | Kasm Workspaces (may need more resources; container-first, VM if required) |
| 12 | **gitea** | container | public | internet | repos/db: longhorn (irreplaceable → backed up) | — | Git hosting; DR-critical |
| 12 | **gitea** | container | public | internet | repos/db: longhorn (irreplaceable → backed up) | — | Git hosting; **bootstrapping-critical** — self-hosts the platform's GitOps source + CI/CD; onboarded **early** (see below); DR-critical (off-cluster backup + mirror) |
| 13 | **qbittorrent** | container | internal | **vpn** | config: longhorn; downloads: **bulk** | ← sonarr, ← radarr | VPN + kill-switch; download client |
**VPN-class set (R-NET-05/06):** sonarr, radarr, jackett, qbittorrent — all egress via VPN with an
@@ -103,6 +103,12 @@ and inherit all guarantees automatically — that is the point of the abstractio
([09](09-dns-and-ingress.md)); everything else stays `internal`.
- **Kasm** is resource-heavy and may be a candidate for `kind: vm` if container mode proves
insufficient — the ServiceDefinition makes that a one-field change.
- **Gitea is bootstrapping-critical.** The platform self-hosts its git and CI/CD, so Gitea is brought
online **early** (WS-APPS step 2, right after PiHole). Once stable, the GitOps source and CI move
from the local bootstrap source to Gitea + Gitea Actions ([05](05-gitops-and-delivery.md),
[`../CONTRIBUTING.md`](../CONTRIBUTING.md)). Because a cluster loss would take its own git host with
it, Gitea's repos/DB are backed up **off-cluster** and mirrored, so the platform can always be
rebuilt from a source that outlives the cluster.
## Acceptance gates
- Each service is fully wired from its ServiceDefinition alone (compute, storage, policy, egress,
+3
View File
@@ -77,6 +77,9 @@ survives rebuild; default-deny + `egress` apply to the VM; guest auto-updates.
## Phase 6 — Service Catalog Onboarding + C2 UI
**Entry:** Phases 2–4 done.
**Do:** onboard the 13 services via ServiceDefinitions in dependency order (see workstream WS-APPS);
**bring Gitea online early in this phase** and cut the GitOps source + CI/CD over to it (Gitea
Actions running the Kyverno gate + Chainsaw conformance Job; PRs as the enforced merge gate —
[`../docs/05-gitops-and-delivery.md`](../docs/05-gitops-and-delivery.md), [`../CONTRIBUTING.md`](../CONTRIBUTING.md));
stand up the C2 UI (Homepage root + Grafana + Headlamp + Flux status).
**Exit (demonstrate):** each service fully wired from its ServiceDefinition (compute/storage/policy/
egress/TLS/DNS/monitoring/updates) with default-deny + correct egress; the arr↔qbittorrent↔jackett↔
+13 -4
View File
@@ -50,10 +50,19 @@ only via [`../agents/coordination-protocol.md`](../agents/coordination-protocol.
- WS-C2 is last-ish (Phase 6) since it aggregates everything.
## WS-APPS onboarding order (respect dependencies from [13](../docs/13-service-catalog.md))
1. **pihole** (DNS underpinning) → 2. **plex** (largest; validates bulk tier + public exposure) →
3. **qbittorrent, jackett** (VPN + kill-switch proof) → 4. **sonarr, radarr** (VPN + arr↔downloader
allows) → 5. **seerr** (cross-service allows to arr + plex) → 6. **nextcloud, gitea** (DR-critical
data + backups) → 7. **minecraft, unifi, semaphore, kasm** (remaining).
1. **pihole** (DNS underpinning) →
2. **gitea** — **bootstrapping-critical; brought online early** so the platform self-hosts its git
and CI/CD; after it is stable, WS-GITOPS cuts Flux's source over to Gitea and moves CI to Gitea
Actions ([`../docs/05-gitops-and-delivery.md`](../docs/05-gitops-and-delivery.md),
[`../CONTRIBUTING.md`](../CONTRIBUTING.md)). DR-critical → off-cluster backup + mirror. →
3. **plex** (largest; validates bulk tier + public exposure) →
4. **qbittorrent, jackett** (VPN + kill-switch proof) → 5. **sonarr, radarr** (VPN + arr↔downloader
allows) → 6. **seerr** (cross-service allows to arr + plex) → 7. **nextcloud** (DR-critical data +
backups) → 8. **minecraft, unifi, semaphore, kasm** (remaining).
**GitOps source migration** (WS-GITOPS, tied to step 2): local bootstrap source → self-hosted Gitea;
CI (Kyverno gate + Chainsaw conformance Job trigger) moves to Gitea Actions; PRs become the enforced
merge gate into `main`.
Each onboarding is a full acceptance-gate pass ([`../agents/definition-of-done.md`](../agents/definition-of-done.md)),
not just a merged manifest.