Skip to content

Use Unix socket for Supervisor to Core communication#6590

Open
agners wants to merge 4 commits intomainfrom
use-unix-socket-for-core-api
Open

Use Unix socket for Supervisor to Core communication#6590
agners wants to merge 4 commits intomainfrom
use-unix-socket-for-core-api

Conversation

@agners
Copy link
Copy Markdown
Member

@agners agners commented Feb 23, 2026

Proposed change

Switch internal Supervisor→Core HTTP and WebSocket communication from TCP (port 8123) to a Unix domain socket when the installed Core version supports it.

The existing /run/supervisor directory on the host (already mounted at /run/os inside the Supervisor container) is bind-mounted into the Core container as /run/supervisor. Core receives the SUPERVISOR_CORE_API_SOCKET environment variable with the socket path, creates the socket there, and Supervisor connects to it via aiohttp.UnixConnector at /run/os/core.sock.

Since the Unix socket is only reachable by processes on the same host, requests arriving over it are implicitly trusted and authenticated as the existing "Supervisor" system user. This removes the current token round-trip where Core creates a refresh token, hands it to Supervisor, and Supervisor sends it back as a Bearer token on every Core API call. WebSocket connections are likewise authenticated implicitly, skipping the auth_required/auth handshake.

This reduces attack surface by removing the need for network-based communication between Supervisor and Core, avoids potential port conflicts or http.server_host configuration issues, and removes authentication overhead for internal IPC.

Key design decisions:

  • Session, URL, and transport management lives in HomeAssistantAPI as public properties (session, api_url, ws_url) used by WebSocket and proxy code
  • Version-gated with CORE_UNIX_SOCKET_MIN_VERSION so older Core versions continue using TCP transparently
  • LANDINGPAGE version is explicitly excluded from the version comparison (it's not CalVer)
  • Hard-fails with a clear error if the Unix socket file is unexpectedly missing when expected
  • WSClient.connect() for Unix socket (no auth) and WSClient.connect_with_auth() for TCP (token auth) cleanly separate the two connection modes
  • Token refresh always uses the TCP websession since it is inherently a TCP/Bearer-auth operation
  • Proxy _websocket_client reuses WSClient instead of duplicating the WebSocket auth handshake
  • Logs which transport (Unix socket vs TCP) is being used on first request
  • Reuses the existing /run/os/run/supervisor host mount rather than creating new paths

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality to the supervisor)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Ruff (ruff format supervisor tests)
  • Tests have been added to verify that the new code works.

If API endpoints or add-on configuration are added/changed:

@agners agners added the new-feature A new feature label Feb 23, 2026
@agners agners marked this pull request as draft February 23, 2026 23:22
@mdegat01
Copy link
Copy Markdown
Contributor

mdegat01 commented Mar 4, 2026

What's this waiting on btw @agners ? It looks like both PRs are in draft and there's a good number of comments on the other. Is this dependent on something else in the epic or is it just a time issue?

@agners
Copy link
Copy Markdown
Member Author

agners commented Mar 5, 2026

What's this waiting on btw @agners ? It looks like both PRs are in draft and there's a good number of comments on the other. Is this dependent on something else in the epic or is it just a time issue?

Yeah it's waiting on Core part to complete. I've prioritized some release things and other bug fixes so just a time thing. I'll get back to it next week.

@agners agners force-pushed the use-unix-socket-for-core-api branch from c6c3917 to bffc3e8 Compare March 11, 2026 18:07
@agners agners force-pushed the use-unix-socket-for-core-api branch from bffc3e8 to 9f5deeb Compare March 25, 2026 15:07
@agners agners force-pushed the use-unix-socket-for-core-api branch from 9f5deeb to 7c47347 Compare April 1, 2026 17:08
@agners agners requested a review from Copilot April 1, 2026 17:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Switches Supervisor→Home Assistant Core internal REST/WebSocket traffic from TCP (port 8123) to a Unix domain socket when Core is new enough, keeping TCP as a fallback for older versions and explicitly excluding the Landingpage “version” from version comparisons.

Changes:

  • Add version-gated Unix-socket transport support to HomeAssistantAPI (session + URL selection) and close the Unix session on Supervisor shutdown.
  • Update Core WebSocket client and proxy WebSocket code to support “trusted transport” connections (no auth handshake) vs TCP token-auth connections.
  • Update Docker Core container configuration (mount + env var) and extend tests for mount/env behavior and WebSocket client attribute rename.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
