Commit Graph
4354 Commits
Author SHA1 Message Date
claude-botandClaude Opus 4.8 ab65898234 refactor(immich): fold seed-immich into the immich KS (stage 2)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Completes the immich seed->apps move (same single-transfer pattern as paperless
#400). Restore-test passed first (im-pg backup restorable: 33316 rows in asset).

- git mv seed/immich/{cluster,objectstore,scheduled-backup,barman-creds-
  externalsecret,hetzner-creds-externalsecret,pvcs}.yaml -> apps/immich/, add to
  apps/immich/kustomization.yaml.
- immich KS: drop 'dependsOn: seed-immich', add
  'dependsOn: infrastructure-cnpg-barman', add the CNPG Cluster healthCheckExprs.
- Delete clusters/homelab/seed-immich.yaml and the now-empty seed/immich/.
- e2e-test.sh: drop seed-immich, add immich to the app-KS readiness checks
  (immich was previously only covered via seed-immich).
- Fix the last stale seed/immich comment path in
  infrastructure/openbao/snapshot-pvc.yaml (seed/immich -> apps/immich).

Safe ownership transfer (nothing pruned): all 7 seed/immich objects carry
prune: disabled (5 CNPG from stage 1 #401, 2 PVCs pre-existing), so deleting
seed-immich skips them in GC and the immich KS adopts them in place. No CNPG
restart, no app restart.

Verified: kubectl kustomize apps/immich builds 17 objects incl. Cluster/im-pg,
exactly 7 prune-protected; bash -n e2e-test.sh OK; seed/immich fully removed.

Left intentionally: the hetzner-creds ExternalSecret still reads OpenBao key
seed/immich/... (vault path, not a repo path).

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 10:45:46 +02:00
k.renner d5c7cea403 Merge pull request 'docs(recovery): fix CNPG restore-test drill for the plugin-based barman' (#404) from fix-recovery-md-cnpg-restore-test into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#404
2026-07-25 08:42:19 +00:00
k.renner 07d9f33174 Merge pull request 'feat(ci): select agent model from the live catalog, not hardcoded ids' (#395) from claude-bot/ci-dynamic-model-selection into master
Reviewed-on: k.renner/homelab#395
2026-07-25 08:41:39 +00:00
k.renner 983ade2d1a Merge pull request 'fix(netbird-ingress): restart peers stuck in NeedsLogin, alert on NotReady' (#403) from netbird-needslogin-probe into master
ci/woodpecker/cron/issue-fix Pipeline was successful
Reviewed-on: k.renner/homelab#403
2026-07-25 08:40:04 +00:00
claude-botandClaude Opus 4.8 6274dd66fb docs(recovery): fix CNPG restore-test drill for the plugin-based barman
ci/woodpecker/pr/pr-review Pipeline was successful
The restore-test drill in RECOVERY.md told you to omit spec.plugins from the
throwaway cluster ('NO spec.plugins / isWALArchiver here'). With the plugin-based
barman this homelab now uses (barmancloud.cnpg.io ObjectStore + the
barman-cloud.cloudnative-pg.io plugin), that is invalid: the instance manager
can't load the plugin it needs to replay WAL during restore and dies at startup
with 'Unknown plugin: barman-cloud.cloudnative-pg.io' — the cluster never becomes
healthy. A real cold-rebuild DR following this drill would have failed the same
way. Found + fixed by running the drill against im-pg on 2026-07-25 (restore
succeeded once the plugin was declared; 33316 rows in 'asset', 584 MB DB).

- Declare the barman plugin in spec.plugins with isWALArchiver: false — loaded so
  it can READ WAL for the restore, but never archives (stays read-only against
  the backup path). Update the intro prose to match.
- Verification: pg_stat_user_tables n_live_tup reads 0 right after a restore
  (stats not gathered), so that view is not a data check — add pg_database_size
  and note counting a real table instead.
- Teardown: the old 'reclaim the released PV afterwards' was too vague and leaks
  the Retain PV's RBD image (issue #262). Give the explicit recipe: capture the
  PV name, delete the cluster, then patch the released PV to Delete so the CSI
  reclaims the RBD image.

The in-place cold-rebuild section (which keeps the live cluster's existing
spec.plugins isWALArchiver: true) was already correct and is unchanged.

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 09:11:51 +02:00
claude-botandClaude Opus 4.8 1992fabb12 fix(ci): make model-picker actually tool-less; gate tests on PRs
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the 14aa6415 review.

- pick-model.sh: the picker relied on `--allowedTools ""` to be tool-less, but an
  empirical check showed an empty allowlist does NOT block tools (the Write tool
  still ran) -- it is treated as "unset" and the default toolset stays live. Add
  `--permission-mode plan`, which IS verified to block Write while still producing
  the selection JSON. --allowedTools "" + the Read deny-list remain as additional
  layers. Corrected the comments in pick-model.sh and the picker settings.json
  that claimed the empty allowlist made it tool-less.
- .woodpecker/ci-scripts-test.yml: new pull_request-triggered pipeline (homelab
  only) that runs scripts/ci/test-model-selection.sh, so a PR weakening the
  injection gate fails checks immediately instead of only on the next scheduled
  e2e cron (closes the coverage-timing gap the review flagged).
- Pin a recorded real /v1/models response (scripts/ci/testdata/v1-models-sample.json)
  and assert fetch-models.sh maps it correctly (opus effort=true, haiku
  effort=false), documenting the verified response shape.

Deferred with rationale: hard-failing a pipeline when the degraded-schema warning
fires -- the warning already reaches the job log (curl/jq stderr is no longer
suppressed), and hard-failing on a benign API schema addition would break all
model selection; graceful degradation to name+date is the deliberate choice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 09:08:39 +02:00
claude-botandClaude Fable 5 c59ad31db9 fix(netbird-ingress): restart peers stuck in NeedsLogin, alert on NotReady
ci/woodpecker/pr/pr-review Pipeline was successful
A 24-min WAN outage to netbird.renner.uno on 2026-07-24 exceeded the
10-min ephemeral-peer lifetime, so management deregistered all three
routing peers. The client treats 'peer is not registered' as
unrecoverable and parks in NeedsLogin; that fails readiness but not the
'--check live' liveness probe, so the pods sat Running-but-NotReady for
~20 h until they were deleted by hand.

- liveness probe now fails on the dead auth states (NeedsLogin /
  LoginFailed / SessionExpired) and only on those: a restart re-runs the
  entrypoint, which re-enrolls with NB_SETUP_KEY. Transient
  Disconnected/Connecting states still retry silently, so a management
  outage does not kill the working WireGuard data plane.
- new PrometheusRule: warning when the DaemonSet runs degraded >10m,
  critical when 0 peers are Ready >5m.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 09:06:01 +02:00
claude-botandClaude Opus 4.8 14aa641560 fix(ci): validate pr-review model pick against pre-triage catalog snapshot
ci/woodpecker/pr/pr-review Pipeline was successful
The pr-review triage agent runs with an unscoped Write tool and its cwd contains
./.pr-review (-> $D), so a prompt-injected diff could make it overwrite
./.pr-review/models.txt and then name a smuggled token as its `model`. Validation
re-read models.txt FROM DISK after the triage ran, so the catalog-membership
check was defeated by the agent's own writeback -- letting an attacker force a
weaker real model, or a syntactically-valid fake id that makes the review's
`claude --model <fake>` fail and silently suppress the review entirely.

Validate against $MODELS_TABLE -- the catalog captured in a shell variable BEFORE
the triage runs -- instead of re-reading the file the triage can overwrite. This
mirrors pick-model.sh, which already validates against its in-memory table before
its (tool-less) picker runs. The triage still reads models.txt for its own pick;
tampering only misleads that pick, which is then rejected against the pristine
snapshot -> CLI default (so the review still runs, it can't be suppressed).

Verified: a token present only in a tampered on-disk file but absent from the
pre-triage snapshot is now rejected (it was accepted before). Added a regression
test to scripts/ci/test-model-selection.sh, and fixed the header comment to note
the complex-review override as another path to the CLI default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 08:29:18 +02:00
k.renner 6f29ca56b6 Merge pull request 'chore(immich): pre-stage prune:disabled on the im-pg CNPG objects (stage 1)' (#401) from immich-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#401
2026-07-25 06:27:38 +00:00
claude-botandClaude Opus 4.8 44a429685c fix(ci): round-3 pr-review hardening — REVIEW_TOKEN, no-die selection, tests
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the a7890e0a review.

- pr-review.yml: strip REVIEW_TOKEN from the valid-model.sh call too (it runs a
  script from $HOMELAB, branch-supplied for a homelab PR), and guard it with
  `|| true` so a missing script can't kill the step -- restoring the stated "a
  run never dies on selection" invariant. For a complex/security-sensitive
  review, ignore the (injectable) triage pick and run on the CLI default
  (strongest configured) model.
- valid-model.sh: reject a leading dash at the shape check (making the header
  accurate) so an option-like value is refused even without a catalog.
- fetch-models.sh: --retry 2 --retry-connrefused so a transient blip doesn't
  drop the run to the default; add a FETCH_MODELS_BODY_FILE test seam.
- pick-model.sh: pass --settings (new ci/agents/model-picker/settings.json,
  Read deny-list) as belt-and-suspenders behind --allowedTools "".
- test-model-selection.sh: add fetch-models.sh mapping tests (well-formed body
  -> non-null capability columns; degraded body -> loud warning) via the seam,
  and wire the whole suite into .woodpecker/e2e.yml so it runs in CI.
- Condense the duplicated model-selection comment in the four fixed jobs to a
  one-line pointer to pick-model.sh.
- PR description: state the cost / no-fixed-ceiling property change.

Deferred with rationale (in the PR thread): selected-model in the four crons'
posted artifacts (already logged per run), pagination past limit=1000 (API max;
has_more warns), and unit-testing pick-model.sh's JSON extraction (its output
always passes through the tested valid-model.sh gate, and the picker prompt is
developer-controlled, not attacker input).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:43:08 +02:00
claude-botandClaude Opus 4.8 38370f9054 chore(immich): pre-stage prune:disabled on the im-pg CNPG objects (stage 1)
ci/woodpecker/pr/pr-review Pipeline was successful
Stage 1 of folding seed-immich into the existing dedicated immich KS (same
pattern as paperless #399/#400). Adds prune: disabled, in place, to the 5 CNPG
DB objects under seed/immich/ so the ownership handoff can never prune them:

- Cluster/im-pg (pruning deletes its data PVCs)
- ObjectStore/im-pg-store, ScheduledBackup/im-pg-daily
- ExternalSecret/barman-cloud-creds, ExternalSecret/hetzner-s3-creds

The immich-library (300Gi) + ml-cache PVCs already carry the annotation.
Metadata-only; no CNPG restart. im-pg has 30 completed barman backups (last
~2.5h) as a backstop.

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:34:31 +02:00
security-bot 60ee1cade4 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784957186
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 05:33:14 +00:00
k.renner 90e4800304 Merge pull request 'refactor(paperless): fold seed-paperless into the paperless KS (stage 2)' (#400) from paperless-dedicated-fold into master
Reviewed-on: k.renner/homelab#400
2026-07-25 05:30:29 +00:00
claude-botandClaude Opus 4.8 a7890e0aa2 test(ci): extract + unit-test model validation; cut triage cost; doc nits
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the remaining pr-review items.

- Extract the untrusted-model validation out of the pipeline YAML into
  scripts/ci/valid-model.sh (single safe-token + catalog-membership gate) and
  reuse it from both pick-model.sh and pr-review.yml, so the security-critical
  logic lives in one tested place. Add scripts/ci/test-model-selection.sh, an
  offline fixture test (no network / claude / Woodpecker) that pins the
  behaviour: valid ids pass; hallucinated, empty, space-, newline-, leading-
  dash-, and metachar-injected values are rejected; empty catalog rejects all.
  This is the regression test for the flag-injection fix.
- pr-review triage cost: hand the triage the COMPLETE changed-file list
  (files.txt) plus a diff capped at 4000 lines (diff.triage.patch) instead of
  the full up-to-20k review diff, so classifying a PR on the default model stays
  cheap. stats.txt still carries the true totals; triage-prompt.md updated.
- Doc nits: note the new helpers are POSIX `sh` (not `bash` like the older
  scripts/ci/*.sh), and that fetch-models.sh's json mode is a debug affordance.

Deferred with rationale (also in the PR thread):
- Model id in the four cron jobs' posted artifacts: selection (including the
  fallback-to-default) is already logged per run in the CI output, the
  operational surface for this; threading it through four different posted
  formats is a separate observability follow-up.
- --settings sandbox on the picker: it is granted no tools (--allowedTools ""),
  so there is nothing for a Read-deny sandbox to constrain -- the other agents'
  sandboxes exist to scope tools they DO have.
- Pagination past limit=1000: that is the API's max page size; >1000 models is
  not a real scenario, and the has_more warning turns even that into a visible
  log line rather than silent truncation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:27:02 +02:00
claude-botandClaude Opus 4.8 8a63be8515 refactor(paperless): fold seed-paperless into the paperless KS (stage 2)
ci/woodpecker/pr/pr-review Pipeline was successful
Completes the paperless seed->apps move. Unlike woodpecker this is a single
ownership transfer into the app's existing dedicated KS.

- git mv seed/paperless/{cluster,objectstore,scheduled-backup,barman-creds-
  externalsecret,hetzner-creds-externalsecret,pvcs}.yaml -> apps/paperless/, and
  add them to apps/paperless/kustomization.yaml.
- paperless KS: drop 'dependsOn: seed-paperless', add
  'dependsOn: infrastructure-cnpg-barman', add the CNPG Cluster healthCheckExprs
  (moved off seed-paperless), refresh the stale 'seed phase' comment.
- Delete clusters/homelab/seed-paperless.yaml and the now-empty
  kubernetes/seed/paperless/.
- e2e-test.sh: drop the seed-paperless readiness check (the paperless app KS is
  already checked).
- Fix two stale file-path comments that pointed at the old location:
  apps/woodpecker/objectstore.yaml and infrastructure/openbao/snapshot-pvc.yaml
  (seed/paperless -> apps/paperless).

Safe ownership transfer (nothing pruned): all 9 seed/paperless objects carry
prune: disabled (5 CNPG objects from stage 1 #399, 4 PVCs pre-existing), so
deleting seed-paperless skips them in GC and the paperless KS adopts them in
place. No CNPG restart (Cluster is not a Deployment), no app restart.

Verified: kubectl kustomize apps/paperless builds 40 objects incl. Cluster/
pprl-pg, exactly 9 prune-protected; bash -n e2e-test.sh OK; seed/paperless fully
removed; no stale file-path refs remain (only the dated migration log + the
OpenBao KV path pending the separate hetzner-creds decoupling).

Left intentionally: the hetzner-creds ExternalSecret still reads OpenBao key
seed/paperless/hetzner-creds (a vault path, not a repo path).

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:25:09 +02:00
k.renner c0635775e5 Merge pull request 'chore(paperless): pre-stage prune:disabled on the pprl-pg CNPG objects (stage 1)' (#399) from paperless-prune-protect into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#399
2026-07-25 05:20:37 +00:00
claude-botandClaude Opus 4.8 555c783774 chore(paperless): pre-stage prune:disabled on the pprl-pg CNPG objects (stage 1)
ci/woodpecker/pr/pr-review Pipeline was successful
Stage 1 of folding seed-paperless into the existing dedicated paperless KS.
Adds kustomize.toolkit.fluxcd.io/prune: disabled, in place, to the five CNPG DB
objects under seed/paperless/ so the ownership handoff can never prune them:

- Cluster/pprl-pg (HA, 2 instances — pruning deletes its data PVCs)
- ObjectStore/pprl-pg-store, ScheduledBackup/pprl-pg-daily
- ExternalSecret/barman-cloud-creds, ExternalSecret/hetzner-s3-creds

The 4 data/media/consume/redis PVCs already carry the annotation. Landing it via
the existing seed-paperless KS guarantees it's live before the move. Metadata-
only; no spec change, so CNPG and the running pprl-pg instances are untouched.

Unlike woodpecker this is a single ownership transfer (seed-paperless ->
paperless KS, both already dedicated), so no app-object commonAnnotations / CI
restart is involved. pprl-pg has healthy barman backups (last ~2.7h) as a
backstop.

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:16:27 +02:00
claude-botandClaude Opus 4.8 f333dfd606 fix(ci): block flag injection via agent-supplied model id (pr-review)
ci/woodpecker/pr/pr-review Pipeline is running
The pr-review triage agent reads the attacker-controlled PR diff and writes
triage.json; its Write tool is not path-scoped, so a prompt-injected diff can
make it emit any `model` string. The catalog validation used
`grep -qxF -- "$MODEL"`, and grep -F treats a MULTI-LINE operand as
newline-separated patterns: a value like
"claude-sonnet-5\n--dangerously-skip-permissions" matches on its first line,
passes validation, and -- via the unquoted `${MODEL:+--model $MODEL}` expansion
-- word-splits into an extra argv entry, injecting a CLI flag into the review
agent's own `claude` invocation (defeating its --allowedTools allowlist and Read
sandbox, with CLAUDE_CODE_OAUTH_TOKEN in that process's env).

Removing map_model() (which returned one of three literal ids) is what opened
this; restore the invariant with a single-token guard before the expansion:
`case "$MODEL" in ""|*[!A-Za-z0-9._-]*) MODEL= ;; esac`. Verified it turns the
newline/space/hallucinated payloads into a plain `claude -p` (CLI default) while
still passing real ids. Mirrored the guard in pick-model.sh as defence in depth
(its grep -oE already keeps CHOICE single-line, so it was not exploitable).

Also from the review:
- pr-review.yml: correct the HOMELAB comment -- the fetch-models.sh call is
  env -u REVIEW_TOKEN'd, so CLAUDE_CODE_OAUTH_TOKEN (not REVIEW_TOKEN) is what
  branch-supplied shell there can see.
- pick-model.sh / triage-prompt.md: stop asking the picker to weigh "cost" the
  catalog does not expose; reason from the visible capability signals (context /
  max_output / effort|thinking|code_exec) as the scale proxy instead.
- fetch-models.sh: drop 2>/dev/null on curl so its -S error reaches the log.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:12:06 +02:00
k.renner 9819c83045 Merge pull request 'chore(woodpecker): drop the transitional commonAnnotations prune-guard (stage 3)' (#398) from woodpecker-drop-transient-prune-guard into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#398
2026-07-25 05:11:20 +00:00
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 9d36087857 fix(ci): second-round pr-review hardening for model selection
ci/woodpecker/pr/pr-review Pipeline was successful
- pick-model.sh: stop swallowing fetch-models.sh and claude stderr so the real
  cause of a fallback (no token / HTTP 401 / parse error / auth) reaches the job
  log instead of just "no catalog available"; time-bound the selection call with
  a self-probing `timeout` (the wrapper is skipped unless a probe confirms the
  image's timeout takes coreutils "SECS CMD" syntax, so it can never silently
  route every run to the default); strip PUSH_TOKEN and REVIEW_TOKEN as well --
  issue-fix binds its bot PAT a second time as PUSH_TOKEN, which survived the
  original `env -u GITEA_TOKEN`; `grep -qxF --`.
- fetch-models.sh: warn loudly when every model returns a null context window,
  so a /v1/models schema change surfaces as a message rather than silently
  reducing both selectors to name+date guessing.
- pr-review.yml: `env -u REVIEW_TOKEN` on the fetch-models.sh call (it needs only
  CLAUDE_CODE_OAUTH_TOKEN), matching the agent calls below it; `grep -qxF --`.

Deferred with rationale: hard-bounding the triage's diff input (kept as prompt
guidance -- a hard cap risks its security-sensitivity judgment) and an offline
fallback-path test harness (tracked as a follow-up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:01:27 +02:00
claude-botandClaude Opus 4.8 b329c432c5 fix(ci): address pr-review feedback on dynamic model selection
ci/woodpecker/pr/pr-review Pipeline was successful
- fetch-models.sh: request ?limit=1000 so older/cheaper models are not dropped
  by the API's 20-item default page; warn on has_more. Add a --table mode so
  the catalog format lives in ONE place (pick-model.sh and pr-review.yml now
  consume it instead of each re-deriving the jq table). Clean up the HTTP-000
  double-print and surface jq parse errors instead of hiding them.
- pick-model.sh: run the picker with GITEA_TOKEN stripped from its env and
  without acceptEdits (it is granted no tools); consume fetch-models.sh --table.
- pr-review.yml: print model `CLI default` instead of an empty backtick pair on
  the fallback path; document that the fetch-models.sh call runs branch-supplied
  shell (not a new exposure -- Woodpecker already runs the pipeline YAML from
  the PR branch).
- triage-prompt.md: read stats + the changed-file list first and pull the full
  diff only when needed, so the triage (now on a capable default model) does not
  read a 20k-line diff just to classify a trivial PR.

Verified against the live catalog: all 11 currently-listed ids run under
`claude --model`, and the capability fields (context/effort/thinking/...) are
present in the OAuth /v1/models response -- so the reviewer's blocking concern
(a listed-but-CLI-unusable id) has no live trigger, and the fallback-to-default
path still covers it if that ever changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 06:34:38 +02: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
claude-botandClaude Opus 4.8 ffe3be4b54 feat(ci): select agent model from the live catalog, not hardcoded ids
ci/woodpecker/pr/pr-review Pipeline was successful
The pr-review, update-audit, security-scan, alert-monitor and issue-fix
pipelines each pinned a dated model id (--model claude-sonnet-5, plus
pr-review's haiku/sonnet/opus map_model allowlist). Those go stale every
time a new model ships and bake in assumptions about which model families
exist, so a newly released model could never be chosen without a code edit.

Replace all of it with selection driven only by what is available right now:

- scripts/ci/fetch-models.sh queries the catalog the CLI itself uses
  (Anthropic GET /v1/models, via the existing CLAUDE_CODE_OAUTH_TOKEN and the
  required anthropic-beta: oauth-2025-04-20 header) and emits each model with
  its objective capability signals -- context, max_output, effort, thinking,
  code_exec, release date -- with no id patterns and no family names.
- scripts/ci/pick-model.sh lets a triage agent choose the best-fit id from
  that catalog for a one-line task description, weighing every listed model
  on its merits. pr-review's existing per-PR triage agent now does the same,
  reading the live catalog instead of emitting a fixed haiku/sonnet/opus tier.

No model id -- and no model family -- is baked into any pipeline. A model
released tomorrow, of any family, becomes eligible the day the API lists it.
The bootstrap selection call runs on the CLI default model, and if the catalog
is ever unreachable --model is simply omitted (CLI default) rather than falling
back to a named model, so a run never dies over model selection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 06:14:45 +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