Commit Graph
104 Commits
Author SHA1 Message Date
Story Crater Bot 54fa540b33 feat:Fix the bootstrap to be deploy key application 2026-08-18 15:08:03 -07:00
Story Crater Bot eac3a2a227 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-08-18 15:08:03 -07:00
Story Crater Bot 20bd4dcf5e 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-08-18 15:08:03 -07:00
Story Crater Bot fef07c058a 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-08-18 15:08:03 -07:00
Story Crater Bot 766555453e refactor(cnpg): unify all apps on 'app' database user/credentials pattern 2026-08-18 15:08:03 -07:00
Story Crater Bot 562ffcfc10 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-08-18 15:08:03 -07:00
Story Crater Bot fd4f48c2cb 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-08-18 15:08:03 -07:00
Story Crater Bot a9e062790c 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-08-18 15:08:03 -07:00
Story Crater Bot a07babe2bf fix(ddb): PostSync Job grants per-app-user schema/database permissions 2026-08-18 15:08:03 -07:00
Story Crater Bot ae8d86242d 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-08-18 15:08:03 -07:00
Story Crater Bot ba115c87e2 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-08-18 15:08:03 -07:00
Story Crater Bot e5209e3794 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-08-18 15:08:03 -07:00
Story Crater Bot 8df78be298 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-08-18 15:08:03 -07:00
Story Crater Bot d16203b79d fix(ingress) patch the wrong ingress port during bootstrap 2026-08-18 15:08:03 -07:00
Story Crater Bot f656338a15 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-08-18 15:08:03 -07:00
Story Crater Bot e2dcd7b5d0 fix(forgejo): rebuild with local storage (single pod, no Longhorn) 2026-08-18 15:08:03 -07:00
Story Crater Bot d76caf2b5a 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-08-18 15:08:03 -07:00
Story Crater Bot bc8ffb70e5 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-08-18 15:08:03 -07:00
Story Crater Bot d70993a6bd 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-08-18 15:08:03 -07:00
Story Crater Bot b0b6ac3bb3 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-08-18 15:08:03 -07:00
Story Crater Bot 2f564f02f8 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-08-18 15:08:03 -07:00
Story Crater Bot d394efc78a fix(homarr): probe tuning via dedicated PostSync patch-job Application (chart-values/Kustomize-patch/controller.probes attempts superseded) 2026-08-18 15:08:03 -07:00
Story Crater Bot ab2f1eeeb3 feat(homarr): bring up chart (repo/sourceRepos, image tag, minimal values schema) 2026-08-18 15:08:03 -07:00
Story Crater Bot 85f6984fbd fix(argocd): add insecureSkipVerify for Authentik OIDC
ArgoCD was failing to query Authentik OIDC discovery endpoint with:
  tls: failed to verify certificate: x509: certificate signed by unknown authority

Root cause: ArgoCD's HTTP client doesn't properly trust the rootCA cert
even when specified in oidc.config.

Fixed by adding insecureSkipVerify: true to OIDC config. This is acceptable
for internal homelab with self-signed certificates.

Tested: ArgoCD SSO login via Authentik now works
2026-08-18 15:08:03 -07:00
Story Crater Bot 1f97d744f6 feat(dns): add git.riotpiao.com subdomain for Forgejo SSH access
Adds CoreDNS rewrite: git.riotpiao.com → forgejo-gitea-ssh.cicd.svc.cluster.local

Separates SSH from HTTPS access:
  - forgejo.riotpiao.com → HTTPS/Web UI (192.168.1.160, ingress)
  - git.riotpiao.com → SSH (192.168.1.165:2222, LoadBalancer)

Usage:
  git remote set-url origin ssh://[email protected]:2222/riotpiao.com/homelab.git
  git push

External access requires /etc/hosts entry:
  192.168.1.165  git.riotpiao.com
2026-08-18 15:08:03 -07:00
Story Crater Bot 8b4a5ad129 fix(forgejo): register Authentik OAuth source via CLI
Root cause: Forgejo OAuth env vars (CLIENT_ID, CLIENT_SECRET, etc.) only
configure the OAuth2 *server*-side settings. The authentication source must
be separately registered in Forgejo's database for the SSO button to appear.

