Commit Graph
4354 Commits
Author SHA1 Message Date
k.renner 103f3b9284 Merge pull request 'mosquitto: auto-update the broker image (image-automation) + guard the rollout' (#130) from feat/mosquitto-pin-image into master
Reviewed-on: gitea_admin/homelab#130
2026-07-08 11:48:15 +02:00
k.rennerandClaude Opus 4.8 d0c8044a67 mosquitto: alert on a bad image bump + roll without a downtime gap
image-automation commits tag bumps straight to master with no CI gate, and
the broker is a single pod — so an image that fails to come up would be
silent. Two changes make that safe:

- gatus TCP-checks the broker's ClusterIP on :1883, so a pod that never comes
  up alerts within one interval. Reachability needed a scoped egress (apps ns)
  + ingress (home-assistant ns, gatus pod on :1883 only), mirroring the
  existing evcc -> mosquitto:1883 allow.
- strategy Recreate -> RollingUpdate (maxUnavailable:0, maxSurge:1). With the
  PVC gone there is no RWO volume to fence, so the new broker comes up and
  passes its readiness probe before the old one is torn down — no full-down
  window on a bump/reschedule; clients on the old pod just reconnect. The brief
  two-pod overlap is two independent stateless brokers behind the LB, fine with
  no shared state + self-healing discovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 11:44:47 +02:00
k.rennerandClaude Opus 4.8 2155edd3b4 mosquitto: put the broker image under Flux image-automation
The broker ran on a floating `eclipse-mosquitto:2` tag — no reproducibility,
and a reschedule could pull a changed/broken image. Rather than hand-pin a
digest (which would then never get patched), add it to the repo's Flux
image-automation like redis et al.:

- ImageRepository + ImagePolicy tracking eclipse-mosquitto, semver range 2.x
  (auto-bump minor+patch; a 3.0 major stays a deliberate migration), matching
  plain-semver tags only (excludes -openssl/latest/bare 2).
- The deployment image carries the pinned tag + the $imagepolicy marker, so
  each bump lands as a git commit and rolls the broker (brief restart;
  discovery self-heals via HA).

Net: the broker is both pinned (reproducible) and auto-patched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 11:32:29 +02:00
k.renner de3ff25c19 Merge pull request 'mosquitto: availability hardening — stateless broker + priority + fast failover' (#129) from feat/mosquitto-availability into master
Reviewed-on: gitea_admin/homelab#129
2026-07-08 10:31:12 +02:00
k.rennerandClaude Opus 4.8 9738f6fd6e mosquitto: availability hardening — stateless broker + priority + fast failover
The broker is now load-bearing (heat pump, energy meters, EV charging, HA all
depend on it) but was a slow-to-recover SPOF: single replica on a Ceph RWO PVC
with no priority and default 5-min fault tolerations. mosquitto can't cluster,
so instead of faking HA this minimizes recovery time and removes the storage
coupling.

- persistence false + drop the Ceph PVC. The db was only ~178 KB of retained
  messages (evcc state, HA discovery, Gen1 Shelly announce/state) — all of
  which clients re-publish on reconnect. Removing it means the broker no
  longer depends on Ceph to start, and reschedules with no RWO volume to
  fence/detach/reattach. Trade-off: a restart briefly empties retained state
  until clients reconnect (self-healing).
- PriorityClass `home-critical` (1e6, below system-cluster-critical) so the
  broker is evicted last and reschedules ahead of ordinary workloads.
- not-ready/unreachable tolerations at 30s (was default 300s) → node-failure
  reschedule in ~30s instead of ~5min.
- terminationGracePeriodSeconds 10 for faster Recreate rollovers.

Single replica + Recreate retained (two brokers would split clients/state).

Apply notes:
- Recreates the pod (brief MQTT reconnect; retained state re-populates).
- The old PVC has prune:disabled, so Flux won't delete it — reclaim manually
  with `kubectl -n home-assistant delete pvc mosquitto-data` once happy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 10:29:42 +02:00
k.renner 88871f6315 Merge pull request 'mosquitto: add DoS/resource guards (broker hardening)' (#128) from feat/mosquitto-dos-guards into master
Reviewed-on: gitea_admin/homelab#128
2026-07-08 10:14:42 +02:00
k.rennerandClaude Opus 4.8 3eeb2aadca mosquitto: add DoS/resource guards (security review, broker hardening)
The broker had no resource limits at all. It's now load-bearing and reachable
by ~30 untrusted IoT devices (the Shelly fleet) + the edge host on a single
256Mi replica, so a misbehaving/compromised client is a real DoS vector.

Add:
- message_size_limit 1048576 — MQTT permits up to 256 MB per publish; one such
  message would OOM the broker. Every legit payload here (HA discovery,
  Shelly/open3e/evcc telemetry) is <10 KB.
- persistent_client_expiration 1d — reap abandoned persistent sessions (the
  edge bridges reconnect within seconds, so they never hit this).
- max_connections 200 per listener — bound a connection flood (~40 clients
  today; generous headroom, not the previous unlimited).

All values chosen well above real usage, so no legit client is affected.

Apply: the ConfigMap has no checksum annotation, so Flux won't restart the pod
on its own — needs `kubectl -n home-assistant rollout restart deploy/mosquitto`
(brief reconnect; clients auto-reconnect) after reconcile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 10:10:52 +02:00
security-bot 6082b73614 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1783496012
Updates managed by image-automation-controller.
2026-07-08 07:43:40 +00:00
k.renner 614b7a90aa Merge pull request 'edge-host: TLS for edge MQTT via a local bridging mosquitto (security review #sec-1, edge-1 leg)' (#127) from feat/edge-mqtt-tls-bridge into master
Reviewed-on: gitea_admin/homelab#127
2026-07-08 09:02:45 +02:00
k.rennerandClaude Opus 4.8 a12116486d edge-host: TLS for edge MQTT via a local bridging mosquitto
Security review #sec-1 (edge-1 leg). open3e/e3oncan/sml2mqtt publish to the
cluster broker in plaintext over the untrusted IoT VLAN, and open3e/e3oncan
have no native MQTTS (their MQTT code does a bare connect()), so "just use
8883" isn't possible for them.

Run a local mosquitto on edge-1 that the clients reach over loopback
(127.0.0.1:1883), bridged to the cluster broker over TLS (mqtt.renner.uno:8883,
public LE cert validated via the system CA bundle). Plaintext stays on
loopback; the wire hop is encrypted. Two bridge connections preserve the
existing cluster identities (open3e user carries open3e/# out + open3e/cmnd
back; sml2mqtt user carries sml2mqtt/# out), so NO cluster-side change is
needed and the existing ACLs already suffice.

Bonus (availability, #avail-4): the local broker buffers telemetry in RAM and
flushes on reconnect, so edge-1 no longer hard-depends on cluster/network
uptime for every publish.

eMMC: the local broker writes NOTHING to flash — persistence false (queue in
RAM) + log_dest stdout (volatile journal -> Loki).

Verified before building: edge-1 -> :8883 TLS validates (return code 0);
mosquitto 2.1.2 in Arch extra; existing open3e/sml2mqtt cluster ACLs cover the
bridge; #125 netpol already allows :8883.

Rollback: point edge_mqtt_host back to mqtt.renner.uno and re-run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 09:01:24 +02:00
k.renner b1c380c825 Merge pull request 'mosquitto: tighten ACLs (shellies + sml2mqtt least-privilege) (security review #sec-3/4)' (#126) from feat/mqtt-acl-least-privilege into master
Reviewed-on: gitea_admin/homelab#126
2026-07-08 08:47:17 +02:00
k.rennerandClaude Opus 4.8 20d59edfcd mosquitto: tighten ACLs to least-privilege for shellies + sml2mqtt
Security review finding #sec-3/#sec-4.

- shellies: `readwrite homeassistant/#` -> `write homeassistant/#`. Shelly Gen2
  firmwares self-publish HA MQTT-discovery configs (so the grant must stay),
  but a publish-only client never needs to READ the whole discovery/state
  tree. Write-only removes device-inventory recon for a compromised Shelly
  while keeping discovery working. (`readwrite shellies/#` stays — devices
  publish state and subscribe to their command topics.)
- sml2mqtt: `readwrite sml2mqtt/#` -> `write sml2mqtt/#`. The bridge is
  publish-only (verified: its config has no subscribe/command topics, only
  LWT + value publishes), so it needs write only.

Left as-is deliberately: open3e keeps `readwrite open3e/#` (it subscribes to
open3e/cmnd in listener mode); homeassistant/admin keep full access; evcc is
already least-privilege. Per-device Shelly credentials were considered and
rejected — Mosquitto's static passwd/acl model can't scope them without a
dynamic-auth plugin, which isn't worth it at this device count.

Applying rolls the (single-replica, Recreate) broker -> a brief MQTT
reconnect. kustomize build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 08:31:34 +02:00
k.renner 442dabc936 Merge pull request 'netpol: scope home-assistant world-ingress to the two LBs (security review #sec-2)' (#125) from feat/mqtt-netpol-scope-world-ingress into master
Reviewed-on: gitea_admin/homelab#125
2026-07-08 08:31:26 +02:00
k.rennerandClaude Opus 4.8 9c525d471f netpol: scope home-assistant world-ingress to the two LBs (mqtt + HA)
The home-assistant default-deny-ingress CNP granted `fromEntities: world`
namespace-wide (endpointSelector:{}), so the untrusted IoT VLAN / any
BGP-routed source could reach EVERY pod in the namespace on every port —
vscode (:8080), zone-configurator (:42069), otbr, whisper, matter-server,
speech-to-phrase, etc. — when only two pods are actually fronted by
world-facing LoadBalancers.

Replace the blanket rule with two endpoint+port-scoped CiliumNetworkPolicies:
  - world -> mosquitto  :1883, :8883
  - world -> home-assistant :8123
Verified against live Services: home-assistant-bgp (targetPort 8123) and
mosquitto-iot (1883/8883) are the only LoadBalancers in the namespace;
matter-server's IoT VIP rides a multus net1 interface that bypasses Cilium
policy entirely, so it is unaffected.

Security review finding #sec-2. Additive-safe (removes a blanket allow, adds
scoped allows covering the same two real ingress paths); revert = restore the
namespace-wide world rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 08:28:03 +02:00
k.renner c3f7d3ca7e Merge pull request 'monitoring: Grafana-Dashboard Heiz-Ladeleistung vs. Außentemperatur' (#124) from feat/heating-loading-dashboard into master
Reviewed-on: gitea_admin/homelab#124
2026-07-08 00:25:18 +02:00
k.rennerandClaude Fable 5 07364e8f84 monitoring: Grafana-Dashboard Heiz-Ladeleistung vs. Außentemperatur
Gegenstück zum WW-Dashboard, für die Puffer-/Estrich-Strategie:
Scatter Heiz-Ladeleistung vs. Außentemp + Zeitreihen (inkl. Puffer-
temperatur) + Ø-Kennzahl, auf Basis sensor.vitocal_heating_loading_power.
Datenquelle Prometheus. Füllt sich ab der ersten Heizperiode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 22:58:36 +02:00
k.renner dcf053cdd3 Merge pull request 'monitoring: Grafana-Dashboard WW-Ladeleistung vs. Außentemperatur' (#123) from feat/dhw-loading-grafana-dashboard into master
Reviewed-on: gitea_admin/homelab#123
2026-07-07 18:48:15 +02:00
k.rennerandClaude Fable 5 b35876518e monitoring: Grafana-Dashboard WW-Ladeleistung vs. Außentemperatur
Neues Dashboard heatpump-dhw-loading (uid heatpump-dhw-loading):
Scatter WP-Ladeleistung vs. Außentemperatur + Zeitreihen + Ø-Kennzahl,
auf Basis von sensor.vitocal_dhw_loading_power (nur während WW-Ladung
verfügbar) und der Außentemperatur. Datenquelle Prometheus (HA-
prometheus-Integration; diese HA schreibt nicht nach InfluxDB).
Gegenstück zum HA-Sensor-PR im homeassistant-Repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:34:25 +02:00
security-bot ac146ad448 chore(image-automation): bump ghcr.io/immich-app/immich-machine-learning:v3.0.1
Updates managed by image-automation-controller.
2026-07-07 10:36:47 +00:00
security-bot ef7263b999 chore(image-automation): bump ghcr.io/immich-app/immich-machine-learning:v2.7.5
Updates managed by image-automation-controller.
2026-07-07 10:36:46 +00:00
k.renner 1fcc2922ba Merge pull request 'immich: Update auf v3.0.1 (Image-Policy 2.x -> 3.x)' (#122) from feat/immich-v3 into master
Reviewed-on: gitea_admin/homelab#122
2026-07-07 12:36:14 +02:00
k.rennerandClaude Fable 5 53e682fbdd immich: Update auf v3.0.1 (Image-Policy 2.x -> 3.x)
Immich hat am 2026-07-02 v3.0.0/v3.0.1 released; die ImagePolicy war
bewusst auf semver 2.x gegated, daher blieb v2.7.5 stehen.

Preflight fuer den Major-Bump:
- pgvecto.rs-Removal betrifft uns nicht: DB_VECTOR_EXTENSION unset,
  v2.7.5 laeuft bereits auf VectorChord (dual-extension CNPG-Image)
- keine der entfernten ML-Env-Vars (MACHINE_LEARNING_PRELOAD__*,
  IMMICH_MACHINE_LEARNING_PING_TIMEOUT) in den Manifests
- oauth.issuerUrl ist valide HTTPS-URL (strengere v3-Validierung ok)
- Gatus probt /api/server/ping — nicht auf der Removed-Endpoints-Liste

Policy-Gate wandert auf 3.x; 4.x-Cutover braucht wieder manuellen Review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:32:54 +02:00
k.renner c4ae949bc0 Merge pull request 'edge-host: fix settings-backup terminator check (trailing newline)' (#121) from fix/edge-zwave-settings-backup-newline into master
Reviewed-on: gitea_admin/homelab#121
2026-07-07 11:52:54 +02:00
k.rennerandClaude Opus 4.8 f6f9d550bc edge-host: fix settings-backup terminator check (trailing newline)
The zwave-js settings backup guard in #120 checked `tail -c1 == "}"`, but
settings.json ends with "}\n" — so the last byte is a newline and the guard
always skipped ("not }-terminated"), producing no backup. Strip trailing
whitespace before the check so a valid, newline-terminated file is backed up
while a genuinely truncated one is still rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:39:07 +02:00
k.renner c2f1aad8f7 Merge pull request 'edge-host: batch zwave-js fsync via libeatmydata (+ lazytime) to spare the eMMC' (#120) from feat/edge-zwave-fsync-batching into master
Reviewed-on: gitea_admin/homelab#120
2026-07-07 11:30:37 +02:00
k.rennerandClaude Opus 4.8 9add6f1a56 edge-host: batch zwave-js fsync via libeatmydata (+ lazytime) to spare eMMC
With logs off flash, the dominant remaining eMMC writer is zwave-js-ui: its
value DB is fsync'd on every value change (jsonl-db; zwave-js-ui doesn't
expose the driver throttle). On a busy network that fsync storm churned
~2.6 GiB/day for ~0.1 KiB/s of real data — near-pure write amplification
(each fsync forces a block write + ext4 journal commit).

Preload libeatmydata into the zwave-js container (musl .so extracted from the
image's Alpine base, mounted ro, LD_PRELOAD) so fsync/fdatasync become no-ops
and the kernel batches writes via normal writeback. The WS/MQTT value stream
to HA and the on-disk cache are unchanged — full data resolution, no
re-interview on restart. No ext4 mount option can do this (fsync is a
durability contract; commit=/data=writeback/nobarrier don't batch it, and the
only mount-level alternative — tmpfs — makes the whole store volatile).

Also:
- noatime -> noatime,lazytime on root: batches the inode-timestamp writeback
  (e.g. the zwave-js lockfile heartbeat) without weakening data durability.
- edge-zwave-settings-backup.timer: hourly validated rolling backup of
  settings.json (Z-Wave network keys), since neutralized fsync means a
  power-loss mid settings-write could truncate it. Value-cache loss on crash
  is harmless (devices re-report; jsonl-db tolerates a partial trailing line).

Verify the drop on the eMMC-write monitor; rollback = drop the LD_PRELOAD env
+ .so mount from the compose file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:26:46 +02:00
k.renner 78368ea4ea Merge pull request 'authentik: Passkey-only Login für Audiobookshelf und Immich' (#119) from feat/authentik-passkey-abs-immich into master
Reviewed-on: gitea_admin/homelab#119
2026-07-07 10:38:22 +02:00
k.rennerandClaude Fable 5 eac9a6cd6f authentik: Passkey-only Login auch für Gitea und Grafana
Beide Provider (nur k.renner via Infrastructure Admins, 4 Passkeys
enrolled) an den Passwordless- und Step-up-Flow gebunden; Live-Stand
der Pflichtfelder gespiegelt. PVE, PBS und paperless-gate bleiben
bewusst auf Passwort+MFA.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:32:05 +02:00
k.rennerandClaude Fable 5 5ba5c3108a authentik: Passkey-only Login auch für Audiobookshelf und Immich
Bindet die OAuth2-Provider Audiobookshelf und immich-oauth-provider
an den Passwordless-Authentication-Flow (Passkey-Autofill inklusive)
und den Step-up-Authorization-Flow — gleiche Mechanik wie Home
Assistant und NetBird (#115/#118). Pflichtfelder (invalidation_flow,
client_type, redirect_uris) spiegeln den Live-Stand vom 2026-07-07.

Nutzerabdeckung geprüft: Audiobookshelf Users (k.renner, s.renner)
haben Passkeys; Immich Users enthält g.renner ohne Passkey — sie
bekommt beim ersten Zugriff das Inline-Enrollment über die Step-up-
Stage (configure), kein Lockout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:26:36 +02:00
k.renner 5698990a78 Merge pull request 'authentik: Passkey-Autofill — Login ohne Username-Tippen' (#118) from feat/authentik-passkey-autofill into master
Reviewed-on: gitea_admin/homelab#118
2026-07-07 10:16:54 +02:00
k.rennerandClaude Fable 5 fb6ac12cbe authentik: Passkey-Autofill im Passwordless-Flow (kein Username-Tippen)
Setzt webauthn_stage auf der Identification-Stage des Passwordless-
Flows (Conditional WebAuthn, authentik >= 2025.12): der Browser bietet
enrollte Passkeys direkt im Autofill des Username-Felds an — ein Tap,
kein Username. Username-Tippen bleibt als Fallback für nicht-
discoverable Credentials erhalten.

last_auth_threshold der Passwordless-Validation-Stage von seconds=0
auf minutes=5: verhindert Doppel-Challenge derselben Flow-Ausführung
nach Autofill-Validierung; der Skip ist session-gebunden.

Validation-Stage-Entry vor den Identification-Entry gezogen, weil
!KeyOf nur rückwärts auflöst.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:01:37 +02:00
k.renner 3c60256a88 Merge pull request 'edge-host: correct N100/NVMe doc drift (deployed unit is a Wyse 3040 on eMMC)' (#117) from fix/edge-host-readme-hardware into master
Reviewed-on: gitea_admin/homelab#117
2026-07-07 09:30:56 +02:00
k.rennerandClaude Opus 4.8 2a6c003c90 edge-host: correct N100/NVMe drift — deployed unit is a Wyse 3040 on eMMC
The role README, install play, and a defaults comment described edge-1 as a
"bare-metal Arch N100" partitioning "the NVMe". The deployed hardware is a
Dell Wyse 3040 (Atom x5-Z8350, 2 GB RAM) on a 7.3 GB internal eMMC. Correct
the docs to reflect reality, note the actual hardware up front, and keep the
installer's dual NVMe/eMMC autodetect accurately described. Docs/comments
only — no runtime change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:25:40 +02:00
k.renner 1d5fdce85d Merge pull request 'authentik: Passkey-only Login für Home Assistant und NetBird' (#115) from feat/authentik-passkey-stepup into master
Reviewed-on: gitea_admin/homelab#115
2026-07-07 09:14:15 +02:00
k.renner eef50e8872 Merge pull request 'edge-host: fix edge-alloy ExecStart (Arch binary is grafana-alloy)' (#116) from fix/edge-alloy-binary-path into master
Reviewed-on: gitea_admin/homelab#116
2026-07-07 09:10:31 +02:00
k.rennerandClaude Fable 5 e9e5d8a82c authentik: Passwordless-Passkey-Login für Home Assistant und NetBird
Ergänzt den Step-up um den eigentlichen Kern: einen passwortlosen
Authentication-Flow (Identification ohne Password-Stage → WebAuthn →
Login), per authentication_flow an beide Provider gebunden. Frische
Logins in HA/NetBird zeigen damit kein Passwortfeld mehr; der
Authentik-eigene Login (Default-Brand-Flow) bleibt unverändert.

Sicherheitsdetail: die Validation-Stage im Authentication-Flow ist
eine SEPARATE Stage mit not_configured_action: deny — `configure`
wäre hier eine Account-Übernahme (User ist nur identifiziert, nicht
authentifiziert). Inline-Enrollment bleibt exklusiv im Authorization-
Step-up mit require_authenticated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 09:08:54 +02:00
k.rennerandClaude Opus 4.8 842b60c5b2 edge-host: fix edge-alloy ExecStart — Arch binary is grafana-alloy
PR #114 pointed the edge-alloy.service unit at /usr/bin/alloy, but the Arch
`grafana-alloy` package installs the binary as /usr/bin/grafana-alloy. The
unit crash-looped with status=203/EXEC ("Unable to locate executable
/usr/bin/alloy"). Make the path a variable (edge_alloy_bin) and default it to
the real path. Verified on edge-1: the binary runs (v1.13.2), the alloy user
exists (in systemd-journal), and the deployed config parses cleanly — only
the ExecStart path was wrong.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:08:00 +02:00
k.renner 04460bb97d Merge pull request 'edge-1: keep logs off the eMMC (volatile journal + Alloy→Loki) + flash tuning' (#114) from feat/edge-1-emmc-wear-mitigation into master
Reviewed-on: gitea_admin/homelab#114
2026-07-07 08:55:16 +02:00
k.rennerandClaude Fable 5 3ff1b472d7 authentik: Passkey-Step-up-Flow für Home Assistant und NetBird
Neuer Authorization-Flow `passkey-stepup-authorization` mit einer
WebAuthn-only Authenticator-Validation-Stage, per Blueprint auf die
OAuth2-Provider von Home Assistant und NetBird gebunden. Login in
diese Apps erfordert damit einen Passkey — auch mit bestehender
SSO-Session aus einem Passwort-Login.

- not_configured_action: configure — g.renner (HA-Zugriff, kein
  Passkey enrolled) wird beim ersten Login zur Enrollment geführt
  statt ausgesperrt
- last_auth_threshold hours=8 — kein Doppel-Touch direkt nach
  Passkey-Login; NetBirds /silent-auth (prompt=none) bleibt innerhalb
  des Fensters funktionsfähig
- Provider-Pflichtfelder (invalidation_flow, redirect_uris,
  client_type) spiegeln den Live-Stand vom 2026-07-07; ab jetzt
  besitzt Git diese Felder

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 08:54:30 +02:00
k.rennerandClaude Opus 4.8 3ae01e9f6d edge-1: keep logs off the eMMC (volatile journal + Alloy->Loki) + flash tuning
edge-1 is a Dell Wyse 3040 on a 7.3 GB internal eMMC (not the N100/NVMe the
README assumed). Unbounded docker json logs (a single zwave-js log had grown
to 214 MB) plus a persistent journald were churning the flash. Move all
logging off local flash and ship it to Loki instead:

- journald Storage=volatile (RAM), size-capped; purge the on-disk store
- docker json-file -> journald log driver (no container json logs on disk)
- Grafana Alloy (Arch pkg) tails the RAM journal -> Loki, WAL on /run tmpfs

Loki endpoint: the unauthenticated srv-network Loki is firewalled off the IoT
VLAN, so Alloy pushes to the authenticated cluster ingress
(https://loki.renner.uno) with a dedicated 'edge-1' basic-auth user — its
htpasswd line is added to the loki-basic-auth cluster secret, plaintext in the
edge_host ansible vault. Delivery activates once Flux reconciles that secret;
until then Alloy 401s and buffers in RAM.

Flash longevity: noatime on root, weekly fstrim.timer, and a weekly
edge-emmc-health check of the JEDEC life-time / pre-EOL estimators.

The Alloy config is purpose-built in the edge-host role rather than reusing
the shared monitoring-agent role: that role is Debian-oriented (apt + GitHub
binary), force-enables a docker log source that would double-ship against the
journald driver, and always configures a Prometheus remote-write whose
endpoint is unreachable from the IoT VLAN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:49:07 +02:00
k.renner e2c48bfa3a Merge pull request 'ansible: Hostkey-Prüfung wieder aktivieren' (#113) from fix/enable-host-key-checking into master
Reviewed-on: gitea_admin/homelab#113
2026-07-06 08:38:11 +02:00
k.rennerandClaude Fable 5 776722d95c ansible: Hostkey-Prüfung wieder aktivieren
host_key_checking=False entfernt (zurück auf Default = aktiv) und im
Kommentar festgehalten, warum das nie wieder abgeschaltet werden darf:
Der DNS-Drift-Vorfall vom 2026-07-06 (edge-1.iot.renner.uno zeigte per
public Wildcard auf den Hetzner-Proxy) wurde nur deshalb nicht vom
Hostkey-Wechsel-Alarm gestoppt, weil diese Einstellung ihn unterdrückt
hat — ein Produktions-Play lief dadurch gegen die falsche Maschine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:35:14 +02:00
k.renner 0205eb5abd Merge pull request 'edge-host: SmartGrid-/EMS-Status-DIDs beobachten (543/2350/2543/2560)' (#112) from feat/edge-open3e-sg-ems-observability into master
Reviewed-on: gitea_admin/homelab#112
2026-07-06 08:17:00 +02:00
k.rennerandClaude Fable 5 6c30830e76 edge-host: SmartGrid-/EMS-Status-DIDs ins open3e-Poll-Set
vitocal.543 (SmartGridReadyConsolidator), 2350 (EMS ResultingControl-
State), 2560 (SmartGridFeatureSelection) und 2543 (SmartGrid-Offsets)
— alle read-only. Zweck: beobachten, ob die Eigenstromoptimierung
(EMS) bei PV-Überschuss von selbst den WW-Boost auslöst; der Offset
2543.2 steht laut DID-Scan vom 2026-07-02 bereits auf +10 K. Klärt
Option B (natives EMS) vor SG-Ready-Verdrahtung (Option A) und ohne
CAN-Writes (Serviceanleitung 6200041: EMS und SG-Kontakte bedienen
dieselben 2543er-Parameter; SG-/EMS-Zustand ist auf der DID-Ebene nur
als ro-Status sichtbar).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:15:10 +02:00
k.renner fe349afe48 Merge pull request 'edge-host: poll vx3.266 ErrorDtcHistory for meter-fault monitoring' (#111) from feat/open3e-poll-errordtchistory into master
Reviewed-on: gitea_admin/homelab#111
2026-07-06 07:04:17 +02:00
k.rennerandClaude Opus 4.8 1b1a9708f2 edge-host: poll vx3.266 ErrorDtcHistory for meter-fault monitoring
Adds DID 266 (ErrorDtcHistory, O3EList) for the VX3 to the open3e poll
set so the FailureCommunicationEnergyMeter fault — the E380 energy-meter
link the internal PV-surplus EMS relies on — is continuously visible in
MQTT/HA, catching recurrences beyond the transient 2026-07-02 event.

Decodes via the master O3EList codec inherited through the vx3plus
overlay (the vx3 base list keeps 266). Read-only; applied via the
edge-host ansible pipeline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 10:56:43 +02:00
k.renner f28244f4ae Merge pull request 'evcc: move grid-price topic out of evcc/# to energy/tariff/grid' (#110) from feat/evcc-tariff-topic-move into master
Reviewed-on: gitea_admin/homelab#110
2026-07-04 22:08:17 +02:00
k.rennerandClaude Opus 4.8 1723b51205 evcc: move grid-price topic out of evcc/# to energy/tariff/grid
The grid price was published to evcc/tariff/gridprice, under evcc's own
retained-topic namespace — evcc manages evcc/# and wiped the price
(observed: retained value cleared with no broker/evcc restart), which
would hang evcc on its next restart until the HA automation republishes.

Move it to a neutral topic evcc only READS: grant the evcc Mosquitto user
'read energy/#', and point the grid tariff at energy/tariff/grid. The HA
automation is repointed in the homeassistant repo.

Note: applying this restarts the Mosquitto pod once (ACL change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 20:58:36 +02:00
k.renner 474bdffc4f Merge pull request 'evcc: tariffs (grid from HA price, fixed feed-in) + solar forecast' (#109) from feat/evcc-tariffs-forecast into master
Reviewed-on: gitea_admin/homelab#109
2026-07-04 20:05:20 +02:00
k.rennerandClaude Opus 4.8 9be7b7bc96 evcc: prepared (inactive) SG-Ready heating loadpoint for the Vitocal
Adds a commented, documented heating-device block to evcc.yaml: an
sgready-relay charger + heating loadpoint (surplus-threshold driven) plus
a heat-pump power meter (open3e DID 2488). Left INACTIVE so evcc doesn't
try to reach a relay that isn't wired yet. To activate: wire a Shelly to
the Vitocal SG-Ready terminals (143.1/143.4/143.5), enable SG in the
service menu, set host, uncomment. evcc drives boost/normal via SG-Ready;
the heat pump firmware owns the smart-grid logic.

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