# Homelab Bootstrap — Single-Cluster, GitOps-Ready

**Run once manually, GitOps forever after.**

This bootstrap breaks the ArgoCD ↔ Forgejo circular dependency by:
1. Installing infrastructure in correct dependency order
2. Pointing ArgoCD at a GitHub mirror initially
3. Cutting over to Forgejo once healthy
4. Using Helm for reproducible installs
5. Ensuring ArgoCD adopts (not duplicates) bootstrap resources

## Prerequisites

- Talos cluster running (terraform applied)
- kubectl configured (`KUBECONFIG` points at cluster)
- Helm 3 installed
- SOPS age key at `~/.sops/homelab-age.key`
- GitHub mirror of this repo (for initial ArgoCD source)

## Directory Structure

```
bootstrap/
├── phase1-storage/         # Longhorn via Helm
├── phase2-cnpg/           # CNPG operator via Helm
├── phase3-forgejo/        # Forgejo DB + Forgejo via Helm
├── phase4-argocd/         # ArgoCD via Helm → GitHub initially
└── phase5-cutover/        # Switch ArgoCD source to Forgejo
```

## Usage

```bash
# From repo root:
./bootstrap.sh

# Or step-by-step:
./bootstrap.sh phase1  # Storage
./bootstrap.sh phase2  # CNPG
./bootstrap.sh phase3  # Forgejo
./bootstrap.sh phase4  # ArgoCD (GitHub mirror)
./bootstrap.sh phase5  # Cut over to Forgejo
```

## Design Principles

1. **DRY**: Helm values used by both bootstrap and ArgoCD
2. **Single Source of Truth**: Manifests match what ArgoCD will manage
3. **Idempotent**: Can re-run phases safely
4. **Adoption Ready**: Resources have `argocd.argoproj.io/sync-options: Prune=false`
5. **Dependency Ordered**: Each phase waits for previous to be Ready

## Phase Details

### Phase 1: Storage (Longhorn)

Installs Longhorn with:
- 3-node HA configuration
- Unified `longhorn` StorageClass (default)
- Special `longhorn-cnpg` StorageClass with postgres UID/GID mount options
- CSI plugin tolerations for control-plane nodes

**Source of Truth**: `phase1-storage/longhorn-values.yaml`

### Phase 2: CNPG Operator

Installs CloudNativePG operator with:
- CRD registration (blocks until CRD available)
- Webhook configuration
- Monitoring enabled

**Source of Truth**: `phase2-cnpg/cnpg-values.yaml`

### Phase 3: Forgejo Database + Forgejo

1. Creates `forgejo-db` CNPG Cluster
2. Waits for cluster Ready (PostgreSQL accepting connections)
3. Installs Forgejo via Helm pointing at `forgejo-db-rw` service
4. Waits for Forgejo healthy

**Source of Truth**: 
- `phase3-forgejo/forgejo-db.yaml` (CNPG Cluster CR)
- `phase3-forgejo/forgejo-values.yaml` (Helm values)

### Phase 4: ArgoCD (GitHub Mirror)

Installs ArgoCD via Helm, then applies root app-of-apps pointing at **GitHub mirror**.

This is the circle-breaker: ArgoCD syncs from GitHub (not Forgejo) initially.

**Source of Truth**: 
- `phase4-argocd/argocd-values.yaml`
- `phase4-argocd/root-app-github.yaml` (repoURL = GitHub)

ArgoCD **adopts** Phases 1-3 resources (no duplication) because manifests match.

### Phase 5: Cut Over to Forgejo

1. Push repo to Forgejo
2. Update root app `repoURL` from GitHub → Forgejo
3. ArgoCD re-syncs from Forgejo

**The circle is broken. GitHub mirror is now disaster recovery only.**

## Post-Bootstrap

All changes via Git:
```bash
git commit -m "feat(app): add new service"
git push forgejo main
# ArgoCD auto-syncs
```

## Troubleshooting

- **Phase stuck?** Check `kubectl get events -n <namespace> --sort-by='.lastTimestamp'`
- **ArgoCD duplicating?** Verify manifests match exactly (Helm values ↔ ArgoCD Application)
- **Forgejo won't start?** Check CNPG cluster Ready: `kubectl get cluster forgejo-db -n forgejo`
- **Can't push to Forgejo?** Verify ingress-nginx healthy, DNS resolves `forgejo.riotpiao.com`

## Migration from Old Bootstrap

If you have existing `k8s/bootstrap-local/`:

1. **DO NOT delete** existing resources (Longhorn data!)
2. Run refined bootstrap in "adoption mode" (no delete, just apply)
3. Verify ArgoCD shows "Synced" for all apps
4. Archive old bootstrap: `git mv k8s/bootstrap-local k8s/archive/bootstrap-local-v1`
