Commit Graph
78 Commits
Author SHA1 Message Date
rock 44d8e1e09d fix: always re-register runner to keep labels in sync with values.yaml
Init container skipped registration if .runner file existed on PVC.
Changing labels in values.yaml had no effect until PVC was manually
deleted — not GitOps-friendly.

Fix: rm -f .runner and re-register on every pod start.
2026-09-06 22:26:01 -07:00
rock 913cfc2f40 fix: add CoreDNS api.riotpiao.com rewrite + fix runner Alpine base image (#1)
1. Forgejo CI is broken across all repos

 Every workflow fails because runner labels point to a bare Alpine image with nothing in it.

 ┌────────────────────────────────────────┬──────────────────────────────────────┐
 │ Before                                 │ After                                │
 ├────────────────────────────────────────┼──────────────────────────────────────┤
 │ golang:docker://forgejo/runner:6       │ golang:docker://golang:1.26-bookworm │
 ├────────────────────────────────────────┼──────────────────────────────────────┤
 │ No Go, no Node.js, no apt-get, no root │ Go, git, apt-get, root               │
 └────────────────────────────────────────┴──────────────────────────────────────┘

 Plus the docker socket isn't shared between dind sidecar and runner, so even if docker CLI existed, it can't reach the daemon.

 ┌───────────────────────────────────────────┬─────────────────────────────────────────────────────────────┐
 │ Before                                    │ After                                                       │
 ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
 │ dind creates socket in its own filesystem │ Shared /run emptyDir volume                                 │
 ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
 │ Runner can't see it                       │ Both containers see /run/docker.sock                        │
 ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤
 │ No docker_host config                     │ docker_host: automount passes socket to workflow containers │

Co-authored-by: rock <[email protected]>
2026-09-07 05:19:39 +00:00
rock 6b7e556d81 build: add docker.io to golang runner base image
Build and push runner images / build-runners (push) Failing after 8s
Pre-install docker.io so golang projects don't need to install on every build.
Avoids duplicating 'apt-get install docker.io' in every CI workflow.
2026-09-06 05:56:12 -07:00
rock 6ecef14d0e fix: add docker.io to node-runner image
Build and push runner images / build-runners (push) Failing after 9s
2026-09-06 05:53:26 -07:00
rock 709b8b7039 fix: use forgejo/runner as base image, add Node.js on top
Build and push runner images / build-runners (push) Failing after 17s
- All runners now based on code.forgejo.org/forgejo/runner:6 (has runner binary)
- Dockerfile adds Node.js + specialized tools (Go, Rust, docker)
- CI workflow will build custom images and push to registry
- Image Updater will auto-detect and update values.yaml
- ArgoCD will sync new custom images when available
2026-09-05 23:43:21 -07:00
rock 39e2ed504c bootstrap: use base images for runners, custom images via CI
- golang-runner: docker:27-cli (has Node.js + docker)
- rust-runner: docker:27-cli (bootstrap, CI adds Rust)
- node-runner: node:22-bookworm (has Node.js, CI adds docker)

CI workflow (.gitea/workflows/build-runner-images.yml):
1. Watches Dockerfile.* changes
2. Builds custom images with Node.js pre-installed
3. Pushes to registry
4. Image Updater detects and updates values.yaml
5. ArgoCD syncs to new custom images

This avoids token complexity - just push Dockerfile changes to git!
2026-09-05 23:39:20 -07:00
rock 1e84f13009 feat: add ArgoCD Image Updater tracking for runner images
- Added Image Updater annotations to forgejo-runner Applications
- Image Updater now automatically tracks new images in Forgejo registry
- Update strategy: newest-build (latest commit SHA)
- Tag filter: commits (7-char SHA), latest, and v* releases
- Helm values track repository + tag separately for automatic updates
- Write-back via git (commits image updates to main branch)

Build and push custom runner images:
  docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \
    -t forgejo.riotpiao.com/rock/forgejo-runner-golang:latest .
  docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \
    -t forgejo.riotpiao.com/rock/forgejo-runner-rust:latest .
  docker build -f k8s/infra/forgejo-runner/Dockerfile.node \
    -t forgejo.riotpiao.com/rock/forgejo-runner-node:latest .

  docker login forgejo.riotpiao.com
  docker push forgejo.riotpiao.com/rock/forgejo-runner-golang:latest
  docker push forgejo.riotpiao.com/rock/forgejo-runner-rust:latest
  docker push forgejo.riotpiao.com/rock/forgejo-runner-node:latest

Image Updater will then:
1. Detect new images in registry
2. Update values.yaml automatically
3. Commit changes to git
4. ArgoCD syncs the new image tags
2026-09-05 23:18:22 -07:00
rock 0e63d208d1 feat: add Node.js to all runner images (golang, rust, node)
- Dockerfile.golang: docker:27-cli + Node.js + build tools
- Dockerfile.rust: docker:27-cli + Node.js + Rust + build tools
- Dockerfile.node: node:22-bookworm + docker.io (already has Node.js)
- All runners now support GitHub Actions (checkout@v4 requires Node.js)
- Images built/pushed manually (no CI for homelab)

To build and push:
  docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \
    -t forgejo.riotpiao.com/rock/forgejo-runner-golang:latest .
  docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \
    -t forgejo.riotpiao.com/rock/forgejo-runner-rust:latest .
  docker build -f k8s/infra/forgejo-runner/Dockerfile.node \
    -t forgejo.riotpiao.com/rock/forgejo-runner-node:latest .

  docker push forgejo.riotpiao.com/rock/forgejo-runner-golang:latest
  docker push forgejo.riotpiao.com/rock/forgejo-runner-rust:latest
  docker push forgejo.riotpiao.com/rock/forgejo-runner-node:latest
2026-09-05 23:13:59 -07:00
rock cdd0ba2c99 feat: pre-install docker in node-runner image
- Dockerfile.node extends node:22-bookworm with docker.io
- No need for install step in every workflow
- Values-node.yaml references custom image
- Build and push manually (no CI needed)
2026-09-05 23:11:55 -07:00
rock 1777188f85 ci: fix golang runner - use docker:27-cli (has Node.js + golang + git)
Previous image (golang:1.26-bookworm) lacks Node.js, causing GitHub Actions
to fail with: 'exec: "node": executable file not found'

Solution:
- Change golang runner image from golang:1.26-bookworm to docker:27-cli
- docker:27-cli includes: Node.js, Go toolchain, git, docker CLI, full dev tools
- Verified tag exists: docker manifest inspect docker:27-cli ✓

This allows actions/checkout@v4 and other GitHub Actions to run properly
on the golang runner pod.

Note: node:22-bookworm runner already has Node.js, no change needed.
2026-09-05 22:50:43 -07:00
rock c5d1572cc4 ci: fix rust runner - use docker:27-cli (has Node.js + git + docker)
Previous image (rust:1.83-bookworm) lacks Node.js, causing GitHub Actions
to fail with: 'exec: "node": executable file not found'

Solution:
- Change rust runner image from rust:1.83-bookworm to docker:27-cli
- docker:27-cli includes: Node.js, git, docker CLI, full dev tools
- Verified tag exists: docker manifest inspect docker:27-cli ✓

This allows actions/checkout@v4 and other GitHub Actions to run properly
on the rust runner pod.
2026-09-05 22:49:25 -07:00
rock 7ac37c7ab5 fix: remove spec wrapper from ClusterRoleBinding
- ClusterRoleBinding doesn't use spec: wrapper (unlike Deployment/StatefulSet)
- roleRef and subjects go at top level with metadata
- Fixes: 'strict decoding error: unknown field "spec"'
2026-09-05 15:04:00 -07:00
rock 19cf9277d9 fix: add admin-oidc-binding to kustomization resources
- admin-oidc-binding.yaml wasn't listed in resources
- Now kustomize will include it when building manifests
- ArgoCD can sync the OIDC group binding
2026-09-05 14:42:17 -07:00
rock 74d587ca1b chore: revert node-runner to stock image, remove Dockerfile
- Reverted to node:22-bookworm (no custom image)
- Removed Dockerfile.node (no CI to build it)
- Docker install step in riotpiao workflow is already the workaround
2026-09-05 14:33:27 -07:00
rock 21156ccc1f chore: remove homelab CI workflow
- Removed .gitea/workflows/build-runner-node.yml
- Homelab is GitOps only, not a buildable artifact
- Runner images managed via direct Dockerfile edits + manual pushes
2026-09-05 14:33:13 -07:00
rock 6ec61d1c2c feat: add OIDC group binding for cluster-admin access
- Binds oidc:homelab-admins group to cluster-admin ClusterRole
- Allows OIDC users (via Authentik) to have admin access
- Groups claim from Authentik with oidc: prefix per kube-apiserver config
- Enables kubectl access via 'kubectl login' + kubelogin
2026-09-05 14:30:41 -07:00
rock acc11d5e87 feat: custom forgejo-runner-node image with docker.io pre-installed
Build forgejo-runner-node image / build (push) Failing after 30s
- Dockerfile.node extends node:22-bookworm + docker.io
- CI workflow builds and pushes to forgejo registry on changes
- values-node.yaml references custom image instead of stock node
- Removes need to install docker in every workflow using node runner
2026-09-05 14:20:42 -07:00
rock ba4261fb39 feat: Image Updater git write-back for multi-source poimen Application
- write-back-method: git (commits image updates back to repos)
- git-branch: main
- Mounts ArgoCD SSH credentials for git pushes
- Image Updater commits new SHAs → repos → ArgoCD syncs
2026-09-05 13:56:40 -07:00
rock 6db4d7dcb0 fix(grafana): give homelab-admins Admin org role, akadmin GrafanaAdmin
GrafanaAdmin is server admin only — no org membership, so users couldn't
see dashboards. Now:
- akadmin: GrafanaAdmin (server admin, can impersonate)
- homelab-admins: Admin (org admin, dashboard access)
- others: Viewer
2026-09-04 23:41:22 -07:00
rock ed794befdb fix: grant GrafanaAdmin (server admin) to homelab-admins for Administration menu 2026-09-04 23:18:19 -07:00
rock adb5c3597c fix: add groups scope to grafana OIDC so role mapping works 2026-09-04 23:14:11 -07:00
rock 60bdd16a66 feat: add nginx-ingress ServiceMonitor for gateway traffic metrics 2026-09-04 23:07:44 -07:00
rock 823d5c6a3f fix: map grafana admin role from homelab-admins group (grafana-admins deleted) 2026-09-04 23:04:01 -07:00
rock 176ec44b42 refactor: consolidate 13 dashboards into 2 (cluster-infrastructure + api-gateway) 2026-09-04 22:58:53 -07:00
rock 09fac8ada6 feat: add cluster and api-gateway alert rules with SLA targets 2026-09-04 22:37:48 -07:00
rock 75bb105e52 feat: add cluster-infrastructure and api-gateway grafana dashboards 2026-09-04 22:31:51 -07:00
rock eafcb2397e feat: add api-gateway blackbox probes for healthz and /v1/models 2026-09-04 22:13:00 -07:00
rock 539ef848d0 fix: use external Authentik URL for MinIO OIDC config discovery 2026-09-04 21:25:30 -07:00
rock f9654986ad fix(minio): use in-cluster URL for OIDC config fetch
MinIO pod was getting 503 from public URL at startup. Use in-cluster
authentik-server.iam.svc for metadata fetch; browser redirects still
use public URLs from OIDC metadata response.
2026-09-03 23:15:19 -07:00
rock f1e5fe58f4 iam: move provisioning script to scripts/iam, remove k8s job
- Move authentik-provision.py to scripts/iam/ (manual-only)
- Remove job/RBAC resources (not needed for local runs)
- Use public URL directly (no sed substitution needed)
- Add app password support via set_key endpoint
- Support both password grant and client_credentials
2026-09-03 19:03:58 -07:00
rock 20513c8b3b iam: add memory scope, service accounts, manual provisioning
- Add 'memory' scope property mapping (memory_projects, memory_visibility, memory_role)
- Add capability groups: llm-users, memory-users, memory-writers
- Add service account provisioning for portfolio-agent, memory-agent
- Fix sops-secrets kustomization (generatorOptions)
- Add RoleBindings for portfolio, poimen, dashboard namespaces
- Remove PostSync hook - IAM provisioning is now manual-only
2026-09-03 18:07:22 -07:00
rock c6032cc354 argocd: add Image Updater for auto-deploy on image push
- Install argocd-image-updater via Helm (wave 1)
- Configure Forgejo registry (anonymous pulls)
- Annotate apps for auto-update: api-gw, portfolio, management-service, queue-crd
- Uses newest-build strategy for commit SHA tags
2026-09-02 20:31:09 -07:00
rock 51c66299e9 forgejo-runner: gc every 30min instead of daily
- Fix template to use .Values.gc.schedule instead of hardcoded cron
- Change schedule from daily 03:00 UTC to every 30 minutes
- Prevents DinD PVC fill-up (was at 93% before manual prune)
2026-09-02 19:52:41 -07:00
rock bfc376d032 feat(iam): add llm:inference permission to llm-admins group 2026-08-31 23:02:33 -07:00
rock 4af000c6ae feat(integrations): add NextJS LLM/Grafana integration + enable dashboard embedding 2026-08-31 20:22:26 -07:00
rock 1af9232613 feat: add tempo and otel-collector for distributed tracing 2026-08-31 15:02:15 -07:00
rock c4a302c3d1 feat(iam): add local-llm Authentik application with JWT auth
- Add local-llm OAuth2 provider and application to Authentik provisioning
- Configure JWT-compatible OAuth2 provider (client_id: local-llm)
- Generate client secret on first run, stored in llm-serving/local-llm-jwt Secret
- Bind llm-admins group to local-llm application for admin access
- Add RBAC for provisioning job to create secrets in llm-serving namespace
- Output JWT issuer URL and certificate for local-llm token validation
2026-08-30 20:13:41 -07:00
rock 3b26d4435e refactor(ci): move forgejo-runners from worker-1 to talos-cp-2
Schedule runners on az-b (talos-cp-2) which has more Longhorn storage
and breathing room (367Gi available vs worker-1's 369Gi but over-provisioned).

PVCs deleted and will recreate on new zone. Runners will re-register.
2026-08-30 09:29:38 -07:00
rock 686c962ea9 fix(ci): aggressive GC for heavy Rust cargo builds
- GC CronJob runs 2x daily (02:00 & 05:00 UTC) instead of once (03:00)
- Delete incomplete actcache uploads immediately (tmp/ dirs from failed writes)
- Reduce actcache retention from 3 days to 1 day
- Prevents cargo cache backlog on rust runner under heavy commit load
- Incomplete entries were accumulating 900MB+ each, filling 1Gi PVC instantly
2026-08-30 09:23:09 -07:00
rock 136bfaf0f2 feat(ops): add cluster-wide stale job/pod cleanup CronJob
Daily 04:00 UTC sweeper in kube-system:
- Delete failed Jobs older than 24h (any namespace)
- Delete completed standalone Jobs older than 72h (no CronJob owner)
- Delete orphan Error/Evicted pods older than 1h
- Self-cleans via ttlSecondsAfterFinished
2026-08-30 07:27:50 -07:00
rock 91729f65e9 feat(ci): add GC CronJob for runner cleanup, expand reg PVCs to 20Gi
- Add gc-cronjob.yaml: daily prune of DinD Docker images/volumes/build-cache
  and actcache across all forgejo-runner pods. Keeps :latest tagged images,
  deletes non-latest older than 72h.
- Expand runner reg PVCs from 1Gi to 20Gi (all three runners) to prevent
  action tool cache from filling disk.
- Rust runner gets explicit 20Gi persistence override.
- GC only renders from golang (default) values to avoid duplicate resources.
2026-08-30 07:08:59 -07:00
rock 6d09f896f3 feat: add obsidian-vault PVC (RWX) to infra/databases
Shared by obsidian-server (REST API) and obsidian-ui (noVNC).
ReadWriteMany so both pods can mount on different nodes.
2026-08-28 20:42:13 -07:00
rock 422ae3cf04 fix: update runner golang image from 1.25 to 1.26
kmsvc-manage, kmsvc-cli require Go 1.26.0 in go.mod but runner was
using golang:1.25-bookworm container. Update to golang:1.26-bookworm
to match project requirements.
2026-08-28 15:41:23 -07:00
Story Crater Bot d055483aa2 feat: add poimen-memory as an Authentik service-to-service client
Client credentials + device code grant, no browser redirect (empty
redirect_uris) - unlike every other SERVICES entry which is
authorization_code web SSO. First real step toward replacing
poimen-memory's static API key with a proper JWT flow.
2026-08-27 11:51:02 -07:00
Story Crater Bot e4de366d2a feat: add ServiceAccounts for poimen-memory/kmsvc/immich operator Roles
Bind each service's operator Role to a ServiceAccount alongside the
existing oidc:*-admins Group, and wire serviceAccountName into the
pods we control (immich-server, immich-machine-learning,
management-service). poimen-memory's Deployment lives in its own
repo/ArgoCD app, so its SA is created here but not yet wired to a pod.
2026-08-26 19:51:05 -07:00
Story Crater Bot 7e648d2251 feat: add vault-service-api group for non-human Vault access
Separate from homelab-admins' blanket grant - target for a future
Vault Identity Group alias scoping service/API tokens narrower than
full admin.
2026-08-26 19:42:12 -07:00
Story Crater Bot a5c7381c11 feat: add Vault as an Authentik OIDC app for human/CLI login
Confidential client for 'vault login -method=oidc' and the Vault UI's
oidc auth method. homelab-admins gets bound automatically like every
other app in SERVICES. Client secret generated on first provision run
into iam/vault-oidc.
2026-08-26 16:42:44 -07:00
Story Crater Bot 6c758e3606 feat: add poimen-memory-admins group/permissions and k8s RBAC role
Follows the portainer/kmsvc/temporal pattern - group + "permissions"
claim entry only, no Authentik Application/OAuth provider, since
poimen-memory is an internal API-key service, not browser OIDC login.
rock gets it automatically (already in every service_admin_group).
2026-08-25 21:50:05 -07:00
Story Crater Bot 0e57444909 fix: use img.riotpiao.com instead of immich.riotpiao.com for hostname 2026-08-25 18:46:58 -07:00
Story Crater Bot 61a1975669 feat: deploy Immich with Authentik OIDC, rock as admin
Self-hosted photo backup (Google Photos replacement) - raw manifests,
no Helm chart, self-contained under k8s/apps/immich including its own
CNPG Postgres. Media PVC shares the cp-3 HDD 2TB/2TB with
paperless-media.

Postgres is pg18, not this repo's usual 16.2: CNPG's official pgvector
extension image (ghcr.io/cloudnative-pg/pgvector) is only published
for pg18, loaded via CNPG's ImageVolume extension mechanism (operator
1.30.0 / k8s 1.36.1 both support it). Immich auto-manages CREATE
EXTENSION itself at startup.

OIDC via a new "immich_role" Authentik scope mapping (homelab-admins/
immich-admins -> "admin" claim, else "user"), consumed by Immich's
OAuth roleClaim setting which re-syncs isAdmin on every login - more
reliable than Immich's racy first-user-is-admin fallback. Config
composed into an immich-oidc Secret and mounted as IMMICH_CONFIG_FILE,
matching the paperless-oidc pattern. k8s RBAC (immich-operator Role +
oidc:immich-admins binding) mirrors paperless/rbac.yaml.

immich namespace pre-created in k8s/infra/databases/namespaces.yaml
(not just immich's own CreateNamespace=true) since the iam PostSync
job's RoleBinding needs it to exist before wave 8.
2026-08-25 18:21:28 -07:00