feat(otbr): run an own Thread network on the SLZB-06P7 #1606

Merged
k.renner merged 1 commits from feat/otbr-own-thread-network into main 2026-11-08 09:35:17 +00:00
6 changed files with 34 additions and 51 deletions
+27 -21
View File
@@ -1,14 +1,18 @@
---
# One-shot seed Job. Idempotent — exits 0 immediately if the OTBR pod
# already has an active dataset configured. On a clean install (PVC
# empty, otbr-agent loaded with --auto-attach=0 so it boots into the
# `disabled` state without crashing), it runs the proven bring-up
# sequence:
# One-shot Job that forms otbr's own Thread network. Idempotent — exits 0
# immediately if the OTBR pod already has an active dataset. On a clean
# install (PVC empty, otbr-agent loaded with --auto-attach=0 so it boots
# into the `disabled` state without crashing), it runs:
# 1. ot-ctl ifconfig up (enables PHY on the radio)
# 2. ot-ctl dataset set active <hex> (commits dataset; SetMacKey
# now succeeds because the radio is enabled)
# 3. ot-ctl thread start (MLE attaches; state goes detached → child
# → router → leader)
# 2. ot-ctl dataset init new, channel, networkname, commit active
# (random network key, PAN ID, extended PAN ID and mesh-local
# prefix; SetMacKey succeeds because the radio is enabled)
# 3. ot-ctl thread start (MLE attaches; state goes detached → leader)
#
# Channel 15 sits between Wi-Fi channels 1 and 6, clear of the 2.4 GHz
# APs (channels 1, 6, 11) and of Zigbee on channel 25. The dataset,
# including the network key, lives only on the otbr-thread PVC and in
# Home Assistant's thread.datasets (read over the OTBR REST API).
#
# This sequence is the workaround for SLZB-06P7 firmware behavior where
# `dataset set active` triggers an immediate KeyManager push that fails
@@ -76,11 +80,10 @@ spec:
# (mismatched skew can break exec streaming).
image: docker.io/alpine/kubectl:1.37.2@sha256:5e2de75d37e811ba6eb92e06890b1b3263eae4f0a1c17b49269edec589b1352a
env:
- name: DATASET_TLV
valueFrom:
secretKeyRef:
name: otbr-active-dataset
key: active-dataset.tlv
- name: THREAD_CHANNEL
value: "15"
- name: THREAD_NETWORK_NAME
value: "Suedstr28-Thread"
command:
- sh
- -c
@@ -89,8 +92,8 @@ spec:
NS=home-assistant
SEL=app.kubernetes.io/name=otbr
# otbr is disabled (replicas: 0, see deployment.yaml). With
# the force annotation, every image bump re-creates this Job;
# With otbr scaled to 0 there is no pod to seed. With the
# force annotation, every image bump re-creates this Job;
# exit 0 instead of waiting for a pod that never comes.
replicas=$(kubectl -n "$NS" get deployment otbr \
-o jsonpath='{.spec.replicas}')
@@ -132,16 +135,19 @@ spec:
exit 0
fi
echo "no active dataset; seeding..."
echo "no active dataset; forming ${THREAD_NETWORK_NAME} on channel ${THREAD_CHANNEL}..."
# Order matters: ifconfig up first so the radio's PHY is
# ENABLED before dataset commit triggers SetMacKey. Without
# this, SetMacKey returns INVALID_STATE and otbr-agent
# fatal-exits.
kubectl -n "$NS" exec "$pod" -c otbr -- ot-ctl ifconfig up
kubectl -n "$NS" exec "$pod" -c otbr -- \
ot-ctl dataset set active "$DATASET_TLV"
kubectl -n "$NS" exec "$pod" -c otbr -- ot-ctl thread start
ot() { kubectl -n "$NS" exec "$pod" -c otbr -- ot-ctl "$@"; }
ot ifconfig up
ot dataset init new
ot dataset channel "$THREAD_CHANNEL"
ot dataset networkname "$THREAD_NETWORK_NAME"
ot dataset commit active
ot thread start
# Verify Thread came up.
for i in $(seq 1 30); do
+6 -11
View File
@@ -5,16 +5,11 @@ metadata:
labels:
app.kubernetes.io/name: otbr
spec:
# DISABLED (replicas: 0). Running otbr broke Matter: its radio is RF-isolated
# enough from the Nest mesh that it forms a rival partition of the same
# network, and the two bridge only over Ethernet — fast enough for a ping,
# too slow for the fragmented CASE handshake, so devices stay discoverable
# while every commissioning attempt times out.
#
# The instability is at the Thread partition layer, not in the config below.
# Re-enable only once co-BR integration is verified to MERGE: one partition,
# one OMR prefix, and the Nest routers visible in otbr's router table.
replicas: 0
# otbr runs its own Thread network, formed by otbr-dataset-bootstrap. It is
# not a co-BR of the Nest's NEST-PAN-339C: its radio does not hear that mesh
# well enough to merge partitions, and partitions bridged only over
# Ethernet are too slow for Matter's CASE handshake.
replicas: 1
strategy:
type: Recreate
selector:
@@ -344,4 +339,4 @@ spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: otbr-data
claimName: otbr-thread
-15
View File
@@ -1,15 +0,0 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: otbr-active-dataset
namespace: home-assistant
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: openbao
target:
name: otbr-active-dataset # keeps the old Secret's name — consumers unchanged
dataFrom:
- extract:
key: apps/otbr
-1
View File
@@ -3,7 +3,6 @@ kind: Kustomization
namespace: home-assistant
resources:
- dataset-bootstrap.yaml
- externalsecret.yaml
- deployment.yaml
- network-attachment.yaml
- pvc.yaml
+1 -1
View File
@@ -9,7 +9,7 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: otbr-data
name: otbr-thread
spec:
accessModes:
- ReadWriteOnce
@@ -91,8 +91,6 @@ spec:
name: flux-system
path: ./kubernetes/apps/otbr
prune: true
# Not in use.
suspend: true
wait: true
# decryption block dropped 2026-07-13: path is sops-free — secrets come
# from OpenBao via ESO (MIGRATION-sops-openbao.md).