Commit Graph
191 Commits
Author SHA1 Message Date
Story Crater Bot 44f9bc25c4 fix(cert-manager): regenerate homelab-ca cert with basicConstraints CA:TRUE — old self-signed cert lacked CA:TRUE so the homelab-ca ClusterIssuer rejected it ('certificate is not a CA'); regen keypair Secret + trust-bundle ConfigMaps (4 ns) with matching CA cert 2026-08-12 13:56:37 -07:00
Story Crater Bot 5e97c5cf64 feat(vault): add vault-unseal-keys Secret via ksops after operator init — vault was never initialized (empty S3 bucket), unseal keys captured from init; pod postStart auto-unseals on restart 2026-08-12 13:53:09 -07:00
Story Crater Bot b7b1f15084 fix(logging): deploy loki-s3-creds as kind:Secret via ksops — was a helm-values fragment wired to nothing, loki extraEnvFrom secretRef loki-s3-creds never resolved (CreateContainerConfigError); provides access_key_id/secret_access_key for MinIO S3 backend 2026-08-12 13:46:07 -07:00
Story Crater Bot d51713ad6a fix(iam): deploy authentik-secrets as kind:Secret via ksops — was a helm-values fragment wired to nothing, so envFrom secretRef authentik-secrets never resolved (CreateContainerConfigError); provides AUTHENTIK_SECRET_KEY/BOOTSTRAP_PASSWORD/BOOTSTRAP_TOKEN 2026-08-12 13:37:39 -07:00
Story Crater Bot fa239972a7 fix(cert-manager): render issuers via kustomization resources list, restore automated sync — directory.include with bare filenames rendered empty (never matched), so ArgoCD tracked 0 resources and prune wiped the CA ConfigMaps + ClusterIssuers 2026-08-12 13:30:46 -07:00
Story Crater Bot f53d54cba9 fix(argocd): disable automated sync on cert-manager-issuers — directory.include renders empty, automated prune was wiping ClusterIssuers + homelab-ca ConfigMaps; manual sync until render root-caused 2026-08-12 13:28:24 -07:00
Story Crater Bot e4bbec95fb fix(cert-manager): drop empty kustomization.yaml shadowing cert-manager-issuers directory.include — stub rendered 0 resources, tripping ArgoCD 'auto-sync will wipe all resources' halt, blocking the homelab-ca.crt ConfigMap fix that authentik CA-init needs 2026-08-12 13:19:17 -07:00
Story Crater Bot beb3cb21a0 refactor(argocd): replace SOPS CMP with ksops kustomize generator, rotate age key — CMP discover glob silently shadowed kustomize rendering of any app whose path held a .enc.yaml (MinIO Tenant/cloudflared/authentik jobs never applied); centralize 8 Secret manifests under k8s/argocd/secrets, defer 4 helm-values fragments 2026-08-12 13:16:15 -07:00
Story Crater Bot 9e84fb3386 fix(cert-manager): add homelab-ca.crt key to homelab-ca ConfigMaps — authentik init merge-ca-certs cats /homelab-ca/homelab-ca.crt which was missing, causing Init:Error and 503 2026-08-12 09:13:15 -07:00
Story Crater Bot 2333310c38 fix(argocd): resolve 502 on argocd.riotpiao.com, dedupe Ingress and TLS mode mismatch
argocd-server ran --insecure (plain HTTP :8080) while its Helm-managed
Ingress set ssl-passthrough: true, which sends nginx's raw TLS handshake
straight to the pod - HTTP server can't complete a TLS handshake, nginx
logged 502 (peer closed connection in SSL handshake). Compounded by a
second, conflicting Ingress for the same host in
k8s/bootstrap/ingress/ingress.yaml - two Ingress objects on one host is
undefined nginx routing behavior. Disabled the Helm-managed Ingress
(enabled: false) so ingress.yaml's passthrough Ingress is the sole
source of truth, and set server.insecure: false so argocd-server
actually terminates TLS itself, matching passthrough's requirement.
2026-08-11 21:03:59 -07:00
Story Crater Bot be16020878 fix(argocd): use comma-separated include list, not brace expansion
ArgoCD directory.include uses Go filepath.Match glob syntax, not shell
brace expansion - {a,b,c} silently matched nothing, only the original 2
files stayed tracked.
2026-08-11 20:51:05 -07:00
Story Crater Bot e5c371ed39 feat(cert-manager): add self-signed homelab-ca ClusterIssuer + trust bundle, fix grafana-oidc secret
homelab-ca was referenced by 6 manifests (authentik, forgejo-runner,
blackbox-exporter, management-service) as a CA trust ConfigMap but never
existed anywhere - not in git, not live in cluster. Generated a new
10-year self-signed root CA, wired it as a ClusterIssuer (cert-manager
namespace) and distributed the public cert as a ConfigMap to every
consuming namespace (iam, cicd, monitoring, sqs). Private key lives only
in the encrypted Secret. Widened cert-manager-issuers' directory include
glob rather than creating a new Application - destination.namespace is
just a fallback default on a plain directory source, not a transformer,
so it doesn't fight with each ConfigMap's own explicit namespace.