Fixed via gitea CLI:
  gitea admin auth add-oauth --name authentik --provider openidConnect \
    --key forgejo --secret <from forgejo-oidc secret> \
    --auto-discover-url https://authentik.riotpiao.com/application/o/forgejo/.well-known/openid-configuration

Verified: login_source table now has id=1, type=6 (OAuth2), name=authentik

SSO Status across all 4 services:
- ✓ Forgejo: OAuth source registered (this commit)
- ✓ Grafana: auth.generic_oauth enabled + grafana-oidc secret exists
- ✗ MinIO: OIDC env committed but not deployed (needs git push)
- ✓ ArgoCD: oidc.config in argocd-cm ConfigMap

User: rock / Password: ea6b6e161318351933bfd3593914fed7
2026-08-18 15:08:03 -07:00
Story Crater Bot 86f94f96fd feat(homarr): complete wiring for landing page deployment
Adds Homarr landing page with Authentik SSO:
- k8s/argocd/apps/60-applications.yaml: multi-source Application (homarr
  chart from homarr-labs + in-repo values), ns dashboard, wave 8
- k8s/bootstrap/ingress/ingress.yaml: homarr.riotpiao.com → dashboard/homarr:3000
- k8s/bootstrap/coredns/coredns-configmap.yaml: rewrite homarr.riotpiao.com
  to ingress controller
- k8s/security/iam/scripts/authentik-provision.py: added 'homarr' to SERVICES
  (generates OAuth provider/app + homarr-oidc secret with client-id/secret)
- k8s/security/iam/rbac-dashboard-rolebinding.yaml: grants authentik-provisioner
  SA access to dashboard ns for secret management
- k8s/security/iam/kustomization.yaml: includes new RoleBinding

Homarr now fully wired:
- Ingress: https://homarr.riotpiao.com
- SSO: redirects to Authentik, login as rock
- Persistence: 5Gi RWO on longhorn-wffc (3-replica HA)
- Tile config: UI-managed (saved to PVC)
2026-08-18 15:08:03 -07:00
Story Crater Bot ff8bc74f63 feat(sso): complete MinIO OIDC env + add Homarr landing page base config
MinIO (Part B):
- k8s/infrastructure/minio/minio-tenant.yaml: added full OIDC env block
  (CONFIG_URL, CLIENT_ID, CLIENT_SECRET from minio-oidc secret, CLAIM_NAME,
  REDIRECT_URI, DISPLAY_NAME, SCOPES) — MinIO console SSO login will now work

Homarr (Part C1 - base):
- k8s/applications/homarr/homarr-values.yaml: official chart config with
  Authentik SSO (AUTH_PROVIDERS=oidc, all OIDC env vars, client creds from
  homarr-oidc secret, SECRET_ENCRYPTION_KEY from SOPS secret)
- k8s/applications/homarr/homarr-secrets.enc.yaml: age-encrypted
  SECRET_ENCRYPTION_KEY (stable key — rotating it breaks saved integrations)
- k8s/applications/homarr/kustomization.yaml: namespace dashboard

Still TODO for Homarr:
- Add 'homarr' to authentik-provision.py SERVICES dict
- Add Application to 60-applications.yaml (multi-source: chart + values)
- Add ingress rule (k8s/bootstrap/ingress/ingress.yaml)
- Add CoreDNS rewrite (k8s/bootstrap/coredns/coredns-configmap.yaml)
- Add dashboard RoleBinding for authentik-provisioner SA
2026-08-18 15:08:03 -07:00
Story Crater Bot 6b5c193b82 feat(longhorn): auto-expand all volumes to 3 replicas via PostSync hook (jq query fix) 2026-08-18 15:08:03 -07:00
Story Crater Bot 2458da8e91 fix(forgejo): remove nodeSelector now that Longhorn runs on all nodes
With Longhorn now running on all 3 control-plane nodes (commit be7881d),
Forgejo pods no longer need to be pinned to talos-cp-1. The gitea-shared-storage
PVC can attach on any node, and the scheduler will properly co-locate pod + volume
via WaitForFirstConsumer + 3-replica Longhorn volumes.

