The core_security check (HA < 2021.1.5 with custom components) and the
ResolutionNotify class that created persistent notifications for it are
no longer needed. The minimum supported HA version is well past 2021.1.5,
so this check can never trigger. The notify module was the only consumer
of persistent notifications and had no other users.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Treat empty string password as None in backup restore
Work around a securetar 2026.2.0 bug where an empty string password
sets encrypted=True but fails to derive a key, leading to an
AttributeError on restore. This also restores consistency with backup
creation which uses a truthiness check to skip encryption for empty
passwords.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Explicitly mention that "" is treated as no password
* Add tests for empty string password handling in backups
Verify that empty string password is treated as no password on both
backup creation (not marked as protected) and restore (normalized to
None in set_password).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Improve comment
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Use Python 3.14(.3) in CI and base image
Update base image to the latest tag using Python 3.14.3 and update Python
version in CI workflows to 3.14.
With Python 3.14, backports.zstd is no longer necessary as it's now available
in the standard library.
* Update wheels ABI in the wheels builder to cp314
* Use explicit Python fix version in GH actions
Specify explicitly Python 3.14.3, as the setup-python action otherwise default
to 3.14.2 when 3.14.3, leading to different version in CI and in production.
* Update Python version references in pyproject.toml
* Fix all ruff quoted-annotation (UP037) errors
* Revert unquoting of DBus types in tests and ignore UP037 where needed
* Respect auto-update setting for plug-in auto-updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Also skip auto-updating plug-ins in decorator
* Raise if auto-update flag is not set and plug-in is not up to date
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
aiohttp's BasicAuth.decode() raises ValueError for any non-Basic auth
method (e.g. Bearer tokens). This propagated as an unhandled exception,
causing a 500 response instead of the expected 401 Unauthorized.
Catch the ValueError in _process_basic() and raise HTTPUnauthorized with
the WWW-Authenticate realm header so clients get a proper 401 response.
Fixes SUPERVISOR-BFG
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The _migrate function in addons/validate.py is the first validator in the
SCHEMA_ADDON_CONFIG All() chain and was called directly with raw config data.
If a malformed add-on config file contained a non-dict value (e.g. a string),
config.get() would raise an AttributeError instead of a proper voluptuous
Invalid error, causing an unhandled exception.
Add an isinstance check at the top of _migrate to raise vol.Invalid for
non-dict inputs, letting validation fail gracefully.
Fixes SUPERVISOR-HMP
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Drop unsupported architectures and machines from Supervisor
Since #5620 Supervisor no longer updates the version information on
unsupported architectures and machines. This means users can no longer
update to newer version of Supervisor since that PR got released.
Furthermore since #6347 we also no longer build for these
architectures. With this, any code related to these architectures
becomes dead code and should be removed.
This commit removes all refrences to the deprecated architectures and
machines from Supervisor.
This affects the following architectures:
- armhf
- armv7
- i386
And the following machines:
- odroid-xu
- qemuarm
- qemux86
- raspberrypi
- raspberrypi2
- raspberrypi3
- raspberrypi4
- tinker
* Create issue if an app using a deprecated architecture is installed
This adds a check to the resolution system to detect if an app is
installed that uses a deprecated architecture. If so, it will show a
warning to the user and recommend them to uninstall the app.
* Formally deprecate machine add-on configs as well
Not only deprecate add-on configs for unsupported architectures, but
also for unsupported machines.
* For installed add-ons architecture must always exist
Fail hard in case of missing architecture, as this is a required field
for installed add-ons. This will prevent the Supervisor from running
with an unsupported configuration and causing further issues down the
line.
* Fix add-on build using wrong architecture for non-native arch add-ons
When building a locally-built add-on (no image tag), the architecture
was always set to sys_arch.default (e.g. amd64 on x86_64) instead of
matching against the add-on's declared architectures. This caused an
i386-only add-on to incorrectly build as amd64.
Use sys_arch.match() against the add-on's declared arch list in all
code paths: the arch property, image name generation, BUILD_ARCH build
arg, and default base image selection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use CpuArch enums to fix tests
* Explicitly set _supported_arch as new list to fix tests
* Fix pytests
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Use verbose log output for plug-ins
All three plug-ins which support logging (dns, multicast and audio)
should use the verbose log format by default to make sure the log lines
are annotated with timestamp. Introduce a new flag default_verbose for
advanced logs.
* Use default_verbose for host logs as well
Use the new default_verbose flag for advanced logs, to make it more
explicit that we want timestamps for host logs as well.
The /os/info API endpoint has been using D-Bus property TimeUSec which got
cached between requests, so the time returned was not always the same as
current time on the host system at the time of the request. Since there's no
reason to use D-Bus API for the time, as Supervisor runs on the same machine
and time is global, simply format current datetime object with Python and
return it in the response.
Fixes#6581
* Handle missing Accept header in host logs
Avoid indexing request headers directly in the host advanced logs handler when Accept is absent, preventing KeyError crashes on valid requests without that header. Fixes SUPERVISOR-1939.
* Add pytest
* Ensure uuid of dismissed suggestion/issue matches an existing one
* Fix lint, test and feedback issues
* Adjust existing tests and remove new ones for not found errors
* fix device access issue usage
Remove the docker property and schema validation from backup metadata.
The Docker config (registry credentials, IPv6 setting) was already
dropped from backup/restore operations in #5605, but the property and
schema entry remained. Old backups with the docker key still load fine
since the schema uses extra=vol.ALLOW_EXTRA.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Bump securetar from 2025.12.0 to 2026.2.0
Adapt to the new securetar API:
- Use SecureTarArchive for outer backup tar (replaces SecureTarFile
with gzip=False for the outer container)
- create_inner_tar() renamed to create_tar(), password now inherited
from the archive rather than passed per inner tar
- SecureTarFile no longer accepts a mode parameter (read-only by
default, InnerSecureTarFile for writing)
- Pass create_version=2 to keep protected backups at version 2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Reformat imports
* Rename _create_cleanup to _create_finalize and update docstring
* Use constant for SecureTar create version
* Add test for SecureTarReadError in validate_backup
securetar >= 2026.2.0 raises SecureTarReadError instead of
tarfile.ReadError for invalid passwords. Catching this exception
and raising BackupInvalidError is required so Core shows the
encryption key dialog to the user.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Handle InvalidPasswordError for v3 backups
* Address typos
* Add securetar v3 encrypted password test fixture
Add a test fixture for a securetar v3 encrypted backup with password.
This will be used in the test suite to verify that the backup
extraction process correctly handles encrypted backups.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Harden backup tar extraction with Python data filter
Replace filter="fully_trusted" with a custom backup_data_filter that
wraps tarfile.data_filter. This adds protection against symlink attacks
(absolute targets, destination escapes), device node injection, and
path traversal, while resetting uid/gid and sanitizing permissions.
Unlike using data_filter directly, the custom filter skips problematic
entries with a warning instead of aborting the entire extraction. This
ensures existing backups containing absolute symlinks (e.g. in shared
folders) still restore successfully with the dangerous entries omitted.
Also removes the now-redundant secure_path member filtering, as
data_filter is a strict superset of its protections. Fixes a standalone
bug in _folder_restore which had no member filtering at all.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Simplify security tests to test backup_data_filter directly
Test the public backup_data_filter function with plain tarfile
extraction instead of going through Backup internals. Removes
protected-access pylint warnings and unnecessary coresys setup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Switch to tar filter instead of custom data filter wrapper
Replace backup_data_filter (which wrapped data_filter and skipped
problematic entries) with the built-in tar filter. The tar filter
rejects path traversal and absolute names while preserving uid/gid
and file permissions, which is important for add-ons running as
non-root users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Apply suggestions from code review
Co-authored-by: Erik Montnemery <erik@montnemery.com>
* Use BackupInvalidError instead of BackupError for tarfile.TarError
Make sure FilterErrors lead to BackupInvalidError instead of BackupError,
as they are not related to the backup process itself but rather to the
integrity of the backup data.
* Improve test coverage and use pytest.raises
* Only make FilterError a BackupInvalidError
* Add test case for FilterError during Home Assistant Core restore
* Add test cases for Add-ons
* Fix pylint warnings
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
* Extend and improve release drafter config
Extend the release drafter config with more types (labels) and order
them by priority. Inspired by conventional commits, in particular
the list documented at (including the order):
https://github.com/pvdlg/conventional-changelog-metahub?tab=readme-ov-file#commit-types
Additionally, we left the "breaking-change" and "dependencies" labels.
* Add revert to the list of labels
Add the missing WIFI_P2P (30) entry to the DeviceType NetworkManager
enum. Without it, systems with a Wi-Fi P2P interface log a warning:
Unknown DeviceType value received from D-Bus: 30
Closes#6573
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Unify Core user listing with HomeAssistantUser model
Replace the ingress-specific IngressSessionDataUser with a general
HomeAssistantUser dataclass that models the Core config/auth/list WS
response. This deduplicates the WS call (previously in both auth.py
and module.py) into a single HomeAssistant.list_users() method.
- Add HomeAssistantUser dataclass with fields matching Core's user API
- Remove get_users() and its unnecessary 5-minute Job throttle
- Auth and ingress consumers both use HomeAssistant.list_users()
- Auth API endpoint uses typed attribute access instead of dict keys
- Migrate session serialization from legacy "displayname" to "name"
- Accept both keys in schema/deserialization for backwards compat
- Add test for loading persisted sessions with legacy displayname key
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Tighten list_users() to trust Core's auth/list contract
Core's config/auth/list WS command always returns a list, never None.
Replace the silent `if not raw: return []` (which also swallowed empty
lists) with an assert, remove the dead AuthListUsersNoneResponseError
exception class, and document the HomeAssistantWSError contract in the
docstring.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove | None from async_send_command return type
The WebSocket result is always set from data["result"] in _receive_json,
never explicitly to None. Remove the misleading | None from the return
type of both WSClient and HomeAssistantWebSocket async_send_command, and
drop the now-unnecessary assert in list_users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use HomeAssistantWSConnectionError in _ensure_connected
_ensure_connected and connect_with_auth raise on connection-level
failures, so use the more specific HomeAssistantWSConnectionError
instead of the broad HomeAssistantWSError. This allows callers to
distinguish connection errors from Core API errors (e.g. unsuccessful
WebSocket command responses). Also document that _ensure_connected can
propagate HomeAssistantAuthError from ensure_access_token.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove user list cache from _find_user_by_id
Drop the _list_of_users cache to avoid stale auth data in ingress
session creation. The method now fetches users fresh each time and
returns None on any API error instead of serving potentially outdated
cached results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
NMDeviceType 13 (NM_DEVICE_TYPE_BRIDGE) was not listed in the
DeviceType enum, causing a warning when NetworkManager reported
a bridge interface.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add periodic progress logging during initial Core installation
Log installation progress every 15 seconds while downloading the
Home Assistant Core image during initial setup (landing page to core
transition). Uses asyncio.Event with wait_for timeout to produce
time-based logs independent of Docker pull events, ensuring visibility
even when the network stalls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add test coverage
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jan Čermák <sairon@users.noreply.github.com>
* Fix getting Supervisor IP address in testing
Newer Docker versions (probably newer than 29.x) do not have a global
IPAddress attribute under .NetworkSettings anymore. There is a network
specific map under Networks. For our case the hassio has the relevant
IP address. This network specific maps already existed before, hence
the new inspect format works for old as well as new Docker versions.
While at it, also adjust the test fixture.
* Actively wait for hassio IPAddress to become valid
* Remove blocking I/O added to import_image
* Add scanned modules to extra blockbuster functions
* Use same cast avoidance approach in export_image
* Remove unnecessary local image_writer variable
* Remove unnecessary local image_tar_stream variable
---------
Co-authored-by: Stefan Agner <stefan@agner.ch>
* Raise HomeAssistantWSError when Core WebSocket is unreachable
Previously, async_send_command silently returned None when Home Assistant
Core was not reachable, leading to misleading error messages downstream
(e.g. "returned invalid response of None instead of a list of users").
Refactor _can_send to _ensure_connected which now raises
HomeAssistantWSError on connection failures while still returning False
for silent-skip cases (shutdown, unsupported version). async_send_message
catches the exception to preserve fire-and-forget behavior.
Update callers that don't handle HomeAssistantWSError: _hardware_events
and addon auto-update in tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Simplify HomeAssistantWebSocket command/message distinction
The WebSocket layer had a confusing split between "messages" (fire-and-forget)
and "commands" (request/response) that didn't reflect Home Assistant Core's
architecture where everything is just a WS command.
- Remove dead WSClient.async_send_message (never called)
- Rename async_send_message → _async_send_command (private, fire-and-forget)
- Rename send_message → send_command (sync wrapper)
- Simplify _ensure_connected: drop message param, always raise on failure
- Simplify async_send_command: always raise on connection errors
- Remove MIN_VERSION gating (minimum supported Core is now 2024.2+)
- Remove begin_backup/end_backup version guards for Core < 2022.1.0
- Add debug logging for silently ignored connection errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Wait for Core to come up before backup
This is crucial since the WebSocket command to Core now fails with the
new error handling if Core is not running yet.
* Wait for Core install job instead
* Use CLI to fetch jobs instead of Supervisor API
The Supervisor API needs authentication token, which we have not
available at this point in the workflow. Instead of fetching the token,
we can use the CLI, which is available in the container.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When an addon updates from having no ingress to having ingress, the
ingress token map was never rebuilt. Both update() and rebuild() called
_check_ingress_port() to assign a dynamic port but skipped the
sys_ingress.reload() call that registers the token. This caused
Ingress.get() to return None, resulting in a 503 error.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The CLI calls in the tests are still using deprecated add-ons terminology,
causing deprecation warnings. Change the commands and flags to the new ones.
* Add D-Bus tolerant enum base classes to prevent crashes on unknown values
D-Bus services (systemd, NetworkManager, RAUC, UDisks2) can introduce
new enum values at any time via OS updates. Standard Python enum
construction raises ValueError for unknown values, which would crash
the Supervisor.
Introduce DBusStrEnum and DBusIntEnum base classes that use Python's
_missing_ hook to create pseudo-members for unknown values. These
pseudo-members pass isinstance checks (satisfying typeguard), preserve
the original value, don't pollute __members__, and report unknown
values to Sentry (deduplicated per class+value) for observability.
Migrate 17 D-Bus enums in dbus/const.py and udisks2/const.py to the
new base classes. Enums only sent TO D-Bus (StopUnitMode, StartUnitMode,
etc.) are left unchanged. Remove the manual try/except workaround in
NetworkInterface.type now that DBusIntEnum handles it automatically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add explicit enum conversions for systemd-resolved D-Bus properties
The resolved properties (dns_over_tls, dns_stub_listener, dnssec, llmnr,
multicast_dns, resolv_conf_mode) were returning raw string values from
D-Bus without converting to their declared enum types. This would fail
runtime type checking with typeguard.
Now safe to add explicit conversions since these enums use DBusStrEnum,
which tolerates unknown values from D-Bus without crashing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Avoid blocking I/O in D-Bus enum Sentry reporting
Move sentry_sdk.capture_message out of the event loop by adding a
fire_and_forget_capture_message helper that offloads the call to the
executor when a running loop is detected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Handle exceptions when reporting message to Sentry
* Narrow typing of reported values
Use str/int explicitly since that is what the two existing Enum classes
can actually report.
* Adjust test style
* Apply suggestions from code review
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>