Also adds grafana-oidc secret (GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET),
same pre-existing gap as grafana-admin - was meant to come from a deleted
manual script, value already available in .env.
2026-08-11 20:49:38 -07:00
Story Crater Bot 8b88e13762 fix(portainer): pin to az-b (talos-cp-2), the real Longhorn storage node
nodeSelector still targeted az-a/talos-cp-1 from before the 3-CP topology
change. talos-cp-2 (az-b) has the dedicated Longhorn disks now, so the
pod's zone pin and the PVC's only viable replica location never matched
- ReplicaSchedulingFailure: disks are unavailable, pod stuck
ContainerCreating waiting on AttachVolume.
2026-08-11 16:12:13 -07:00
Story Crater Bot e691a91df1 fix(vault): add vault-minio-creds secret, was created by deleted helmfile presync hook
Vault's S3 storage backend needs AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY
from vault-minio-creds, previously generated by a helmfile presync hook
that no longer exists post-Terraform/helmfile removal. Sourced from the
same MINIO_ROOT_USER/PASSWORD already in .env. vault-unseal-keys still
missing separately — needs a live 'vault operator init' run, deferred.
2026-08-11 16:03:46 -07:00
Story Crater Bot 3fd930c518 fix(portainer): correct storageClass name, longhorn-wffc never existed as a class
PVC sat Pending for 17 days — storageclass.storage.k8s.io "longhorn-wffc"
not found. Only longhorn, longhorn-cnpg, longhorn-static exist. Straight
naming drift, no such class was ever created.
2026-08-11 14:50:39 -07:00
Story Crater Bot 88e73a885a fix(argocd): CPU limit throttling repo-server, skip non-manifest .enc.yaml docs, add grafana-admin admin-user key
repoServer CPU limit (500m) was too tight once the SOPS sidecar added real
decrypt work under the liveness probe's 1s timeout — repo-server kept
getting killed mid-sync. Raised to 1000m (node has 23+ idle cores, no
scarcity). Separately, the generate script's doc-separator fix exposed
that several .enc.yaml files (cloudflared, temporal, authentik, loki) are
raw Helm-values snippets, not K8s manifests — ArgoCD hard-failed the whole
batch on the first one missing 'kind:'. Script now skips those, so
correctly-shaped Secrets (grafana-admin included) sync independently.
grafana-admin also needed an admin-user key alongside admin-password —
the chart looks up both from the same existingSecret.
2026-08-11 14:41:32 -07:00
Story Crater Bot f06cefabc4 fix(argocd): wire missing SOPS CMP plugin sidecar on repo-server, add grafana-admin secret
Sidecar container was absent from live repo-server Deployment (never in
helm history), causing sops-secrets Application to fail with cmp-server
socket not found — cascaded CreateContainerConfigError across every app
depending on SOPS-decrypted secrets. Also fixes duplicate version field
in plugin ConfigMap that produced a mismatched socket filename, and adds
an initContainer to fetch the sops binary into a writable emptyDir since
the sidecar runs non-root. grafana-secrets.enc.yaml rewritten from a bare
values file (never valid as a K8s Secret) to a proper Secret manifest so
grafana-admin now actually gets created.
2026-08-11 14:06:06 -07:00
Story Crater Bot feb7b25aba feat(argocd): migrate all applications from Forgejo to GitHub
- Replace all forgejo.riotpiao.com repo URLs with [email protected] SSH URLs
- Enables immediate GitOps sync without waiting for Forgejo mirror setup
- Includes ingress-nginx now fully ArgoCD-managed (wave 0)
- SOPS secrets can now sync and decrypt TLS certificates
2026-07-25 13:10:40 -07:00
Story Crater Bot 9d0ffcd29f feat(argocd): migrate ingress-nginx to full GitOps management
- Create ArgoCD Application for ingress-nginx controller (wave 0)
- Source: GitHub repo + Helm chart with local values file
- Adopts existing bootstrap Helm release (no downtime)
- Enables automated sync and self-heal for nginx configuration
2026-07-25 13:05:49 -07:00
Story Crater Bot 47e7a2b1d6 feat(bootstrap): add Phase 1c nginx ingress controller
- Add p1_ingress() phase to install nginx-ingress-controller
- Create ingress-nginx namespace with privileged PodSecurity label
- Disable ServiceMonitor during bootstrap (Prometheus CRDs not installed yet)
- Add namespace.yaml with PodSecurity labels (allows hostPort)
- Filter cert-manager CRD errors (will be created by ArgoCD)
- Include ingress phase in bootstrap 'all' flow
2026-07-25 12:39:07 -07:00
Story Crater Bot 8d63db9f3b fix(bootstrap): complete Phase 4 ArgoCD bootstrap with all permanent fixes
- Fix ArgoCD Application schema: move syncOptions under syncPolicy (00-secrets.yaml)
- Remove helm install --wait flag (talos-cp-2 slow node timeout issue)
- Add comprehensive progress logging with timestamps to bootstrap.sh
- Fix SOPS key path (/Users/rockliang/.sops/key.txt, not homelab-age.key)
- Add local SOPS decryption for bootstrap secrets
- Add CNPG NetworkPolicy allowing app→database connectivity
- Disable Forgejo bundled dependencies (saves 66Gi storage)
- Inject database credentials via deployment.env (GITEA__DATABASE__*)
- Remove invalid ext4 mount options from StorageClass
- Add namespace manifests with PodSecurity labels
- Add encrypted forgejo-admin secret (SOPS)
- Reduce forgejo-db size 50Gi→25Gi per instance
- Prepare ArgoCD SOPS CMP plugin (for post-bootstrap)
2026-07-25 12:24:30 -07:00
Story Crater Bot bf67d2d9de fix: patch bootstrap cluster with correct config 2026-07-25 07:09:45 -07:00
Story Crater Bot 95ae933489 feat(bootstrap): Phase-0 GitHub-seed bootstrap — root-app-github (SSH seed), deploy-key Secret template, cutover URL, bootstrap.sh runner (cilium→longhorn→cnpg→forgejo-db→argocd→cutover) 2026-07-23 21:01:12 -07:00
Story Crater Bot e6f2ab1423 refactor(k8s): consolidate to infra/+apps/ single-source tree, dedicated per-app CNPG (authentik-db/temporal-db), wire monitoring-config, forgejo→cicd ns, drop orphan/stale (data-schemas, ollama, story-crater, sqs/argocd, key-rotation) 2026-07-23 20:54:02 -07:00
Story Crater Bot 1c7395d9e1 feat:Fix the bootstrap to be deploy key application 2026-07-23 19:07:39 -07:00
Story Crater Bot eba9f2144c fix(forgejo-runner): use unified longhorn StorageClass
CHANGE: longhorn-wffc → longhorn

