fix(esphome): bind the remote-build peer-link to loopback; finish the rsync fix

ha-dev asked for the Device Builder 1.0.0 remote-build receiver to be turned
off: one instance, builds run locally, so the feature has no role, and it was
binding 0.0.0.0:6055 with mDNS advertisement on a privileged host-network
container that writes firmware to devices.

Reading the source first changed the framing. controllers/remote_build/
_state.py declares 'remote_builds_enabled: bool = True', so nobody enabled it
— it arrived on by default with the rewrite. And the flag has no on-disk
representation until it is changed: neither .device-builder.json nor
.device-builder-preferences.json carries it, and the only writer is the app's
own command API behind the UI. Setting it from a playbook would mean inventing
a schema for a model I have not read.

So this binds ESPHOME_REMOTE_BUILD_HOST=127.0.0.1 — a documented env var, no
entrypoint override — which removes the LAN reachability now and is verifiable
(ss reports 127.0.0.1:6055, was 0.0.0.0:6055). It is explicitly NOT the off
switch ha-dev asked for and the compose comment says so; the Settings toggle
is one UI click and the line can go once someone flips it.

Also completes yesterday's deploy-stack.sh fix, which was half a fix. --no-o
--no-g stopped rsync chgrp-ing a root:docker destination as a non-root
identity, but the very next deploy failed the same way one layer along —
'failed to set times on ...' — because a non-root identity cannot utime() a
root-owned directory either. Same exit 23 after a successful transfer. Added
--omit-dir-times. Fixing only the group half looked fixed until the next run,
which is the whole reason this is worth a line in the script's comment.
This commit is contained in:
2026-09-14 18:55:54 -07:00
parent d1769ed114
commit 8073a6aed9
2 changed files with 24 additions and 3 deletions
+7 -3
View File
@@ -163,13 +163,17 @@ for entry in "${PAIRS[@]}"; do
tmp_out=$(mktemp) tmp_err=$(mktemp)
rc=0
# --no-o --no-g: the deploy tree is root:docker 2775 (setgid) since the
# --no-o --no-g --omit-dir-times: the deploy tree is root:docker 2775 (setgid) since the
# 2026-09-14 fleet normalisation, and plain -a makes rsync try to chgrp the
# destination as the deploy identity, which is not root. It fails with
# "chgrp ... Operation not permitted" and exits 23 AFTER transferring the
# content — a loud error on a deploy that actually succeeded. The setgid bit
# already assigns the right group, so rsync should not be fighting it.
rsync -az --no-o --no-g --delete --dry-run \
# --omit-dir-times is the same problem one layer along: a non-root identity
# cannot utime() a root-owned directory either, which produced the identical
# "exit 23 after a successful transfer" on the next deploy. Both halves are
# needed; fixing only the group half looks fixed until the next run.
rsync -az --no-o --no-g --omit-dir-times --delete --dry-run \
"${RSYNC_REMOTE[@]}" \
--out-format='%i %n' \
"${EXCLUDES[@]}" "${extra[@]}" \
@@ -291,7 +295,7 @@ for entry in "${PAIRS[@]}"; do
extra=()
[ "$kind" = compose ] && extra+=(--exclude='conf/')
printf 'pushing %s → %s\n' "$src" "$dest"
rsync -az --no-o --no-g --delete \
rsync -az --no-o --no-g --omit-dir-times --delete \
"${RSYNC_REMOTE[@]}" \
"${EXCLUDES[@]}" "${extra[@]}" \
"$src" "$dest"
+17
View File
@@ -39,6 +39,23 @@ services:
# the 2025.8.2 -> 2026.8.2 upgrade, 2026-09-14.
- ESPHOME_USERNAME=${ESPHOME_USERNAME}
- ESPHOME_PASSWORD=${ESPHOME_PASSWORD}
#
# Device Builder 1.0.0 added a remote-build peer-link receiver that binds
# 0.0.0.0:6055 and advertises itself over mDNS as _esphomebuilder._tcp.
# It is ON BY DEFAULT — controllers/remote_build/_state.py declares
# `remote_builds_enabled: bool = True` — so nobody enabled it; it arrived
# with the rewrite. There is exactly one Device Builder here and builds
# run locally in this container, so the feature has no role: it is
# unrequested LAN reachability on a privileged host-network container
# whose job is writing firmware to devices.
#
# ⚠ This binds it to loopback; it does NOT disable it. The real off
# switch is a Settings toggle in the UI, and its enabled flag has no
# on-disk representation until it is changed, so there is nothing to set
# from a playbook without inventing a schema. Flip it in Settings and
# this line can go. (ha-dev's stated preference, correctly, is not
# enabling a feature over restricting its bind address.)
- ESPHOME_REMOTE_BUILD_HOST=127.0.0.1
labels:
- homepage.group=Apps
- homepage.name=ESPHome