Removes the kubernetes.io/hostname: talos-cp-1 nodeSelector added in commit
dde4b60 (which was a workaround for single-node storage).
2026-08-18 15:08:03 -07:00
Story Crater Bot e2fcfe1fa8 feat(storage): enable Longhorn on all 3 control-plane nodes for true HA
Changes:
- k8s/infrastructure/longhorn/longhorn-taint-toleration.yaml: new Setting
  to tolerate node-role.kubernetes.io/control-plane:NoSchedule taint,
  allowing Longhorn DaemonSet to run on cp-2/cp-3 (not just cp-1)
- k8s/infrastructure/longhorn/longhorn-nodes.yaml: explicit Node CRDs for
  talos-cp-2 and talos-cp-3 (auto-discovery doesn't work when nodes have
  taints; these define /var/lib/longhorn as the storage path)
- k8s/infrastructure/longhorn/longhorn-wffc-storageclass.yaml: bump
  numberOfReplicas from 1→3 (true HA: each volume gets 3 copies across
  3 nodes; if one node fails, 2 others still have the data)
- k8s/infrastructure/longhorn/kustomization.yaml: add new resources

Root cause: Longhorn was only running on talos-cp-1 (.213) because cp-2/cp-3
have the control-plane taint and Longhorn DaemonSet had no matching toleration.
Every workload with a PVC was forced to schedule on cp-1 (via nodeSelector or
implicit co-location with the storage), defeating the entire purpose of a 3-node
HA cluster.

With this fix:
- Longhorn manager runs on all 3 nodes
- Storage is replicated 3x (erasure-coded across nodes)
- Pods can schedule on any node without PVC attachment failures
- True HA: lose 1 node, cluster still serves all volumes
2026-08-18 15:08:03 -07:00
Story Crater Bot 86f5603063 fix(sso): complete forgejo OAuth2 integration + force pods to storage node
Adds missing CLIENT_SECRET env injection + nodeSelector constraint:
- k8s/argocd/bootstrap/forgejo.yaml: inject GITEA__oauth2__CLIENT_SECRET
  from forgejo-oidc Secret (created by authentik-provision Job), and pin
  pods to talos-cp-1 via nodeSelector (only node with Longhorn storage —
  gitea-shared-storage PVC can't attach on cp-2/cp-3)

Root cause chain for 'Forgejo SSO not working':
1. Authentik 2026.5.5 requires explicit grant_types on OAuth2 providers
2. Old provision script never set it → all providers had grant_types=[]
3. /authorize returned 'Invalid grant_type for provider' → all SSO broken
4. Fixed in k8s/security/iam/scripts/authentik-provision.py (commit be2a56c)
   + successfully re-ran via iam-jobs Application sync
5. But Forgejo deployment still missing CLIENT_SECRET env var → no creds
6. Forgejo bootstrap App used inline valuesObject (chicken-egg with git
   repo self-hosting), but missing the extraEnv block that was only in
   k8s/security/ci-cd/forgejo-values.yaml → CLIENT_SECRET never injected

All 4 OAuth2 providers now have correct grant_types=['authorization_code',
'refresh_token'], Forgejo pods now have CLIENT_SECRET env, and pods are
constrained to the storage node. SSO login flow should now work end-to-end.
2026-08-18 15:08:03 -07:00
Story Crater Bot 368e4a020a fix(iam): Authentik OAuth2 provisioning — grant_types/groups claim, idempotent script, skip PATCH on existing apps 2026-08-18 15:08:03 -07:00
Story Crater Bot a9800c7a3e fix(authentik): widen server probe timeouts (3s->15s) — slow-but-200 health checks under DB contention triggered a liveness kill loop, dropping the pod from Service endpoints and breaking OAuth provisioning 2026-08-18 15:08:03 -07:00
Story Crater Bot 51c07a845f fix(temporal): provision schema via CNPG temporal_visibility DB + drop mysql-only tx_isolation param 2026-08-18 15:08:03 -07:00
Story Crater Bot ae4f683850 fix(temporal): switch server to sprig configMapsToMount + setConfigFilePath — dockerize path removed in server 1.30.3, config was not loaded so it fell back to Cassandra and crashed 2026-08-18 15:08:03 -07:00
Story Crater Bot 4c9610f86f fix(minio): set HOME=/tmp in policy-setup PostSync hook — mc could not create /.mc as non-root, hanging the job in an endless wait loop 2026-08-18 15:08:03 -07:00
Story Crater Bot cf8c97864b fix(temporal): db-secret-sync image bitnami/kubectl:1.30 doesn't exist
Bitnami stopped publishing versioned image tags in 2025 - only 'latest' and
sha256-pinned digests remain for their free-tier images. Confirmed via
Docker Hub API before writing this fix: no '1.30' tag exists for
bitnami/kubectl, which caused an indefinite ImagePullBackOff (job stuck
'Running' with 0 pods able to start).

Switched to python:3.12-alpine + a stdlib urllib kubectl download, matching
the exact pattern already proven working in
k8s/security/iam/authentik-provision-job.yaml (which hit its own apk
permission problem on this same base image, now fixed the same way in
both places) - avoids depending on any third party's tagging policy.
2026-08-18 15:08:03 -07:00
Story Crater Bot e821358106 fix(temporal): db-secret-sync Job deadlocked as PreSync hook
PreSync hooks run BEFORE an Application's own normal (non-hook) resources
are synced. This Job's ServiceAccount/ClusterRole/RoleBindings are plain
resources in the same Application, so marking the Job PreSync created a
chicken-and-egg deadlock: confirmed live, the Job sat 'Running' for 14
minutes producing zero pods, with job-controller repeatedly logging
'serviceaccount temporal/temporal-db-secret-sync not found' - because that
ServiceAccount hadn't been created yet (it's created during the normal Sync
phase, which comes after PreSync).