Forgejo-runner PVCs were Pending due to obsolete StorageClass.
Unified longhorn provides 3-replica HA storage.
2026-07-23 11:00:09 -07:00
Story Crater Bot 4ad4df7965 refactor(temporal): adopt unified CNPG pattern - use 'app' user
CHANGES:
  - temporal-values.yaml: user 'app', existingSecret 'ddb-cluster-app'
  - bootstrap.sh: Copy ddb-cluster-app to temporal namespace
  - Removed db-secret-sync directory (obsolete PostSync Job)
  - 60-applications.yaml: Removed db-secret-sync source from temporal Application

PATTERN (same as Forgejo/Authentik):
  1. Database CR: owner app
  2. bootstrap.sh: Copy ddb-cluster-app to temporal namespace
  3. App values: Reference ddb-cluster-app secret
  4. No PostSync Jobs needed

FIXES:
  - Temporal schema CrashLoopBackOff (wrong credentials)
  - Dropped/recreated databases with app owner (clean state)

Following CLAUDE.md CNPG pattern documentation.
2026-07-23 10:57:53 -07:00
Story Crater Bot 8fda8c50d3 refactor(argocd): remove orphaned infrastructure Applications - bootstrap is source of truth
REMOVED ORPHANED APPLICATIONS:
  - cnpg-operator (OutOfSync, conflicted with bootstrap)
  - forgejo (OutOfSync, conflicted with bootstrap)
  - ingress-nginx-bootstrap (orphaned, no ownerReferences)

