Provisioning a Shelly over BLE fails at the GATT connect and the cause is
inside the ESP's BLE stack — invisible from shelly-manager's side. To
diagnose and iterate without a USB re-flash per change:
* ota: (esphome platform) so future config changes push over WiFi from the
ESPHome Device Builder. Bootstrapping costs one USB flash; set
shelly_ble_proxy_ota_password in the dashboard secrets first.
* logger DEBUG with esp32_ble_client=VERBOSE / esp32_ble_tracker,
bluetooth_proxy=DEBUG, so a connect attempt logs its progress and the
failure reason.
Takes effect on the (one) USB re-flash; thereafter iterate remotely.
Co-Authored-By: Claude Bot <claude-bot@renner.uno>
openbao.k.renner.uno -> openbao.renner.uno. The k. subdomain is a
migration-era artifact, never a production convention, and is banned
per owner directive 2026-07-14 — the wiki note that claimed a
dual-hostname convention was wrong and is being corrected.
Non-public reachability is a real control, not a hostname: the public
edge serves only explicitly configured SNI (openbao is not configured),
and the new CiliumNetworkPolicy default-denies the namespace with
explicit grants (traefik->8200, ESO->8200, intra-ns for the snapshot
CronJob) matching the <app>-ns.yaml pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
The renner.uno fallthrough forwarder still pointed at the Proxmox DNS HA VIP
10.2.40.53, which is decommissioned/unreachable. Every non-Ingress renner.uno
name (device/service hostnames like udm.renner.uno) therefore dead-ended in
SERVFAIL/timeout. A routine home-assistant pod restart on 2026-07-14 forced HA
to re-resolve its device hostnames and left ~1374 entities unavailable / ~30
config entries stuck (ZHA, Shelly, UniFi, Bluetooth, cloud integrations).
Point the fallthrough at the UDM (10.2.40.1 / fd00::1), which serves the
split-horizon renner.uno zone (verified: it answers udm.renner.uno). Also
scrub the stale 10.2.40.53 references in the CoreDNS comment, bootstrap.sh
DNS_SERVER, and the Gatus monitor (which was pointlessly probing the dead VIP).
A live hotfix has already been applied to the k8s-gateway ConfigMap to restore
resolution; this makes it durable so Flux does not revert it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwcQMntG9y9XRkNMPDq2fe
bao operator raft snapshot save nightly at 03:00 to a dedicated PVC
(keep 7), authenticated via a new k8s-auth role openbao-snapshot ->
policy raft-snapshot (read on sys/storage/raft/snapshot only; applied
2026-07-14). Velero openbao-daily (06:15) ships ONLY the snapshot PVC
offsite via label selector — the live raft data PVC stays unbacked
(crash-consistent files; the .snap is the restore artifact). A failed
Job doubles as the sealed-after-restart alarm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
shelly-manager now auto-discovers esphome bluetooth_proxy nodes on the IoT
VLAN via mDNS (_esphomelib._tcp) — see k.renner/shelly-manager#14. Replace
the static Ble__Proxies__0__{Name,Host,Port} env (which addressed the proxy
by shelly-ble-proxy-1.iot.renner.uno — poisoned over IPv6 by the
*.renner.uno wildcard) with a single shared fleet Noise key exposed as
Ble__Discovery__Key, from the same shelly-ble-proxy-key secret.
Merge after #14's image is live so discovery is present when the static
entry goes away.
Co-Authored-By: Claude Bot <claude-bot@renner.uno>
authentik-secrets + samba-bridge config: values in OpenBao KV, both
round-trip-verified text. The identity provider was deliberately last —
every other consumer proved the pipeline first. ZERO sops files remain
under kubernetes/; authentik KS decryption dropped. Phase 4 (age-key
retirement) is now unblocked pending soak.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
seed/{authentik,immich,paperless} barman+hetzner creds and
seed/influxdb bootstrap: values in OpenBao KV (homelab/seed/*), all
round-trip-verified text. Future cold rebuilds no longer need the age
key for the seed phase. Decryption blocks dropped from every seed-* KS
(incl. dormant seed-audiobookshelf/seed-music-assistant and the
suspended, deprecated seed KS).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
home-assistant (5), immich, paperless (2): values in OpenBao KV, all
verified text-safe via base64 round-trip before load. ExternalSecrets
keep the old Secret names. Decryption blocks dropped from
home-assistant/immich/paperless KS plus the dormant one on
speech-to-phrase.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
evcc, otbr, libation, external-dns (apps), monitoring (4), woodpecker:
values in OpenBao KV (homelab/apps/*, loaded 2026-07-13), manifests are
now ExternalSecrets keeping the old Secret names. libation's payload is
a binary SQLite db — stored base64 in KV with decodingStrategy: Base64
on the ES. Decryption blocks dropped from apps/evcc/otbr/libation/
monitoring KS (paths sops-free) plus the dormant one on grafana-db.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
external-dns-unifi, netbird-ingress, velero (both), cloudflare-api-token:
values live in OpenBao KV (homelab/infrastructure/*, loaded 2026-07-13),
manifests become ExternalSecrets targeting the same Secret names so
consumers see no change. Both infra KS paths are now sops-free, so their
decryption blocks go too. Rollback per file = git revert (age key still
active until phase 4).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6
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
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>
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
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>
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
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
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
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
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>
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>