Fixed to PostSync. This app (sync-wave 7) still fully completes - including
this hook - before the temporal Application (sync-wave 8) begins, so the
ordering guarantee we need (secret exists before Temporal's pods try to
mount it) is unaffected; only the intra-app hook-vs-normal-resource
ordering was wrong.
2026-08-18 15:08:03 -07:00
Story Crater Bot 36db843a79 fix(iam): authentik-provision Job failing on apk permission denied
Job was crash-looping: 'apk add --no-cache curl' failed with Permission
denied - the container runs as non-root UID 1000 (securityContext.
runAsNonRoot: true), and both apk's working directories and /usr/local/bin
(where curl-downloaded kubectl was being written) are root-owned in the
python:3.12-alpine base image.

Replaced with a pure-Python download via urllib (stdlib, already a
dependency of this Job) writing to /tmp (world-writable) instead - no apk
install needed at all. PATH is extended to include /tmp before invoking the
provisioning script so authentik-provision.py's existing
subprocess.run(['kubectl', ...]) calls resolve it via normal PATH lookup,
no changes needed to the script itself.
2026-08-18 15:08:03 -07:00
Story Crater Bot 3c1342cef9 fix(temporal): actually enable PostgreSQL persistence (chart schema mismatch)
Root cause: pinned to temporalio/helm-charts @ 0.74.0, which uses the OLD
flat persistence schema (server.config.persistence.<store>.driver/.sql),
NOT the datastores:-wrapped schema shown in the CURRENT chart's
values/values.postgresql.yaml example (that key was introduced in a later
major version). Our old values.yaml used the datastores: key, which doesn't
exist in 0.74.0 - Helm doesn't validate unknown keys, so it was silently a
no-op. persistence.default.driver / persistence.visibility.driver stayed at
their chart default ("cassandra", with empty hosts: []) the entire time,
regardless of anything nested under datastores:.

Verified before writing this fix: cloned temporalio/helm-charts, checked out
tag temporal-0.74.0 (exact pin), ran  +
 against our actual values.yaml - confirmed the rendered
