Commit Graph
2432 Commits
Author SHA1 Message Date
claude-botandClaude Opus 4.8 80098afd8d chore(woodpecker): drop the transitional commonAnnotations prune-guard (stage 3)
ci/woodpecker/pr/pr-review Pipeline was successful
Final stage of the seed-woodpecker -> dedicated woodpecker KS move. The
commonAnnotations prune:disabled guard on apps/woodpecker/ has done its job: the
ownership handoff completed (verified — wp-pg + all app objects adopted by the
woodpecker KS, RESTARTS=0, nothing pruned), so the app objects no longer need
blanket prune protection and should be normally prunable by their owning KS again.

Removes only the commonAnnotations block. The 4 CNPG DB objects keep their
per-file prune: disabled (permanent data-guard on Cluster/wp-pg etc.) — verified
kubectl kustomize apps/woodpecker now annotates exactly those 4, not the 13 app
objects.

Rolls woodpecker-server + agent once more as the annotation leaves their pod
templates — the same accepted CI restart as stage 1.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:07:37 +02:00
k.renner 44134dc5d2 Merge pull request 'refactor(woodpecker): dedicated woodpecker KS, retire seed-woodpecker (stage 2)' (#396) from woodpecker-dedicated-ks into master
Reviewed-on: k.renner/homelab#396
2026-07-25 05:04:43 +00:00
claude-botandClaude Opus 4.8 6900271f86 refactor(woodpecker): dedicated woodpecker KS, retire seed-woodpecker (stage 2)
ci/woodpecker/pr/pr-review Pipeline was successful
Restructures woodpecker into its own Flux Kustomization instead of splitting its
app across the catch-all apps KS and its DB across seed-woodpecker.

- Add clusters/homelab/woodpecker.yaml: dedicated KS over apps/woodpecker with
  the infrastructure-cnpg-barman dep + CNPG Cluster healthCheckExprs (moved off
  seed-woodpecker) so the CNPG concerns live here, not in the shared apps KS.
- Remove woodpecker/ from apps/kustomization.yaml (catch-all apps KS no longer
  owns the woodpecker app objects).
- git mv seed/woodpecker/{cluster,objectstore,scheduled-backup,
  hetzner-creds-externalsecret}.yaml + MIGRATION.md -> apps/woodpecker/, and add
  the four DB manifests to apps/woodpecker/kustomization.yaml.
- Delete clusters/homelab/seed-woodpecker.yaml and the now-empty
  kubernetes/seed/woodpecker/.
- bootstrap.sh: add a wait_kustomization woodpecker entry (it used to be covered
  by 'wait apps'); e2e-test.sh: add woodpecker to the KS readiness checks.

Safe dual ownership transfer (nothing pruned): every object carries prune:
disabled from stage 1 (#394) — the app objects via commonAnnotations, the DB
objects per-file — so when the apps KS drops woodpecker/ and seed-woodpecker is
deleted, both GC sweeps skip these live objects and the new woodpecker KS adopts
them in place. No CNPG restart (Cluster is not a Deployment); no app restart
(manifests unchanged, only the owning KS label flips).

Verified: kubectl kustomize apps/woodpecker builds 17 objects (13 app + 4 DB
incl. wp-pg Cluster), all prune-protected; bash -n bootstrap.sh + e2e-test.sh
OK; wp-pg is the sole CNPG cluster in apps ns and hetzner-s3-creds is used only
by wp-pg-store; seed/woodpecker fully removed.

Stage 3 will drop the transient commonAnnotations (one more accepted app roll).

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 06:18:54 +02:00
k.renner 5d3f94fd8b Merge pull request 'chore(woodpecker): pre-stage prune:disabled on the wp-pg CNPG objects (stage 1)' (#394) from woodpecker-prune-protect into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#394
2026-07-25 04:12:21 +00:00
claude-botandClaude Opus 4.8 27d338f9be chore(woodpecker): also prune-guard the app objects via commonAnnotations
ci/woodpecker/pr/pr-review Pipeline was successful
Folds the app-object protection into stage 1 now that a woodpecker CI restart is
acceptable. commonAnnotations adds kustomize.toolkit.fluxcd.io/prune: disabled to
every object under apps/woodpecker/ (the woodpecker-ci Namespace, server/agent
Deployments, Service, Ingress, PDB, NetworkPolicy, ResourceQuota/LimitRange,
RBAC, ExternalSecret), so when 'woodpecker/' is later removed from
apps/kustomization.yaml the catch-all apps KS can't prune them mid-handoff.

Trade-off accepted: kustomize propagates commonAnnotations into the two
Deployments' pod templates, so this rolls woodpecker-server + agent once on
apply (and once more when removed in the final stage). Confirmed harmless — the
DB (wp-pg) is a separate CNPG resource, untouched.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 05:59:37 +02:00
claude-botandClaude Opus 4.8 a7b4e2b996 chore(woodpecker): pre-stage prune:disabled on the wp-pg CNPG objects
ci/woodpecker/pr/pr-review Pipeline is pending
Stage 1 of moving woodpecker off seed-woodpecker into a dedicated woodpecker KS.
Adds kustomize.toolkit.fluxcd.io/prune: disabled, in place, to the four
data-bearing DB objects in seed/woodpecker/ so the later ownership handoff can
never prune them:

- Cluster/wp-pg           (pruning deletes the DB's data PVCs)
- ObjectStore/wp-pg-store (barman backup target)
- ScheduledBackup/wp-pg-daily
- ExternalSecret/hetzner-s3-creds (barman S3 creds)

Landing the annotation via the existing seed-woodpecker KS guarantees it is live
on the objects before any move. Metadata-only; no spec change, so CNPG/the
running wp-pg pod are untouched. The Cluster keeps the annotation permanently as
a data-guard; the others are incidental to the same handoff.

The woodpecker *app* objects (server/agent/Namespace/etc., currently under the
catch-all apps KS) are protected separately in the next stage — they need
per-object top-level annotations rather than commonAnnotations, which would
propagate into the Deployments' pod templates and needlessly restart live CI.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 05:55:36 +02:00
security-bot 0802b42146 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784949660
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-25 03:33:14 +00:00
k.renner 7430b8ea90 Merge pull request 'refactor(influxdb): move seed-influxdb to apps/influxdb (step 2/2)' (#390) from move-influxdb-step2 into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#390
2026-07-25 03:16:31 +00:00
k.renner 9a4e8b0a1d Merge pull request 'fix(shelly-manager): set ShellyManager__DnsSuffix=iot.renner.uno' (#391) from fix/389-shelly-manager-dnssuffix into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#391
2026-07-25 03:11:51 +00:00
security-bot cd5cd05741 chore(image-automation): bump redis:8.8.1-alpine
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 18:27:08 +00:00
claude-botandClaude Opus 4.8 ffc3b2d468 docs(influxdb): purge remaining stale seed-Job references in helmrelease/ha-token-job
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the follow-up review on #390: three more comments still described the
removed influxdb-seed Job, some in the present tense (contradicting the note the
prior commit added).

- helmrelease.yaml adminUser comment: drop the 'sibling influxdb-seed Job reads
  the secret / deletes the bucket' description; state the admin token comes from
  the influxdb-bootstrap Secret (OpenBao via ESO).
- helmrelease.yaml resource/timeout rationale: reword 'after a partial seed' and
  'batches from the seed Job' to the seed-independent conditions (large dataset /
  large write batches) that still justify the 6Gi limit + 60s write timeout.
- ha-token-job.yaml: drop 'Same wait pattern as the seed Job's wait-for-influxd';
  describe the two-stage wait directly.

Verified: kubectl kustomize apps/influxdb builds; the only remaining 'seed'
strings are accurate migration-rationale comments and the OpenBao KV path.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 15:06:31 +02:00
security-bot 153ae5d219 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784897722
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 13:03:13 +00:00
claude-botandClaude Fable 5 4956f47354 fix(shelly-manager): set ShellyManager__DnsSuffix=iot.renner.uno
ci/woodpecker/pr/pr-review Pipeline was successful
Without the suffix the app resolves devices only via its 2-minute mDNS
cache; the authoritative-DNS leg of MdnsConnectCallback is disabled and
the presence sweep falsely marks online always-on devices missing
(5 devices confirmed online but flagged, 2026-07-24).

Fixes #389

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:01:25 +02:00
claude-botandClaude Opus 4.8 9f49b705ed docs(influxdb): note why the cnpg-barman dep was dropped
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the doc-only suggestion on #390: explain in the new influxdb KS that
dropping seed-influxdb's infrastructure-cnpg-barman dependency is intentional
(copy-paste boilerplate; InfluxDB has no CNPG cluster, only a ceph-block PVC).

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 15:00:37 +02:00
claude-botandClaude Opus 4.8 2c8a91162c refactor(influxdb): move seed-influxdb to apps/influxdb (step 2/2)
ci/woodpecker/pr/pr-review Pipeline was successful
Step 2 of the InfluxDB seed->apps handoff. #388 already landed
prune: disabled on the live HelmRelease/HelmRepository/ExternalSecret, so
retiring seed-influxdb here cannot trigger a helm uninstall — GC skips the
annotated objects and the new influxdb KS adopts them in place.

- git mv kubernetes/seed/influxdb/ -> kubernetes/apps/influxdb/ (annotations
  from #388 travel with the files).
- Add kubernetes/clusters/homelab/influxdb.yaml (KS 'influxdb', path
  apps/influxdb, deps infrastructure/crds/ceph). Delete seed-influxdb.yaml.

Review fixes from the closed atomic PR (#387):
- bootstrap.sh: wait on 'influxdb' instead of the deleted 'seed-influxdb'
  (would otherwise spin the full 600s timeout on cold bootstrap).
- e2e-test.sh: swap the seed-influxdb readiness check for 'influxdb'.
- Fix stale comment paths kubernetes/seed/influxdb/... -> apps/influxdb/... in
  network-policies/home-assistant-ns.yaml and apps/monitoring/
  helmrelease-prometheus.yaml.
- Trim the helmrelease.yaml comment that described the removed seed-job.yaml,
  and the ha-token-job.yaml 'at cutover moves to apps/influxdb' note (this is
  that cutover).

Left intentionally: externalsecret.yaml key 'seed/influxdb/bootstrap' is an
OpenBao KV path, not a repo path — moving it needs a vault-side secret
migration, out of scope here.

Verified: kubectl kustomize apps/influxdb builds all objects with the right
prune/force annotations; bash -n passes on bootstrap.sh + e2e-test.sh; no
remaining seed-influxdb references in the repo (only the OpenBao KV path).

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 14:56:11 +02:00
k.renner 37920ff0be Merge pull request 'chore(influxdb): pre-stage prune:disabled on live seed-influxdb objects (step 1/2)' (#388) from annotate-influxdb-prune-disabled into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#388
2026-07-24 12:51:08 +00:00
claude-botandClaude Opus 4.8 ed17ac78d9 chore(influxdb): pre-stage prune:disabled on live seed-influxdb objects
ci/woodpecker/pr/pr-review Pipeline was successful
Step 1 of a two-step seed->apps handoff for InfluxDB (the move itself follows
in a separate PR). Adds kustomize.toolkit.fluxcd.io/prune: disabled to the
HelmRelease, HelmRepository, and ExternalSecret *in place* under
kubernetes/seed/influxdb/, with no move and no KS changes.

Why split: doing the annotate + move + delete-old-KS in one commit races the
new KS's first reconcile (which stamps the annotation onto the live objects)
against the old KS's deletion-GC. If GC wins, it prunes the un-annotated
HelmRelease -> helm uninstall -> the running InfluxDB StatefulSet is torn down.
Landing the annotation via the existing seed-influxdb KS first guarantees it is
live on the objects before any prune can run, per Flux's documented pattern.

No behavioural change: only a kustomize metadata annotation is added, so
helm-controller performs no upgrade and the server is untouched. After merge,
verify the annotation is live on all three objects, then land the move PR.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 14:05:01 +02:00
k.renner 7f48bfd68e Merge pull request 'refactor(audiobookshelf): fold seed KS into the app KS' (#386) from move-audiobookshelf-to-apps into master
ci/woodpecker/cron/e2e Pipeline was successful
ci/woodpecker/cron/security-scan Pipeline was successful
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#386
2026-07-24 11:47:57 +00:00
claude-botandClaude Opus 4.8 978cc704a0 refactor(audiobookshelf): fold seed KS into the app KS
ci/woodpecker/pr/pr-review Pipeline was successful
Second step of dissolving the transitional 'seed' layer (after
music-assistant). Move the audiobookshelf config/metadata/books PVCs from
seed-audiobookshelf into the app's own Kustomization and retire the seed KS.

- git mv kubernetes/seed/audiobookshelf/pvcs.yaml -> kubernetes/apps/audiobookshelf/
  and add it to the app kustomization.
- Drop 'dependsOn: seed-audiobookshelf' from the audiobookshelf KS (it now owns
  the 3 PVCs directly; ceph dep already present, no CNPG involved).
- Delete kubernetes/clusters/homelab/seed-audiobookshelf.yaml and the now-empty
  kubernetes/seed/audiobookshelf/.
- Remove seed-audiobookshelf from the e2e-test.sh Kustomization readiness checks
  (it no longer exists post-merge) — same fix the review flagged on the
  music-assistant PR, done proactively here.

Safe ownership transfer: all 3 PVCs carry kustomize.toolkit.fluxcd.io/prune:
disabled, so Flux GC skips them when seed-audiobookshelf is pruned; the
audiobookshelf KS adopts them on reconcile. Verified: PVCs Bound + prune-disabled
live; kubectl kustomize apps/audiobookshelf builds all 3.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:43:36 +02:00
k.renner 80666a8323 Merge pull request 'refactor(music-assistant): fold seed KS into the app KS (seed->apps pilot)' (#385) from move-music-assistant-to-apps into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#385
2026-07-24 11:40:27 +00:00
claude-botandClaude Opus 4.8 9b3cb98bc6 fix(e2e): drop retired seed-music-assistant from Kustomization checks
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses review on #385.

Blocking: e2e-test.sh asserted kustomization/seed-music-assistant Ready, but
this PR deletes that KS — the test would fail post-merge with 'not found'.
Remove it from the checked list (line 110).

Also add a clarifying note in MIGRATION-sops-openbao.md that
seed-music-assistant has since been retired (the 2026-07-14 log line is left
factually intact as a historical record).

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:26:12 +02:00
claude-botandClaude Opus 4.8 efa5d6833c refactor(music-assistant): fold seed KS into the app KS
ci/woodpecker/pr/pr-review Pipeline was successful
First step of dissolving the transitional 'seed' layer: move the
music-assistant-data PVC from seed-music-assistant into the app's own
Kustomization and retire the seed KS.

- git mv kubernetes/seed/music-assistant/pvcs.yaml -> kubernetes/apps/music-assistant/
  and add it to the app kustomization.
- Drop 'dependsOn: seed-music-assistant' from the music-assistant KS (it now
  owns the PVC directly; ceph dep already present, no CNPG involved).
- Delete kubernetes/clusters/homelab/seed-music-assistant.yaml and the now-empty
  kubernetes/seed/music-assistant/.

Safe ownership transfer: the PVC already carries
kustomize.toolkit.fluxcd.io/prune: disabled, so Flux GC skips it even when the
seed-music-assistant KS is pruned — the object is never torn down, and the
music-assistant KS adopts it on next reconcile. Verified: PVC is Bound and
prune-disabled live; kubectl kustomize apps/music-assistant builds with the PVC.

Pilot for the per-app seed->apps relocation; remaining apps (audiobookshelf,
influxdb, woodpecker, paperless, immich, authentik) follow the same pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:20:14 +02:00
k.renner 5313806a44 Merge pull request 'chore(flux): retire deprecated monolithic seed Kustomization' (#384) from retire-seed-monolith into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#384
2026-07-24 11:19:53 +00:00
claude-botandClaude Opus 4.8 e606a15d96 chore(flux): retire deprecated monolithic seed Kustomization
ci/woodpecker/pr/pr-review Pipeline was successful
The monolithic `Kustomization/seed` was split into per-app `seed-<app>`
KSes; it was left suspended + prune-disabled only so its inventory could
be pruned by a follow-up commit. That inventory is now empty (ownership
fully migrated to the per-app KSes), so the object owns nothing and can
be removed cleanly — flux-system (prune: true) will garbage-collect the
now-absent Kustomization/seed, and its own prune: false + empty inventory
means zero live resources are torn down.

Also drop kubernetes/seed/kustomization.yaml, the aggregator that only the
monolith referenced (path ./kubernetes/seed); it was already stale (missing
woodpecker). The per-app KSes target ./kubernetes/seed/<app> subdirs and are
unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:52:21 +02:00
k.renner f933ae5a6e Merge pull request 'fix(gitea): retire deprecated [server] LFS_CONTENT_PATH; disable query auth tokens' (#383) from gitea-lfs-deprecation into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#383
2026-07-24 08:41:15 +00:00
claude-botandClaude Opus 4.8 d09688bac9 fix(gitea): retire deprecated [server] LFS_CONTENT_PATH; disable query auth tokens
ci/woodpecker/pr/pr-review Pipeline was successful
Gitea 1.21.4 logs a deprecation warning for [server] LFS_CONTENT_PATH
(superseded by [lfs] PATH) and recommends DISABLE_QUERY_AUTH_TOKEN
(default from 1.23). Both surface as startup notices on the admin
dashboard.

Add the replacements as GITEA__ env overrides so they are tracked in
git. environment-to-ini can only add keys, so the stale
[server] LFS_CONTENT_PATH was also removed from the on-PVC app.ini by
hand (same path value -> existing data/lfs objects untouched).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 10:36:59 +02:00
security-bot f50e0e06f4 chore(image-automation): bump ghcr.io/home-assistant/home-assistant:2026.7.4
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 08:27:10 +00:00
security-bot 60985c79e2 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784879999
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 08:03:12 +00:00
security-bot 9bd4c23af9 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784872746
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 06:26:12 +00:00
k.renner 6b905c6b37 Merge pull request 'chore(ci): bump ci-claude image to 2.1.218' (#382) from ci-image/claude-2.1.218 into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#382
2026-07-24 04:40:07 +00:00
security-bot 5a96a46ea4 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784866857
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-24 04:26:12 +00:00
claude-bot d4f91779a1 chore(ci): bump ci-claude image to 2.1.218
ci/woodpecker/pr/pr-review Pipeline was successful
2026-07-24 04:25:34 +00:00
k.renner 0810d4464d Merge pull request 'chore(ci): make ci-claude reproducible and keep it on latest (#351)' (#378) from claude-bot/ci-claude-reproducible into master
ci/woodpecker/push/ci-claude-refresh Pipeline was successful
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: k.renner/homelab#378
2026-07-24 04:24:50 +00:00
security-bot 46a63eaee7 chore(image-automation): bump netbirdio/netbird:v0.75.0-rootless
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-23 17:26:53 +00:00
claude-botandClaude Opus 4.8 f414f6e7bb fix(ci): only build/push ci-claude from master, never a PR branch
ci/woodpecker/pr/pr-review Pipeline was successful
The push trigger matched any branch, so pushing this feature branch ran the
refresh pipeline from the branch: it built and pushed ci-claude:2.1.218 and
opened a bump PR (#379) prematurely. Restrict the push event to branch: master
so only master ever builds/pushes images or opens bump PRs; cron and manual
are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 17:19:17 +02:00
claude-botandClaude Opus 4.8 16eecf07e8 chore(ci): provision ci-claude bump token; use claude-bot identity
ci/woodpecker/push/ci-claude-refresh Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was canceled
The bump credential is a claude-bot PAT scoped write:repository only (a
dedicated Gitea user needs admin to create, which claude-bot lacks; the
scoped-down PAT is the same least-privilege at the token level). Token minted
and stored as the ci_claude_bump_bot_token Woodpecker secret (events:
push/cron/manual). Commit author set to claude-bot accordingly. Only the cron
registration remains, to be done after this merges to master.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 17:12:47 +02:00
claude-botandClaude Opus 4.8 a675bf6cf0 chore(ci): make ci-claude reproducible and keep it on latest (#351)
ci/woodpecker/pr/pr-review Pipeline was successful
The ci-claude image installed a fixed, pinned Claude Code version and every
consumer pipeline pinned a stale dated image tag. Two problems:

- The npm install layer changed on every build (install-time mtimes + the
  npm cache), so it never deduped in the Gitea registry — the single largest
  orphan pattern behind #336 (~260 MB orphaned per no-op rebuild).
- A frozen version silently rots: old Claude Code releases eventually stop
  working against the API.

Containerfile: default to @latest (npm auto-selects the musl native binary on
this alpine base) and make the install layer byte-stable — clean the npm cache
and normalize install-time mtimes on the installed tree and the directories the
RUN touches. Two independent --no-cache builds now produce a byte-identical npm
layer, so a rebuild of the same version dedups instead of orphaning a copy.

New ci-claude-refresh.yml owns the image lifecycle: weekly (and on-demand) it
resolves the latest published version, and only when it differs from what the
consumers pin does it build ci-claude:<version> (+:latest) and open a PR
rewiring the 5 consumer pipelines. Version tags make "did it change?" a
race-free string compare and keep the cron from opening no-op PRs. Nothing
auto-merges. build-ci-images.yml no longer builds ci-claude so the two never
fight over tags.

Requires owner setup before it does anything (documented in the pipeline
header): a dedicated ci-claude-bump-bot identity (write:repository only) as the
ci_claude_bump_bot_token secret, and a cron named "ci-claude-refresh".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:58:36 +02:00
k.renner 2c5e54865a Merge pull request 'Stable predictable interface names (net.ifnames=1) — fixes the v30iot NIC shuffle' (#375) from claude-bot/talos-predictable-naming into master
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#375
2026-07-23 14:29:32 +00:00
security-bot f65f52f160 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784816363
Updates managed by image-automation-controller.
2026-07-23 14:26:48 +00:00
claude-botandClaude Fable 5 3939fe51f6 fix(ci): ash-portable schematic substring; actually finish the eth1 sweep
ci/woodpecker/pr/pr-review Pipeline was successful
Review blocker: my previous filename fix used bash substring syntax
${TALOS_SCHEMATIC:0:12}, but the apply step runs under busybox ash
(Alpine /bin/sh) which errors 'Bad substitution' — the repo already uses
cut for this exact reason. Replaced with $(echo "$TALOS_SCHEMATIC" |
cut -c1-12). The HCL substr() in talos_image.tf is unaffected (not shell).

Also finishes the comment sweep the prior commit claimed but didn't: 4
more stale 'eth1' host-NIC references in the matter/otbr/music-assistant
NAD headers are now ens19. Grep confirms zero eth1 left in the v30iot
NAD/deployment files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014S5CLfizEtzHS51kxJFQgc
2026-07-23 16:21:37 +02:00
k.renner 40a20cb3bd Merge pull request 'fix(ci): strip REVIEW_TOKEN from the review agents' environment' (#377) from claude-bot/pr-review-strip-review-token into master
Reviewed-on: k.renner/homelab#377
2026-07-23 14:18:51 +00:00
claude-botandClaude Opus 4.8 a4836879a9 fix(ci): strip REVIEW_TOKEN from the review agents' environment
ci/woodpecker/pr/pr-review Pipeline was successful
The step exports REVIEW_TOKEN (write:repository PAT) for its own curl/gitea_post
calls, but that also placed it in the environment of the claude triage+review
agents, which process attacker-controlled PR content — a prompt-injected diff
could read /proc/self/environ and exfiltrate it into the public report. The
agents never call Gitea (the shell does, via $AUTH), so run them under
'env -u REVIEW_TOKEN'. Defence in depth alongside the settings.json /proc deny.
Caught by the review bot; matches the fix applied to the other fleet repos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:12:27 +02:00
claude-botandClaude Fable 5 b762cc7941 docs(nad): finish the eth1->ens19 comment sweep in the NAD headers
ci/woodpecker/pr/pr-review Pipeline was successful
Review follow-up (optional): the shelly/matter/otbr NAD header comments
still described the old eth1 probe-order naming, contradicting this PR's
own root-cause writeup. Updated to the stable ens19 predictable name and
dropped the stale k8s-edge/probe-order narration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014S5CLfizEtzHS51kxJFQgc
2026-07-23 16:12:23 +02:00
claude-botandClaude Fable 5 7eb2e02b1d fix(ci): key Talos image cache on schematic; reconcile stale comments
ci/woodpecker/pr/pr-review Pipeline was successful
Review blocker: the Proxmox image-exists check derived TALOS_FILENAME
from version only, so bumping the schematic left the cached filename
unchanged — a future VM (re)provision would find the stale old-schematic
image and skip the download, silently reintroducing net.ifnames=0. Embed
the schematic hash in the filename (pipeline + opentofu/talos_image.tf,
kept in sync) so a schematic bump forces a fresh download.

Also reconciles stale comments the rename left behind: the MA reconcile
docstring (hostNetwork-era eth1 explanation), cluster.yaml kubelet nodeIP
(eth1->ens19), and the matter NAD's k8s-edge claim (edge isn't migrated
and isn't a current node; the pod is pinned off it anyway).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014S5CLfizEtzHS51kxJFQgc
2026-07-23 15:59:18 +02:00
k.renner 285b52eb55 Merge pull request 'feat(secret-rotator): pin image, reconcile path, unsuspend the two rotators' (#373) from claude-bot/rotator-pin-unsuspend into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#373
2026-07-23 13:54:10 +00:00
claude-botandClaude Fable 5 4ee087ec5b fix(talos): stable predictable interface names (net.ifnames=1) for v30iot
ci/woodpecker/pr/pr-review Pipeline was successful
Root cause of the IoT NIC name shuffle: the nocloud platform driver
unconditionally injects net.ifnames=0, disabling systemd predictable
naming, so eth0/1/2 followed driver-probe order and renamed across
reboots (a k8s-3 reboot put the v30iot NIC on eth2 and mgmt on eth1,
silently mis-targeting the v6-disable sysctl onto mgmt and attaching
macvlan children to the Server VLAN). LinkAliasConfig cannot fix this
(it only sets a cosmetic IFLA_IFALIAS, invisible to sysctl/CNI).

Fix: re-enable predictable naming via a new Image Factory schematic
(extraKernelArgs: -net.ifnames=0, net.ifnames=1). Because the cluster
boots sd-boot/UKI, the cmdline is baked into the image — machine.install
extraKernelArgs is silently ignored — so this MUST be delivered as a
schematic + talosctl upgrade, not a config patch. The busPath-derived
names are topology-stable and identical across the three identical VMs:
  0000:06:13.0 (v30iot) -> ens19
  0000:06:12.0 (mgmt)   -> ens18
  0000:02:00.0 (ceph)   -> enp2s0np1

Re-keys everything that referenced the volatile eth1 onto the stable
ens19: the four v30iot macvlan NADs (matter/otbr/shelly/music-assistant)
and the per-node v6-disable sysctl. Bumps the schematic id in the deploy
pipeline. Applied live via rolling talosctl upgrade (one node at a time,
full Ceph recovery between each); verified ens19 identical on all three,
v30iot NIC addressless v4+v6, macvlan children retain their own v6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014S5CLfizEtzHS51kxJFQgc
2026-07-23 15:47:42 +02:00
claude-botandClaude Opus 4.8 0b4ccd66f8 fix(secret-rotator): restore spec: key in claude-oauth CronJob
ci/woodpecker/pr/pr-review Pipeline was successful
The earlier edit that removed the stale suspend-comment block also dropped the
'spec:' line, leaving suspend/schedule/jobTemplate nested under metadata: — an
invalid CronJob that would be rejected by the API server (kustomize build does
not validate against the k8s schema, so it passed). Re-add spec: and sync the
top-of-file doc comment to the reconciled infrastructure/claude#setup-token
path. Caught by the review bot on this PR; verified with kubectl --dry-run=server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 15:25:50 +02:00
k.renner 211f1314ba Merge pull request 'Expose Zigbee2MQTT frontend via Authentik gate (zigbee2mqtt.home.renner.uno)' (#374) from claude-bot/zigbee2mqtt-ingress into master
ci/woodpecker/manual/alert-monitor Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#374
2026-07-23 13:08:45 +00:00
claude-botandClaude Opus 4.8 de402ba531 Expose Zigbee2MQTT frontend via Authentik gate
ci/woodpecker/pr/pr-review Pipeline was successful
Gate zigbee2mqtt.home.renner.uno behind the same passkey-step-up Authentik
proxy-outpost pattern used for esphome/shelly-manager/vscode. The Z2M frontend
has no auth of its own (anyone on :8080 controls the whole Zigbee network), so
Traefik only ever routes the hostname to a dedicated outpost that validates the
session before forwarding.

New:
  - apps/home-assistant/zigbee2mqtt-gate-service.yaml — ExternalName mirror to
    ak-outpost-zigbee2mqtt-gate in the authentik ns (:9000).
  - apps/home-assistant/zigbee2mqtt-ingress.yaml — zigbee2mqtt.home.renner.uno →
    the outpost; cert-manager ingress-shim issues zigbee2mqtt-tls.
  - apps/authentik/zigbee2mqtt-gate-egress.yaml — CNP letting the outpost egress
    to the zigbee2mqtt pod on :8080.

Edited:
  - apps/authentik/blueprints-configmap.yaml — 5-entry blueprint block (group /
    proxyprovider / application / policybinding / outpost) named zigbee2mqtt-gate;
    internal_host carries the explicit :8080 (Service listens on 8080, unlike
    esphome's :80). Authentik spawns the ak-outpost-zigbee2mqtt-gate Deployment
    +Service from the outpost entry.
  - infrastructure/network-policies/home-assistant-ns.yaml — fromEndpoints rule
    admitting the zigbee2mqtt-gate outpost to :8080 (shared 8080 with vscode/
    shelly-manager; the fromEndpoints selector + the authentik-side egress scope
    it to the zigbee2mqtt pod).
  - the two kustomizations registering the new files.

DNS (k8s-gateway watches Ingress) and TLS (cert-manager) are automatic. Access
limited to the "Zigbee2MQTT Managers" group (k.renner). Verified: kustomize
builds, dry-run OK, blueprint YAML parses with the new block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 15:03:37 +02:00
claude-botandClaude Opus 4.8 a9e1dfc85a feat(secret-rotator): activate review-bot + claude-oauth rotators
ci/woodpecker/pr/pr-review Pipeline was successful
Pin both CronJobs to the built secret-rotator image that carries woodpecker-secret
mode (sha256:828bf486…), flip suspend:false, and reconcile the claude-oauth
BAO path to homelab/infrastructure/claude#setup-token (where the OAuth value is
actually seeded) — with the matching policy update (applied out-of-band).

Bootstraps + policies/roles are already applied and the org secrets are live, so
the rotators now maintain them on schedule: review-bot mints+syncs review_bot_token
quarterly; claude-oauth syncs claude_code_oauth_token daily.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 15:00:35 +02:00