ARCHITECTURE NOW CLEAN:
   Bootstrap: 7 manifests (infrastructure base for regional deployment)
     - ArgoCD, CNPG operator, DDB, Forgejo, ingress-nginx, namespaces, wait-for-databases
   ArgoCD: 32 Applications (all services/apps)
   No duplicate management

DEPLOYMENT FLOW:
  1. kubectl apply -k k8s/bootstrap-local/ (infrastructure)
  2. kubectl apply -k k8s/argocd/root/ (app-of-apps)
  3. ArgoCD auto-syncs from Forgejo (applications)

CLEANUP:
  - Archived old bootstrap configs (k8s/argocd/bootstrap.archived/)
  - Deleted orphaned Applications (ArgoCD tracking only, resources untouched)

Bootstrap remains single source of truth for infrastructure.
ArgoCD manages all applications and services.
2026-07-23 10:29:10 -07:00
Story Crater Bot 751ae733d5 refactor(cnpg): adopt unified Forgejo pattern for all apps
UNIFIED PATTERN: All apps follow same credential distribution

FORGEJO PATTERN (now universal):
  1. CNPG creates ddb-cluster-app in ddb namespace (source)
  2. bootstrap.sh copies to app namespaces (cicd, iam)
  3. Apps reference local copy via secretKeyRef
  4. No PostSync Jobs needed

CHANGES:
  - bootstrap.sh: Copy ddb-cluster-app to iam namespace (like cicd)
  - authentik-values.yaml: Reference local ddb-cluster-app via env vars
  - Removed: sync-db-credentials PostSync Job (not needed)
  - kustomization.yaml: Removed PostSync Job reference

BENEFITS:
   Same pattern as working Forgejo
   No complex PostSync Jobs
   bootstrap.sh handles setup for future clusters
   Simple secretKeyRef, no cross-namespace issues
   ArgoCD manages applications, not secrets

Database recreated with app owner (fresh migrations needed).
2026-07-23 10:12:40 -07:00
Story Crater Bot 68107ba962 fix(authentik): use 'app' database credentials from CNPG (GitOps)
GITOPS FIX: Permanent solution for database credentials

CHANGES:
  1. authentik-values.yaml:
     - postgresql.user: authentik → app
     - env vars reference ddb-cluster-app secret (via secretKeyRef)
     - Both server + worker containers updated

  2. sync-db-credentials-job.yaml (PostSync):
     - Copies ddb-cluster-app from ddb → iam namespace
     - Allows secretKeyRef to work (no cross-namespace support)
     - Runs after every iam-jobs sync

  3. kustomization.yaml:
     - Added sync-db-credentials-job to resources

REPLACES:
  - Manual kubectl patch of authentik-secrets
  - SOPS-encrypted per-app credentials
  - Complex permission grants

BENEFITS:
   ArgoCD won't revert changes (in git)
   Follows CNPG simple pattern (app user)
   Single source of truth (ddb-cluster-app)
   Auto-syncs on every deploy

Deployed by: iam-jobs Application (wave 3)
2026-07-23 10:05:37 -07:00
Story Crater Bot c2bcda58d9 refactor(cnpg): adopt simple pattern - all apps use 'app' user
ARCHITECTURAL CHANGE: Align with CNPG design intent