schema-setup Job used CASSANDRA_HOST/temporal-cassandra-tool the whole time.
Re-rendered with the corrected flat schema - zero Cassandra references,
correct postgres12 pluginName/connectAddr wired to ddb-cluster-rw.

Also fixed two compounding no-ops found the same way:
  -  -> real keys are schema.setup.enabled /
    schema.update.enabled / schema.createDatabase.enabled (jobs.autoSetup
    doesn't exist anywhere in this chart's templates or values.yaml).
  - cassandra.enabled was never actually set to false (stayed at chart
    default true) - now explicitly false, along with mysql/elasticsearch/
    prometheus/grafana (none of which we want).

Password wiring: existingSecret: temporal-db-role + secretKey: password,
pointing at the CNPG-generated Secret - avoids storing the DB password as
plaintext in this values file. Added a new temporal-db-secret-sync
Application (sync-wave 7, one before temporal's wave 8) with a PreSync hook
Job that copies that Secret from the ddb namespace into temporal (Secrets
are namespace-scoped; CNPG creates it in ddb, but Temporal's pods run in
temporal). Deliberately a standalone directory/Application rather than
folded into temporal/'s own kustomization.yaml, which has a The Temporal CLI manages, monitors, and debugs Temporal apps. It lets you run
a local Temporal Service, start Workflow Executions, pass messages to running
Workflows, inspect state, and more.

* Start a local development service:
      `temporal server start-dev`
* View help: pass `--help` to any command:
      `temporal activity complete --help`

Usage:
  temporal [command]

Available Commands:
  activity    Operate on Activity Executions
  batch       Manage running batch jobs
  completion  Generate the autocompletion script for the specified shell
  config      Manage config files (EXPERIMENTAL)
  env         Manage environments
  help        Help about any command
  operator    Manage Temporal deployments
  schedule    Perform operations on Schedules
  server      Run Temporal Server
  task-queue  Manage Task Queues
  worker      Read or update Worker state
  workflow    Start, list, and operate on Workflows

Flags:
      --client-connect-timeout duration
                The client connection timeout. 0s means no timeout.
                (default 0s)
      --color string
                Output coloring. Accepted values: always, never, auto.
                (default "auto")
      --command-timeout duration
                The command execution timeout. 0s means no timeout.
                (default 0s)
      --config-file $CONFIG_PATH/temporalio/temporal.toml
                File path to read TOML config from, defaults to
                $CONFIG_PATH/temporalio/temporal.toml where
                `$CONFIG_PATH` is defined as `$HOME/.config` on Unix,
                `$HOME/Library/Application Support` on macOS, and
                `%AppData%` on Windows.
      --disable-config-env
                If set, disables loading environment config from
                environment variables.
      --disable-config-file
                If set, disables loading environment config from config file.
      --env ENV
                Active environment name (ENV). (default "default")
      --env-file $HOME/.config/temporalio/temporal.yaml
                Path to environment settings file. Defaults to
                $HOME/.config/temporalio/temporal.yaml.
  -h, --help
                help for temporal
      --log-format string
                Log format. Accepted values: text, json. (default "text")
      --log-level string
                Log level. Default is "never" for most commands and
                "warn" for "server start-dev". Accepted values: debug,
                info, warn, error, never. (default "never")
      --no-json-shorthand-payloads
                Raw payload output, even if the JSON option was used.
  -o, --output string
                Non-logging data output format. Accepted values: text,
                json, jsonl, none. (default "text")
      --profile string
                Profile to use for config file.
      --time-format string
                Time format. Accepted values: relative, iso, raw.
                (default "relative")
  -v, --version
                version for temporal

Use "temporal [command] --help" for more information about a command. transformer that would silently rewrite the copy-job's ddb-scoped
RoleBinding back to temporal (same class of bug just fixed in
k8s/security/iam/kustomization.yaml).
2026-08-18 15:08:02 -07:00
Story Crater Bot 6b4f72ef1c feat(iam): automate Authentik OAuth provisioning + create admin user rock 2026-08-18 15:08:02 -07:00
Story Crater Bot d2d377a6a2 fix(monitoring,minio): prometheus CRD sync loop + stuck minio-policy-setup hook
1. prometheus CRD sync failure (OutOfSync, permanently failing):
   - helm.skipCrds: true on the prometheus Application - stop ArgoCD from
     managing these CRDs through client-side apply (kube-prometheus-stack's
     CRDs are large enough that the kubectl.kubernetes.io/last-applied-
     configuration annotation exceeds etcd's 262144-byte limit on every sync).
   - New prometheus-crds Application: plain git-sourced YAML (extracted via
     helm show crds, committed under k8s/platform/monitoring/crds/), synced
     with ServerSideApply=true. Chosen over a Helm-sourced 'CRDs only' app
     because there's no clean way to ask ArgoCD's Helm source for 'render only
     the crds/ directory' - a committed plain-YAML source is unambiguous.
   - ServerSideApply=true can't go on the main prometheus Application: it
     conflicts with managedNamespaceMetadata's forced namespace apply
     ('--force cannot be used with --server-side'), hence the split.

2. minio-tenant stuck OutOfSync (blocked 97+ minutes):
   - minio-policy-setup PostSync hook Job was NAME:
  mc alias set - set a new alias to configuration file

USAGE:
  mc alias set ALIAS URL ACCESSKEY SECRETKEY

FLAGS:
  --path value                     bucket path lookup supported by the server. Valid options are '[auto, on, off]' (default: "auto")
  --api value                      API signature. Valid options are '[S3v4, S3v2]'
  --config-dir value, -C value     path to configuration folder (default: "/Users/rockliang/.mc") [$MC_CONFIG_DIR]
  --quiet, -q                      disable progress bar display [$MC_QUIET]
  --disable-pager, --dp            disable mc internal pager and print to raw stdout [$MC_DISABLE_PAGER]
  --no-color                       disable color theme [$MC_NO_COLOR]
  --json                           enable JSON lines formatted output [$MC_JSON]
  --debug                          enable debug output [$MC_DEBUG]
  --resolve value                  resolves HOST[:PORT] to an IP address. Example: minio.local:9000=10.10.75.1 [$MC_RESOLVE]
  --insecure                       disable SSL certificate verification [$MC_INSECURE]
  --limit-upload value             limits uploads to a maximum rate in KiB/s, MiB/s, GiB/s. (default: unlimited) [$MC_LIMIT_UPLOAD]
  --limit-download value           limits downloads to a maximum rate in KiB/s, MiB/s, GiB/s. (default: unlimited) [$MC_LIMIT_DOWNLOAD]
  --custom-header value, -H value  add custom HTTP header to the request. 'key:value' format.
  --help, -h                       show help

EXAMPLES:
  1. Add MinIO service under "myminio" alias. For security reasons turn off bash history momentarily.
     $ set +o history
     $ mc alias set myminio http://localhost:9000 minio minio123
     $ set -o history
  2. Add MinIO service under "myminio" alias, to use dns style bucket lookup. For security reasons
     turn off bash history momentarily.
     $ set +o history
     $ mc alias set myminio http://localhost:9000 minio minio123 --api "s3v4" --path "off"
     $ set -o history
  3. Add Amazon S3 storage service under "mys3" alias. For security reasons turn off bash history momentarily.
     $ set +o history
     $ mc alias set mys3 https://s3.amazonaws.com \
                 BKIKJAA5BMMU2RHO6IBB V8f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12
     $ set -o history
  4. Add Amazon S3 storage service under "mys3" alias, prompting for keys.
     $ mc alias set mys3 https://s3.amazonaws.com --api "s3v4" --path "off"
     Enter Access Key: BKIKJAA5BMMU2RHO6IBB
     Enter Secret Key: V8f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12
  5. Add Amazon S3 storage service under "mys3" alias using piped keys.
     $ set +o history
     $ echo -e "BKIKJAA5BMMU2RHO6IBB\nV8f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12" | \
                 mc alias set mys3 https://s3.amazonaws.com --api "s3v4" --path "off"
     $ set -o history against
     http://minio.storage.svc.cluster.local:9000 - stale port. The minio
     Service's port now tracks requestAutoCert on the Tenant (443 when
     auto-TLS is on, 80 when off - we set it to false earlier), so 9000
     doesn't exist on that Service anymore and the job hung in its 'waiting
     for minio...' retry loop indefinitely, blocking ArgoCD's sync operation
     (PostSync hooks block the sync from completing until they succeed).
   - Fixed to use minio-cluster-hl.storage.svc.cluster.local:9000 - the
     headless per-pod Service, which always listens on 9000 regardless of
     the Tenant's TLS mode, so this can't silently break again the same way.
2026-08-18 15:08:02 -07:00
Story Crater Bot 8bca21e8ab fix(ingress-nginx): force-ssl-redirect=true globally
Our Ingress objects intentionally omit per-rule tls: blocks (single wildcard
cert served via --default-ssl-certificate). nginx-ingress's normal ssl-redirect
setting only forces HTTPS 301 for hosts with an explicit TLS block on their own
Ingress resource - since none of ours do, plain http://*.riotpiao.com requests
were served directly instead of redirected, exposing every client-facing
console (forgejo, authentik, argocd, grafana, vault, etc.) over plaintext HTTP.

force-ssl-redirect forces the redirect globally regardless of per-ingress TLS
block presence. Verified fix works (tested via manual patch then reverted -
confirmed 308 redirects to https:// on forgejo/authentik/argocd) before
committing via GitOps.
2026-08-18 15:08:02 -07:00
Story Crater Bot d0c150eab9 fix(argocd): repoURL http://forgejo.riotpiao.com:3000 -> https://forgejo.riotpiao.com
Root cause of widespread 'Unknown' sync status / Skipping auto-sync across
almost every Application: CoreDNS rewrites forgejo.riotpiao.com to the nginx
ingress controller service (rewrite name forgejo.riotpiao.com -> ingress-nginx-
controller...), which only listens on 80/443, not 3000. Every git fetch from
argocd-repo-server to the :3000 repoURL was timing out (context deadline
exceeded), so ArgoCD couldn't compare desired vs live state for any app.

Fix: use https://forgejo.riotpiao.com (no port, TLS via nginx + wildcard cert)
consistent with the 'all external endpoints HTTPS' requirement. Verified git
smart-http response 200 on the new URL before committing.
2026-08-18 15:08:02 -07:00
Story Crater Bot 06164288bb fix(ingress): correct broken/mismatched backends found in full audit
- minio console ingress: minio-console -> minio-cluster-console:9090 (service renamed by operator)
- minio-api ingress: point to minio:9000 (restored once requestAutoCert disabled)
- minio tenant: requestAutoCert: false (MinIO was TLS-only internally, breaking
  plain-HTTP clients like Vault's S3 backend - this was the real cause of the
  Vault S3 hang)
- argocd ingress: moved from namespace cicd -> argocd (service lives in argocd
  namespace; ingress in wrong namespace can never route, was returning 503)
- removed duplicate kmsvc ingress (sqs namespace already has management-service
  ingress with proper TLS block for same host/backend)

Audit method: cross-checked every ingress backend.service.{name,port} against
actual Service objects in cluster. Found 3 broken backends out of 15 ingresses.
2026-08-18 15:08:02 -07:00
Story Crater Bot 5dd38d37d4 fix(vault): correct MinIO S3 backend endpoint/timeout/api_addr — converges to minio-cluster-hl:9000 2026-08-18 15:08:02 -07:00
Story Crater Bot 1ee24e947b feat(temporal): drop Cassandra/ES, migrate persistence to CNPG PostgreSQL 2026-08-18 15:08:02 -07:00
Story Crater Bot 24dda9d8f8 fix(minio): disable standalone console (use tenant built-in console instead) 2026-08-18 15:08:02 -07:00
Story Crater Bot 92891d0f9c feat(temporal): Cassandra/ES persistence bring-up attempt (RBAC + config iterate) 2026-08-18 15:08:02 -07:00