Commit Graph
4354 Commits
Author SHA1 Message Date
k.renner 5c28c5114c Merge pull request 'feat(esphome): enable IPv6 on the shelly BLE proxy' (#169) from feat/esphome-proxy-ipv6 into master
Reviewed-on: gitea_admin/homelab#169
2026-07-13 16:59:54 +00:00
claude-botandClaude Fable 5 98346b370e feat(secrets): ClusterSecretStore + declarative audit (SOPS migration phase 2)
OpenBao was initialized and configured today (kv-v2 at homelab/, k8s
auth, eso-read policy + role bound to the external-secrets SA); the
store can therefore turn Ready and no longer endangers the post-crds
wait gate. Audit device moves into the server HCL because OpenBao 2.5
rejects API-managed audit devices; log_raw stays off so Loki only ever
sees HMAC-redacted values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
2026-07-13 18:46:35 +02:00
claude-botandClaude Opus 4.8 ad35dc3189 feat(esphome): enable IPv6 on the shelly BLE proxy
The IoT VLAN advertises a global IPv6 prefix (RA/SLAAC), but the Atom Echo
ran v4-only because ESPHome defaults IPv6 off. Enable it so the device is a
proper dual-stack citizen of the segment and has a real global IPv6.

This is the prerequisite for the DNS-side fix: once the device has a real
AAAA, a `unifi_dns_record` AAAA for shelly-ble-proxy-1.iot.renner.uno can be
pinned to it (same pattern as the existing udm/captive records in
opentofu/dns_renner_uno.tf), so the name resolves to the device instead of
falling through to the *.renner.uno proxy wildcard.

Takes effect on the next USB re-flash (no OTA on this device by design).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:43:31 +02:00
k.renner 5e3c1fcff9 Merge pull request 'feat(secrets): OpenBao + External Secrets Operator — SOPS migration phase 1' (#168) from openbao-eso into master
Reviewed-on: gitea_admin/homelab#168
2026-07-13 16:30:29 +00:00
claude-botandClaude Fable 5 be59c4ae37 feat(secrets): deploy OpenBao + External Secrets Operator (SOPS migration phase 1)
Single-node raft OpenBao (5Gi ceph-block, UI at openbao.k.renner.uno,
internal-only ingress) and ESO 2.x. No ClusterSecretStore yet — it can
only turn Ready after manual init/unseal, and committing it now would
freeze the infrastructure-crds wait gate (2026-07-13 incident shape).
Full phased plan incl. per-file checklist and DR runbook in
kubernetes/MIGRATION-sops-openbao.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
2026-07-13 18:26:01 +02:00
k.renner 1210d0da7a Merge pull request 'feat(shelly-manager): wire the ESPHome proxy Noise key' (#166) from feat/shelly-proxy-key into master
Reviewed-on: gitea_admin/homelab#166
2026-07-13 16:01:59 +00:00
claude-botandClaude Opus 4.8 2f2b96905e feat(shelly-manager): wire the ESPHome proxy Noise key
shelly-manager's native ESPHome-API client (PR k.renner/shelly-manager#12)
does the real Noise_NNpsk0 handshake to the Atom Echo bluetooth_proxy when
it has the api.encryption.key. Provide it:

  * shelly-ble-proxy-key.sops.yaml — SOPS Secret holding api-key, the same
    value as the ESPHome dashboard's shelly_ble_proxy_api_key.
  * shelly-manager-deployment: inject it as Ble__Proxies__0__Key via
    secretKeyRef.

Effect: /ble-radios upgrades from a bare TCP :6053 check to a real
handshake + DeviceInfo (device name/version), which also validates the
Noise/frame stack on live hardware ahead of the BLE scan/GATT work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:52:33 +02:00
security-bot 6025df7d1a chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783956697
Updates managed by image-automation-controller.
2026-07-13 15:42:43 +00:00
k.renner 27adf660b0 Merge pull request 'fix(gitea): allow Prometheus to scrape in-cluster gitea /metrics' (#165) from fix/gitea-metrics-netpol into master
Reviewed-on: gitea_admin/homelab#165
2026-07-13 15:30:22 +00:00
claude-botandClaude Opus 4.8 c69fa9ac8a fix(gitea): allow Prometheus (monitoring ns) to scrape /metrics
After the LXC->in-cluster cutover the gitea scrape target became an in-cluster
pod, which the apps default-deny-ingress CiliumNetworkPolicy drops (it allows
only same-ns / home-assistant / host). Prometheus scrapes time out (context
deadline exceeded) and TargetDown keeps firing. Add a scoped CNP allowing the
monitoring namespace to reach the gitea pod on :3000. Monitoring egress is
unrestricted (its only policy is ingress-only), so this ingress allow is
sufficient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 17:27:53 +02:00
k.renner 0d411be12a Merge pull request 'chore(ansible): retire gitea/woodpecker LXC provisioning + srv.renner.uno refs' (#164) from chore/retire-lxc-ansible into master
ci/woodpecker/push/deploy-woodpecker-agent Pipeline failed
Reviewed-on: gitea_admin/homelab#164
2026-07-13 15:16:41 +00:00
security-bot 9e7a7b175f chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783933726
Updates managed by image-automation-controller.
2026-07-13 14:03:27 +00:00
claude-botandClaude Opus 4.8 d2c620f825 chore(ansible): drop remaining retired-runner/agent leftovers
Follow-up to the LXC retirement sweep:
- remove orphan inventory/group_vars/woodpecker_agents (group already deleted)
- drop the retired gitea-runner (root@10.2.40.214 act_runner) journalctl entry
  from .vscode/settings.json

Kept (live): the ci.renner.uno gatus check and the woodpecker-ci@homelab
authorized key, which belong to the in-cluster Woodpecker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 15:47:00 +02:00
claude-botandClaude Opus 4.8 dc36653808 chore(ansible): retire gitea/woodpecker LXC provisioning + refs
Gitea and Woodpecker now run in-cluster (k8s); their Proxmox LXCs are retired.
Remove the provisioning that rebuilt them and every reference to the dead hosts:

- delete roles gitea-lxc, gitea-runner, woodpecker-agent, woodpecker-server
- delete plays install-gitea, install-gitea-runner, configure-gitea-runner-logging,
  install-woodpecker-server, install-woodpecker-agent
- drop [woodpecker_server]/[woodpecker_agents] inventory groups + group_vars
- monitoring: remove the gitea Prometheus scrape job + gitea:22 blackbox target;
  repoint the gatus / blackbox-dns / chaos-test / proxmox-dns dig probes and the
  dns-overview dashboard panel from gitea.srv.renner.uno to k8s-1.srv.renner.uno
- drop the retired gitea-runner journalctl entries from .vscode/settings.json

Live services keep their public hostnames (gitea.renner.uno, ci.renner.uno),
untouched. Other *.srv.renner.uno hosts untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 15:44:56 +02:00
k.renner f5b46e7a28 Merge pull request 'fix(monitoring): scrape in-cluster Gitea, not retired LXC' (#163) from fix/prometheus-gitea-scrape into master
Reviewed-on: gitea_admin/homelab#163
2026-07-13 13:11:50 +00:00
k.renner 29416d7e05 Merge pull request 'feat(shelly-manager): register the Atom Echo BLE proxy as a radio' (#162) from feat/shelly-manager-ble-proxy into master
Reviewed-on: gitea_admin/homelab#162
2026-07-13 13:11:42 +00:00
claude-botandClaude Opus 4.8 59aa162a19 fix(monitoring): scrape in-cluster Gitea, not the retired LXC
Gitea moved in-cluster (apps ns StatefulSet); LXC 114 is stopped, so the static
`gitea` scrape target gitea.srv.renner.uno:3000 is down -> firing TargetDown.
Point it at the ClusterIP Service (gitea.apps.svc.cluster.local:3000), which
serves /metrics anonymously (verified HTTP 200 with gitea_* metrics).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 15:06:20 +02:00
claude-botandClaude Opus 4.8 0d7e530126 feat(shelly-manager): register the Atom Echo BLE proxy as a radio
Add Ble:Proxies[0] = { atom-echo-1, shelly-ble-proxy-1.iot.renner.uno, 6053 } to
the shelly-manager deployment so the flashed M5 Atom Echo esphome bluetooth_proxy
(now up at 10.2.30.109, ESPHome API :6053 open) shows on the /ble-radios page and
is liveness-probed. Reached over the same net1 macvlan route as the Shellys.
Provisioning THROUGH it is the native ESPHome-API client, still to come.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:04:07 +02:00
k.renner 365852efe4 Merge pull request 'chore(esphome): trim Shelly BLE proxy to WiFi + API only' (#161) from chore/esphome-proxy-trim into master
Reviewed-on: gitea_admin/homelab#161
2026-07-13 12:59:35 +00:00
claude-botandClaude Opus 4.8 fdddbe7e78 chore(esphome): trim Shelly BLE proxy to WiFi + API only
Per how it's actually run: drop OTA and the fallback AP/captive_portal — this is
a fixed, low-value proxy, so updates and any WiFi recovery are done by re-flashing
over USB. Secrets shrink to iot_wifi_ssid / iot_wifi_password /
shelly_ble_proxy_api_key (the api encryption key stays; it's the ble-agent
handoff). README secrets list + flashing note updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:01:51 +02:00
k.renner 71d1111879 Merge pull request 'feat(esphome): version the Atom Echo Shelly BLE proxy config' (#158) from feat/esphome-shelly-ble-proxy-config into master
Reviewed-on: gitea_admin/homelab#158
2026-07-13 06:13:49 +00:00
k.renner 91fceb11ac Merge pull request 'fix(gitea): BUILTIN_SSH_SERVER_USER=gitea for gitea@ auth' (#160) from fix/gitea-builtin-ssh-user into master
Reviewed-on: gitea_admin/homelab#160
2026-07-12 23:49:54 +00:00
claude-botandClaude Opus 4.8 c745723e82 fix(gitea): BUILTIN_SSH_SERVER_USER=gitea so gitea@ remotes authenticate
#159 set SSH_USER=gitea, but SSH_USER is cosmetic (clone-URL display only) and
does not change which login the built-in SSH server accepts — so gitea@ still got
Permission denied. BUILTIN_SSH_SERVER_USER is the setting the built-in server
checks the login name against (defaults to RUN_USER=git); pin it to gitea so the
existing gitea@ remotes (the LXC's non-default SSH user) authenticate with zero
client changes. git@ stops authenticating (single built-in login) — unused here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 01:46:31 +02:00
claude-bot f3cc5c7027 Merge pull request 'fix(gitea): SSH_USER=gitea for existing remotes' (#159) from fix/gitea-ssh-user into master 2026-07-12 23:28:52 +00:00
claude-botandClaude Opus 4.8 3d40ad3620 fix(gitea): SSH_USER=gitea so existing gitea@ remotes keep working
The retired LXC ran RUN_USER=gitea, so all clone URLs and git remotes are
gitea@gitea.renner.uno. The rootless image runs as `git`, defaulting SSH_USER to
`git` and rejecting `gitea@` logins (Permission denied). Pinning SSH_USER=gitea
restores the pre-migration convention with no per-repo remote changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 01:28:50 +02:00
claude-bot 33e04f0892 Merge pull request 'feat(woodpecker): cut over to in-cluster server + k8s-backend agent' (#157) from feat/woodpecker-cutover into master 2026-07-12 23:19:21 +00:00
claude-botandClaude Opus 4.8 04d8485b04 feat(esphome): version the Atom Echo Shelly BLE proxy config
Source-of-truth YAML (+ README) for the M5 Atom Echo dedicated Bluetooth proxy
that lends its BLE radio to shelly-manager's ble-agent for provisioning WiFi-less
Shellys over mOS RPC-over-BLE. Built/flashed via the in-cluster ESPHome Device
Builder (esphome.home.renner.uno).

Lean, dedicated config (no voice — bluetooth_proxy + voice boot-loops the
ESP32-PICO), esp-idf framework, bluetooth_proxy active. The api encryption key is
the handoff to the ble-agent (its BLE_PROXY_NOISE_PSK). The dashboard reads from
its PVC, not git, so these files are the reviewed/backed-up copy kept in sync by
hand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 01:11:39 +02:00
claude-botandClaude Opus 4.8 2bd1acc7d5 feat(woodpecker): cut over to the in-cluster server + kubernetes-backend agent
Moves ci.renner.uno off LXC 231 onto the in-cluster StatefulSet (DB loaded from
the LXC beforehand) and brings the kubernetes-backend agent online, retiring the
docker agents (LXC 910/911). server replicas 0->1; Service ExternalName->ClusterIP
with http:8000 (UI) + grpc:9000 (agent<->server); agent replicas 0->1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 01:11:20 +02:00
claude-bot 515e9bf95c Merge pull request 'feat(gitea): phase 2 — flip Service to ClusterIP + SSH IngressRouteTCP' (#157) from feat/gitea-phase2-flip into master 2026-07-13 00:43:20 +02:00
claude-botandClaude Opus 4.8 52f5619e5a feat(gitea): phase 2 — flip Service to ClusterIP + SSH IngressRouteTCP
Moves gitea.renner.uno and git-over-ssh onto the in-cluster StatefulSet, which
has been loaded from LXC 114 and verified (data intact, SSH host-key identity
preserved, master at the revision Flux expects). gitea Service ExternalName ->
ClusterIP; adds the gitea-ssh IngressRouteTCP binding the Traefik entrypoint
(added in phase 1) to :2222. Woodpecker stays on the LXC (server replicas 0,
Service ExternalName) — its cutover is a separate migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-13 00:42:41 +02:00
claude-bot fbed8bb392 Merge pull request 'feat(gitea,woodpecker): migrate off Proxmox LXC into the cluster' (#155) from feat/gitea-woodpecker-k8s into master 2026-07-12 22:15:43 +02:00
claude-botandClaude Opus 4.8 5d66fb5445 chore(woodpecker): add SOPS-encrypted secret and enable it for phase 1
woodpecker-server envFrom's woodpecker-secret; without it the pod crashloops and
holds the apps Kustomization (wait:true) red. Encrypted with the repo age
recipient (kubernetes/.sops.yaml); Flux decrypts it via the apps Kustomization's
sops provider. Values (AGENT_SECRET/GITEA_CLIENT/GITEA_SECRET) lifted from the
running CT 231 woodpecker-server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-12 21:38:07 +02:00
claude-botandClaude Opus 4.8 287e01a95c feat(gitea,woodpecker): phase 1 — in-cluster workloads, Services stay ExternalName
Stand up Gitea + Woodpecker as in-cluster workloads beside the existing
Proxmox LXCs WITHOUT flipping any traffic. Both Services remain ExternalName
-> the LXCs, the Woodpecker agent ships replicas:0, and the gitea SSH
IngressRouteTCP is absent. Merging this changes nothing user-facing — Flux
brings up empty-PVC pods that get loaded and verified out-of-band before a
phase-2 PR flips the Services to ClusterIP.

Gitea: gitea/gitea:1.21.4-rootless (its entrypoint runs environment-to-ini,
so do NOT override it), built-in SSH server on 2222, forced GITEA__log__ROOT_PATH,
host-key reuse. Traefik gains an additive gitea-ssh TCP entrypoint (:22 on the
shared VIP). Woodpecker: kubernetes backend (build pods in ns woodpecker-ci on
ceph-filesystem RWX, privileged-plugin allow-list + PodSecurity label), agent
replicas:0; SOPS secret is created+committed at pre-flight, never kubectl-applied.

Cutover is fully GitOps: flux suspend + kubectl exec/scale data load, verify
via port-forward, phase-2 flip with a repo-resync; break-glass = repoint the
Flux source to the GitHub mirror (known-good tag before-k8s-migration). See
kubernetes/apps/MIGRATION-gitea-woodpecker.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HQhvu1mrQ7EgwLhrzdNyY7
2026-07-12 20:55:15 +02:00
k.renner f9a0d6cd1a Merge pull request 'feat(esphome): deploy ESPHome Device Builder behind an Authentik gate' (#156) from feat/esphome-device-builder into master
Reviewed-on: gitea_admin/homelab#156
before-k8s-migration
2026-07-12 19:11:09 +02:00
claude-botandClaude Opus 4.8 948852d83f feat(esphome): deploy ESPHome Device Builder behind an Authentik gate
Adds the ESPHome Device Builder (ghcr.io/esphome/esphome:2026.6.2, :6052) to the
home-assistant namespace so ESPHome devices — starting with an M5 Atom Echo BLE
proxy for shelly-manager's BLE provisioning — can be built, browser/USB-flashed
(ESP Web Tools / Web Serial), and config-managed in-cluster.

v1 deliberately runs with NO macvlan. Unlike shelly-manager it needs no L2
presence on the IoT VLAN: initial flashing is browser-side over USB and builds
are offline. That also sidesteps the macvlan-on-0.0.0.0 hole (a gated pod with a
macvlan bypasses Cilium policy and exposes its unauthenticated UI on the IoT VLAN
— confirmed against shelly-manager/matter-server/otbr). Live status / logs / OTA
would need cluster→v30iot reachability (a separate, still-open item); a macvlan
can be added later if wanted.

Mirrors the shelly-manager gate pattern exactly:
- home-assistant: esphome deployment (Recreate, RWO 10Gi ceph-block PVC for the
  PlatformIO build cache), service, gate ExternalName, and a Traefik ingress that
  backends the Authentik esphome-gate outpost (not esphome directly).
- authentik: an esphome-gate proxy provider + application + dedicated outpost
  (passkey step-up, "ESPHome Managers" group, git-owned membership) in
  blueprints-configmap.yaml, plus esphome-gate-egress CNP (outpost → :6052).
- network-policies: the home-assistant default-deny-ingress grants ONLY the
  esphome-gate outpost → :6052. :6052 is unique (no collision with the shared
  :8080 the vscode/shelly gates ride on).

Validated: kubectl kustomize builds cleanly for the home-assistant, authentik,
and network-policies kustomizations, all rendering the new resources.

Post-deploy checks: confirm the dashboard has no credentials:'omit' service
worker/manifest that 302-loops at the gate (add skip_path_regex if so); confirm
the image serves the dashboard on :6052 with the default entrypoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:08:59 +02:00
security-bot 0aa3b30f50 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783872114
Updates managed by image-automation-controller.
2026-07-12 16:03:34 +00:00
security-bot ab05f833e6 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783870162
Updates managed by image-automation-controller.
2026-07-12 15:31:14 +00:00
security-bot 5fac770347 chore(image-automation): bump ghcr.io/music-assistant/server:2.9.8
Updates managed by image-automation-controller.
2026-07-12 15:02:03 +00:00
security-bot bd976b8f45 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783846070
Updates managed by image-automation-controller.
2026-07-12 08:49:39 +00:00
security-bot 6056b64c71 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783840829
Updates managed by image-automation-controller.
2026-07-12 07:24:46 +00:00
security-bot 1f67cb7446 chore(image-automation): bump ghcr.io/music-assistant/server:2.9.7
Updates managed by image-automation-controller.
2026-07-12 03:01:58 +00:00
k.renner 8f316893cc Merge pull request 'woodpecker: auto-deploy netbird-proxy-watchdog role changes' (#154) from fix/netbird-watchdog-deploy-path into master
ci/woodpecker/push/deploy-netbird-proxy Pipeline was successful
ci/woodpecker/push/deploy-netbird-server Pipeline was successful
Reviewed-on: gitea_admin/homelab#154
2026-07-11 21:45:12 +02:00
k.rennerandClaude Opus 4.8 464e10417b woodpecker: auto-deploy netbird-proxy-watchdog role changes
ci/woodpecker/push/deploy-netbird-server Pipeline was successful
Both install-netbird-server.yml (VPS-1) and install-netbird-proxy.yml (proxy-1)
apply the netbird-proxy-watchdog role, but neither deploy pipeline's path filter
watched ansible/roles/netbird-proxy-watchdog/**. So the flood-watchdog (#153)
did not auto-deploy on merge and had to be triggered manually. Add the role to
both filters so future watchdog changes redeploy both hosts on merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 21:42:21 +02:00
k.renner 11ec6449db Merge pull request 'netbird-proxy-watchdog: flood watchdog for instant route-manager-wedge recovery' (#153) from fix/netbird-proxy-flood-watchdog into master
ci/woodpecker/manual/deploy-netbird-proxy Pipeline was successful
ci/woodpecker/manual/deploy-netbird-server Pipeline was successful
2026-07-11 21:16:27 +02:00
k.rennerandClaude Opus 4.8 6c66ca3c42 netbird-proxy-watchdog: add flood watchdog for instant route-manager-wedge recovery
The reverse-proxy wedges when an ephemeral NetBird routing peer (a k8s ingress
pod) is DELETED: its route manager pins the now-gone peer and never re-selects
a healthy HA-group peer, serving errors for every domain behind that route
until restarted. Upstream bug, unfixed as of netbird v0.74.4 (netbirdio/netbird
#3976; dnsinterceptor disables HA for an already-routed IP + recalculateRoutes
aborts on the dead peer's teardown error). A restart is the only reliable clear.

The existing URL-probe watchdog does eventually catch it, but far too slowly:
its 127.0.0.1 probe stays warm for ~3 min after the wedge starts, then needs 3
consecutive 60s rounds -- so it took 5-9 min on 2026-07-11, and both proxy
members wedged so the public outage lasted until the slower one recovered.

The wedge floods "couldn't fetch peer state" in the proxy's own log the instant
it happens, minutes before the probe notices. That flood is an unambiguous
fingerprint: 14 days of logs show it ONLY in bursts of tens per minute, never a
benign trickle. So add a persistent follower that restarts the proxy on the
first matching line.

Trade, chosen deliberately: ~1/6 of these bursts self-recover (the re-select
race sometimes wins) and get an unnecessary restart -- but a proxy restart is
near-instant, so a rare spurious restart beats a multi-minute outage.

- new netbird-proxy-flood-watchdog.sh (docker logs -f | grep, restart on match)
- new netbird-proxy-flood-watchdog.service (Type=simple, Restart=always)
- shares LAST_RESTART_FILE + cooldown with the probe watchdog (no loops, no
  double-restart); URL-probe watchdog kept unchanged as the backstop.

Deploying only adds a systemd service; it does not touch the proxy or ingress,
so it can't itself cause an outage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 17:04:36 +02:00
k.renner b04546e14e Merge pull request 'netbird-ingress: graceful drain + paced roll so rolls don't drop public ingress' (#152) from fix/netbird-ingress-graceful-drain into master 2026-07-11 10:48:21 +02:00
k.rennerandClaude Opus 4.8 67353b8e29 netbird-ingress: graceful drain + paced roll so rolls don't drop public ingress
Every netbird image bump (image-automation, ~weekly) rolls this DaemonSet and
takes public ingress (home.renner.uno, mesh-only via the HA route group) down
for ~1 min — observed 2026-07-11 08:58 on the uptime monitor, correlated exactly
with the 3 peers rolling at 06:56:18/39/58 UTC. It is NOT the IPS/firewall
(the UDM was idle; the "intrusions" were the admin laptop hitting its own
ingress) — it's the roll itself failing over slowly.

Root cause: the redundant roll (maxUnavailable:1, 2/3 peers always up) should be
seamless, but two gaps break the failover:
  - No preStop drain: a rolled peer is only SIGTERM'd, so the mesh keeps the
    dead peer in the route group until a keepalive timeout (~25-30s); traffic
    riding through it black-holes until then. Compounded by ephemeral
    enrollment (emptyDir) — the restarted pod enrolls a NEW peer and the old one
    lingers until reaped.
  - No minReadySeconds: the roll advances to the next node as soon as
    `netbird status --check ready` passes (seconds), faster than the mesh
    re-converges onto the freshly-enrolled peer — so the HA route is degraded
    for the whole ~1-min roll.

Fix:
  - preStop `netbird down` — deregister the peer from management before the
    process dies, so the HA route fails over to the other per-node peers
    immediately instead of after a timeout.
  - minReadySeconds: 45 — hold each new peer Ready until the mesh has converged
    onto it before rolling the next, so >=2 peers are always actually serving.

Keeps auto-updates on; turns the surprise ~1-min outage into a seamless roll.
Validation: watch the uptime monitor across the next roll (natural image bump
or a deliberate `kubectl rollout restart ds/netbird-ingress`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 10:37:27 +02:00
security-bot 6c599ea246 chore(image-automation): bump netbirdio/netbird:v0.74.4-rootless
Updates managed by image-automation-controller.
2026-07-11 06:56:09 +00:00
k.renner ea4cb567f4 Merge pull request 'otbr: disable (replicas 0) — splits the Nest Thread partition, breaks Matter' (#151) from fix/otbr-disable-splits-thread into master
Reviewed-on: gitea_admin/homelab#151
2026-07-11 03:30:16 +02:00
k.rennerandClaude Opus 4.8 723ec6a30b otbr: disable (replicas 0) — it splits the Nest Thread partition and breaks Matter
otbr is meant to be an additive co-border-router on the existing Nest Thread
network (it's seeded with the Nest dataset to join the same partition). In
practice it does not merge cleanly: its SLZB-06 radio is RF-isolated enough
that otbr elects itself leader of a rival partition, and the network churns
(OMR prefix observed changing fd5f -> fdd5, leader role flapping). The two
same-network partitions then bridge only over the Ethernet backbone, which
carries a ping but is too slow for the large fragmented Matter CASE handshake:
every device stays mDNS-discoverable while CASE times out. Scaling otbr to 0
re-merges the mesh onto the Nest hubs and Matter recovers.

Explicitly NOT a root-cause of a config bug. The DHCP/sbr routing changes
(19a4482, df61407) were suspected and investigated: matter-server's IPv6 path
to the mesh works (reaches devices, ping ok), sbr's v6 table 100 is an inert
orphan (no v6 rule references it), and otbr's eth0 IPv6 default is outranked by
net1's pref-high RA default. The instability sits at the Thread partition
layer, not in the pod's routing table. Re-enable only after the co-BR
integration is verified to MERGE (one partition, one OMR, otbr's router table
shows the Nest routers) rather than split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 03:28:26 +02:00