BEFORE (Complex, broken):
  - Per-app roles (authentik, temporal) with Database CR owner field
  - Database CR doesn't transfer ownership properly
  - Needed manual permission grants (PostSync Job)
  - Apps couldn't create tables without grants from 'app' role

AFTER (Simple, works):
  - All apps use shared 'app' bootstrap user
  - Database CRs: owner: app (matches actual ownership)
  - No permission grants needed (owner has full rights)
  - Isolation via separate database names only

CHANGES:
  - Database CRs: owner changed from app-specific to 'app'
  - ddb-cluster.yaml: removed managed.roles section
  - Deleted grant-schema-permissions PostSync Job
  - Follows Forgejo pattern (already working this way)

MANUAL STEPS REQUIRED:
  1. Update authentik-secrets: AUTHENTIK_POSTGRESQL__USER=app
  2. Update temporal secrets: similar change
  3. Recreate databases with app as owner
  4. Restart applications

Benefits:
  - Simpler architecture
  - No permission grant complexity
  - Aligns with CNPG single-cluster design
  - Matches working Forgejo implementation
2026-07-23 09:59:06 -07:00
Story Crater Bot 14a17b4542 fix(authentik): increase startup probe timeout for migrations
Fresh authentik deployment runs ~100 database migrations which takes 15-20
minutes. Previous startup probe failureThreshold of 60 (10 minutes) killed
the pod before migrations could complete, causing infinite restart loop.

Increased to 120 failures (20 minutes) to allow migrations to finish.

Fixes: nginx 503 due to pod never becoming Ready.
2026-07-23 09:47:50 -07:00
Story Crater Bot ce019f5f3a fix(ddb): add database-level CREATE privilege for schema creation
Authentik migrations need to CREATE SCHEMA (not just tables in public schema).
This requires GRANT CREATE ON DATABASE, not just schema-level permissions.

Added to PostSync Job:
- GRANT CREATE ON DATABASE authentik TO authentik
- GRANT CREATE ON DATABASE temporal TO temporal
- GRANT CREATE ON DATABASE temporal_visibility TO temporal

App user can grant these (it owns the databases).
2026-07-23 09:36:23 -07:00
Story Crater Bot a55918e1f0 fix(storage): consolidate longhorn-kafka → unified longhorn StorageClass
Removes duplicate longhorn-kafka StorageClass managed by Kafka chart.
All applications now use single 'longhorn' StorageClass (3 replicas, Immediate binding).

Changes:
- Kafka chart: use 'longhorn' instead of 'longhorn-kafka'
- Delete Kafka StorageClass template (no longer needed)
- Update longhorn-storageclass.yaml to match deployed config (Immediate, not WaitForFirstConsumer)

Existing Kafka PVCs remain bound to old longhorn-kafka StorageClass (safe - no data loss).
New PVCs will use unified 'longhorn' StorageClass.
2026-07-23 09:09:44 -07:00
Story Crater Bot 2499cc241f fix(ddb): add PostSync Job for per-database schema permissions
ROOT CAUSE: CNPG Database CR creates databases but doesn't grant schema
permissions to the owner role. Bootstrap DB owner 'app' retains CREATE
privilege on public schema, blocking authentik/temporal from creating tables.

SECURITY FIX: Removed insecure 'GRANT TO PUBLIC' from postInitApplicationSQL.

SOLUTION: PostSync Job connects as 'app' (DB owner) and grants schema
permissions to named roles (authentik, temporal) in their respective databases.
Runs after Database CRs reconcile, survives CNPG database recreation.

Pattern: Per-database grants via PostSync, not cluster-wide PUBLIC grants.
2026-07-23 09:02:19 -07:00
Story Crater Bot 1c98628417 fix(ddb): grant universal schema permissions to all roles
Adds SQL to postInitApplicationSQL granting schema permissions to PUBLIC.
Allows any role (authentik, temporal, etc) to create tables in databases.

For existing cluster: run SQL manually (done).
For future bootstrap: automatic via initdb.