supervisor/homeassistant/api.py Adds Unix-socket-aware session/url selection and uses it for API requests (token auth only for TCP).
supervisor/homeassistant/websocket.py Splits WS connection logic into Unix-socket no-auth vs TCP auth; renames _clientclient.
supervisor/api/proxy.py Reuses WSClient for proxy WebSocket connection setup, including Unix-socket mode.
supervisor/docker/const.py Introduces ENV_CORE_API_SOCKET and MOUNT_CORE_RUN; extends bind options serialization.
supervisor/docker/homeassistant.py Mounts /run/supervisor into Core and sets socket path env var when supported.
supervisor/const.py Adds SOCKET_CORE path constant used for Unix connector.
supervisor/core.py Ensures the Unix-socket ClientSession is closed during Supervisor shutdown.
tests/docker/test_homeassistant.py Updates expected mounts and adds coverage for the Core socket env var.
tests/conftest.py Adjusts WebSocket client mocking for renamed attribute.
tests/homeassistant/test_module.py Updates tests to use websocket.client instead of protected _client.

Comment on lines +141 to +142
# Supervisor <-> Core communication socket
MOUNT_CORE_RUN,
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MOUNT_CORE_RUN is added for all non-LANDINGPAGE Core containers, even when use_unix_socket is false. Since this mount exposes /run/supervisor from the host into the Core container and is only needed for the Unix socket feature, consider gating MOUNT_CORE_RUN behind self.sys_homeassistant.api.use_unix_socket to minimize unnecessary host filesystem exposure on older Core versions.

Suggested change
# Supervisor <-> Core communication socket
MOUNT_CORE_RUN,
]
)
if self.sys_homeassistant.api.use_unix_socket:
# Supervisor <-> Core communication socket
mounts.append(MOUNT_CORE_RUN)
mounts.extend(
[

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter much 🤷 I think I'd just always mount it for simplicity.

Switch internal Supervisor-to-Core HTTP and WebSocket communication
from TCP (port 8123) to a Unix domain socket.

The existing /run/supervisor directory on the host (already mounted
at /run/os inside the Supervisor container) is bind-mounted into the
Core container at /run/supervisor. Core receives the socket path via
the SUPERVISOR_CORE_API_SOCKET environment variable, creates the
socket there, and Supervisor connects to it via aiohttp.UnixConnector
at /run/os/core.sock.

Since the Unix socket is only reachable by processes on the same host,
requests arriving over it are implicitly trusted and authenticated as
the existing Supervisor system user. This removes the token round-trip
where Supervisor had to obtain and send Bearer tokens on every Core
API call. WebSocket connections are likewise authenticated implicitly,
skipping the auth_required/auth handshake.

Key design decisions:
- Version-gated by CORE_UNIX_SOCKET_MIN_VERSION so older Core
  versions transparently continue using TCP with token auth
- LANDINGPAGE is explicitly excluded (not a CalVer version)
- Hard-fails with a clear error if the socket file is unexpectedly
  missing when Unix socket communication is expected
- WSClient.connect() for Unix socket (no auth) and
  WSClient.connect_with_auth() for TCP (token auth) separate the
  two connection modes cleanly
- Token refresh always uses the TCP websession since it is inherently
  a TCP/Bearer-auth operation
- Logs which transport (Unix socket vs TCP) is being used on first
  request

Closes #6626
Related Core PR: home-assistant/core#163907

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@agners agners force-pushed the use-unix-socket-for-core-api branch from 7c47347 to 2e7c612 Compare April 1, 2026 17:26
agners and others added 3 commits April 1, 2026 19:31
Ensure the underlying WebSocket connection is closed before raising
when the handshake produces an unexpected message. Also validate that
the first TCP message is auth_required before sending credentials.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Split use_unix_socket into two properties to handle the Supervisor
upgrade transition where Core is still running with a container
started by the old Supervisor (without SUPERVISOR_CORE_API_SOCKET):

- supports_unix_socket: version check only, used when creating the
  Core container to decide whether to set the env var
- use_unix_socket: version check + running container env check, used
  for communication decisions

This ensures TCP fallback during the upgrade transition while still
hard-failing if the socket is missing after Supervisor configured
Core to use it.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@agners agners requested review from mdegat01 and sairon April 1, 2026 17:48
@agners agners marked this pull request as ready for review April 1, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants