Files
supervisor/supervisor
Stefan AgnerandClaude Fable 5.1 a806e32406 Release Core's port reservation from every Core start path
In #7189 a manual POST /core/start during app boot produced a Core that
could not bind its own port: the boot flow in Core.start() still held the
transient systemd socket reserving it, and only that flow knew how to
release it. #7194 closed the API vector by rejecting such calls while
Supervisor is not RUNNING, but the guarantee then rests on the API gate
rather than on the reservation itself -- any new or ungated path that
starts Core reopens the race.

Move the reservation onto HomeAssistantCore, which owns the container it
protects. reserve_port() is called by the boot flow as before;
release_port() is now also called by HomeAssistantCore.start() and
restart() right before the container is started, so whichever path gets
to Core first gives the port back. rebuild(), update() and the watchdog
all funnel through those two. release_port() is a no-op unless a
reservation is actually held, so the common case adds no D-Bus traffic.

The boot flow keeps its explicit release after the services phase so
application-phase apps see the same behavior as before when Core boot is
disabled, and its finally block still acts as the backstop.

Tests for the systemd interaction move alongside the code to
tests/homeassistant/test_core.py; the boot-ordering tests in
tests/test_core.py stay and are adapted to the new API. New tests cover
start() and restart() releasing a held reservation before the container
is touched, start() with no reservation not touching systemd, and start()
proceeding when the release cannot be confirmed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 14:57:41 +02:00
..