Pattern for apps: Database CR + app-specific init Job optional (co-located).
2026-07-23 08:07:34 -07:00
Story Crater Bot ec046cccde fix(ddb): use app user credentials in db-permissions Job
ddb-cluster-superuser secret doesn't exist (not configured).
Use ddb-cluster-app secret instead - app is DB owner, can grant permissions.
2026-07-23 08:05:03 -07:00
Story Crater Bot 9d485d1238 fix(ddb): add PostSync Job for database schema permissions
CNPG Database CR creates DBs but doesn't grant schema permissions properly.
Database owner is 'app' instead of specified role (authentik, temporal).

PostSync Job grants ALL on schema public to both app and named roles,
ensuring applications can create tables. Runs after Database CRs reconcile.

Fixes: authentik InsufficientPrivilege error on migration.
2026-07-23 08:02:44 -07:00
Story Crater Bot 48aac4998b fix(storage): add PodSecurity privileged labels for minio
Minio operator requires privileged securityContext. Without these labels,
StatefulSet stuck at 0/0 replicas (PodSecurity admission blocks pod creation).
2026-07-23 07:54:20 -07:00
Story Crater Bot 70fcf111b9 fix(ingress): add service alias for CoreDNS compatibility
CoreDNS rewrites *.riotpiao.com → ingress-nginx-controller but bootstrap
deployed as ingress-nginx-bootstrap-controller. Service alias makes both work.
2026-07-23 07:45:23 -07:00
Story Crater Bot 423e40200a refactor(argocd): consolidate Applications (39→35)
Merge related Applications using multi-source pattern and PostSync hooks:

1. ingress-config ← wildcard-cert + homelab-ingress (2→1)
   - Both in k8s/bootstrap/ingress/, now use kustomization
   - Certificate deployed before Ingresses (wave 1)

2. homarr ← homarr + homarr-patches (2→1)
   - Added PostSync hook source (fix-probes-job.yaml)
   - Patches run after Helm chart deployment

3. temporal ← temporal + temporal-db-secret-sync (2→1)
   - Added PostSync hook source (copy-job.yaml)
   - DB secret sync runs after Temporal deployment

4. Removed duplicate: ingress-nginx Application
   - ingress-nginx-bootstrap (bootstrap) is working
   - Removed redundant ArgoCD-managed ingress-nginx
   - Eliminated duplicate DaemonSet

Skipped: cert-manager + cert-manager-issuers
  - Wave separation needed (CRDs before Issuers)
  - Keep separate for safety

Result: 39 → 35 Applications (-4, -10.3%)

Files:
- k8s/bootstrap/ingress/kustomization.yaml (updated)
- k8s/argocd/apps/00-substrate.yaml (merges + removal)
- k8s/argocd/apps/60-applications.yaml (merges)
- CONSOLIDATION-RESULTS.md (documentation)
- APPLICATION-CONSOLIDATION-PLAN.md (analysis)
- GITOPS-STATUS.md (updated inventory)
2026-07-23 07:29:51 -07:00
Story Crater Bot 4e7a7b065e fix(ingress): add TLS configuration for Forgejo Ingress
- Add explicit tls block with riotpiao-com-tls secret
- Enables HTTPS access to https://forgejo.riotpiao.com
- Matches wildcard certificate (*.riotpiao.com)

The file comment mentioned TLS should be handled via default-ssl-certificate,
but explicit TLS blocks are needed for proper HTTPS routing.
2026-07-23 07:20:51 -07:00
Story Crater Bot a831c4d3db fix(ingress) patch the wrong ingress port during bootstrap 2026-07-23 00:14:12 -07:00
Story Crater Bot dafccd5d72 feat: complete GitOps migration, storage HA verification, and cluster fixes
Major accomplishments from comprehensive cluster review:

## Storage HA (answering "are volumes replicated?")
- Verified 3-node Longhorn HA: ALL 17 volumes have 3 replicas
- Fixed CLAUDE.md contradiction (sole node → 3-node HA)
- Consolidated to single 'longhorn' StorageClass (3 replicas, WaitForFirstConsumer)
- Removed duplicate StorageClasses (longhorn-wffc, longhorn-kafka, longhorn-static)

## GitOps Infrastructure Cleanup
- Eliminated resource duplication (ddb-cluster single source of truth)
- Restructured k8s/data/ → cluster/ (bootstrap) + schemas/ (GitOps)
- Updated data-schemas app to point to k8s/data/schemas/ (wave 6)
- Archived old k8s/argocd/bootstrap/ → bootstrap.archived/

## Bootstrap Dependencies Fixed
- Added 05-wait-for-databases.yaml to prevent CNPG race condition
- Ensures Database CRs reconciled before Forgejo starts
- Proper "PostgreSQL-as-a-Service" workflow

## Longhorn CSI Plugin Fixed
- Added patch-csi-tolerations-job.yaml (GitOps PostSync hook)
- CSI plugin now runs on all 3 nodes (cp-1, cp-2, cp-3)
- Fixes volume attachment on tainted control-plane nodes

## Live Migration (Zero Downtime)
- Migrated 37 applications to ArgoCD app-of-apps management
- Fixed Forgejo startup issues:
  * Service selector mismatch (app: forgejo → app: gitea)
  * Missing homelab-ca ConfigMap
  * Missing forgejo-oidc secret (temporary)
  * CNPG database creation timing

## Documentation (10 comprehensive files)
- WHATS-NEXT.md - Daily GitOps workflow
- MIGRATION-STATUS.md - Cluster health report
- REVIEW-SUMMARY.md - Session overview
- GITOPS-REBUILD-PLAN.md - Architecture reference
- DDB-REVIEW.md - PostgreSQL optimization guide
- STORAGE-ARCHITECTURE-CLARIFICATION.md - Storage HA investigation
- BOOTSTRAP-DEPENDENCY-FIX.md - CNPG race condition fix
- STORAGECLASS-CONSOLIDATION.md - Single StorageClass rationale
- IMPLEMENTATION-CHECKLIST.md - Migration checklist
- bootstrap.sh - Automated bootstrap script

## Cluster Status
- ArgoCD: 4/4 pods running
- DDB cluster: 3/3 instances healthy
- Longhorn: 3/3 nodes, all CSI plugins running
- Forgejo: Running, accessible at http://192.168.1.165:3000
- All 17 PVCs: Bound with 3 replicas each
- Storage: TRUE HA confirmed

All future changes via git push only (100% GitOps).
2026-07-22 23:56:34 -07:00
Story Crater Bot e963ceb90e fix(forgejo): rebuild with local storage (single pod, no Longhorn) 2026-07-22 13:26:55 -07:00
Story Crater Bot ed9cf4d1e6 fix(longhorn): add spec.name field to talos-cp-2/cp-3 Node CRDs
Root cause: Longhorn refuses to schedule replicas on nodes without spec.name
field. talos-cp-1 was auto-discovered (has spec.name), but cp-2/cp-3 were
manually created CRDs without it.

Error: 'no node name provided to check node down or deleted'

Fix: Add spec.name matching metadata.name for both nodes.
2026-07-22 13:18:16 -07:00
Story Crater Bot 7b0f9171e2 feat(homarr): add Authentik SSO configuration
Configure Homarr to use Authentik for OIDC authentication:
- AUTH_PROVIDERS: oidc,credentials (both SSO and local auth)
- AUTH_OIDC_ISSUER: Authentik endpoint
- CLIENT_ID/SECRET: from homarr-oidc secret
- Groups attribute for authorization

Allows users to sign in via Authentik SSO.
2026-07-22 11:17:22 -07:00
Story Crater Bot 2d9a23c4db fix(homarr): correct ingress port from 3000 to 7575
Service listens on port 7575 (chart default), not 3000.
Nginx was routing to wrong port → 503 errors.
2026-07-22 11:16:17 -07:00
Story Crater Bot 7854e4557e fix(homarr): use python:3.12-alpine + wget kubectl in probe patch Job
bitnami/kubectl:1.31 doesn't exist (Bitnami retired versioned tags in 2025).
Standard pattern: python:3.12-alpine + wget kubectl binary.
2026-07-22 11:00:20 -07:00
Story Crater Bot 162d95c314 fix(homarr): remove encrypted secret from kustomization
homarr-patches Application doesn't have SOPS support.
Secret is managed by sops-secrets Application instead.

Kustomization now only contains:
- fix-probes-job.yaml (PostSync hook)
2026-07-22 10:57:08 -07:00