* Include Docker registry configurations in backups
Docker registry credentials were removed from backup metadata in a prior
change to avoid exposing secrets in unencrypted data. Now that the encrypted
supervisor.tar inner archive exists, add docker.json alongside mounts.json
to securely backup and restore registry configurations.
On restore, registries from the backup are merged with any existing ones.
Old backups without docker.json are handled gracefully.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Increase test coverage by testing more error paths
* Address review feedback for Docker registry backup
Remove unnecessary dict() copy when serializing registries for backup
since the property already returns a dict.
Change DockerConfig.registries to use direct key access instead of
.get() with a default. The schema guarantees the key exists, and
.get() with a default would return a detached temporary dict that
silently discards updates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Reuse IMAGE_REGISTRY_REGEX for docker_image validation
Replace the monolithic regex in docker_image validator with a
function-based approach that reuses get_registry_from_image() from
docker.utils for robust registry detection. This properly handles
domains, IPv4/IPv6 addresses, ports, and localhost while still
rejecting tags (managed separately by the add-on system).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review feedback: reorder checks for efficiency
Check falsy value before isinstance, and empty path before tag check,
as suggested in PR review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
aiodocker derives ServerAddress for X-Registry-Auth by doing
image.partition("/"). For Docker Hub images like
"homeassistant/amd64-supervisor", this extracts "homeassistant"
(the namespace) instead of "docker.io" (the registry).
With the classic graphdriver image store, ServerAddress was never
checked and credentials were sent regardless. With the containerd
image store (default since Docker v29 / HAOS 15), the resolver
compares ServerAddress against the actual registry host and silently
drops credentials on mismatch, falling back to anonymous access.
Fix by prefixing Docker Hub images with "docker.io/" when registry
credentials are configured, so aiodocker sets ServerAddress correctly.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Added support for negative numbers in options
* Do not allow -. as float
* Added tests for integers and floats in options.
* Fixed ruff errors
* Added tests for outside of int/float limits
Besides file not found also catch "Too many levels of symbolic links"
which can happen when there are symbolic link loops in the add-on/apps
repository.
Also improve error handling in the repository update process to catch
OSError when checking for local modifications and raise a specific
error that can be handled appropriately.
Fixes SUPERVISOR-1FJ0
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Include network storage mount configurations in backups
When creating backups, now stores mount configurations (CIFS/NFS shares)
including server, share, credentials, and other settings. When restoring
from a backup, mount configurations are automatically restored.
This fixes the issue where network storage definitions for backups
were lost after restoring from a backup, requiring users to manually
reconfigure their network storage mounts.
Changes:
- Add ATTR_MOUNTS schema to backup validation
- Add store_mounts() method to save mount configs during backup
- Add restore_mounts() method to restore mount configs during restore
- Add MOUNTS stage to backup/restore job stages
- Update BackupManager to call mount backup/restore methods
- Add tests for mount backup/restore functionality
Fixes home-assistant/core#148663
* Address reviewer feedback for mount backup/restore
Changes based on PR review:
- Store mount configs in encrypted mounts.tar instead of unencrypted
backup metadata (security fix for passwords)
- Separate mount restore into config save + async activation tasks
(mounts activate in background, failures don't block restore)
- Add replace_default_backup_mount parameter to control whether to
overwrite existing default mount setting
- Remove unnecessary broad exception handler for default mount setter
- Simplify schema: ATTR_MOUNTS is now just a boolean flag since
actual data is in the encrypted tar file
- Update tests to reflect new async API and return types
* Fix code review issues in mount backup/restore
- Add bind mount handling for MEDIA and SHARE usage types in
_activate_restored_mount() to mirror MountManager.create_mount()
- Fix double save_data() call by using needs_save flag
- Import MountUsage const for usage type checks
* Add pylint disable comments for protected member access
* Tighten broad exception handlers in mount backup restore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address second round of reviewer feedback
- Catch OSError separately and check errno.EBADMSG for drive health
- Validate mounts JSON against SCHEMA_MOUNTS_CONFIG before importing
- Use mount_data[ATTR_NAME] instead of .get("name", "unknown")
- Overwrite existing mounts on restore instead of skipping
- Move restore_mount/activate logic to MountManager (no more
protected-access in Backup)
- Drop unused replace_default_backup_mount parameter
- Fix test_backup_progress: add mounts stage to expected events
- Fix test_store_mounts: avoid create_mount which requires dbus
* Rename mounts.tar to supervisor.tar for generic supervisor config
Rename the inner tar from mounts.tar to supervisor.tar so it can hold
multiple config files (mounts.json now, docker credentials later).
Rename store_mounts/restore_mounts to store_supervisor_config/
restore_supervisor_config and update stage names accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix pylint protected-access and test timeouts in backup tests
- Add pylint disable comment for _mounts protected access in test_backup.py
- Mock restore_supervisor_config in test_full_backup_to_mount and
test_partial_backup_to_mount to avoid D-Bus mount activation during
restore that causes timeouts in the test environment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address agners review feedback
- Change create_inner_tar() to create_tar() per #6575
- Remove "r" argument from SecureTarFile (now read-only by default)
- Change warning to info for missing mounts tar (old backups won't have it)
- Narrow exception handler to (MountError, vol.Invalid, KeyError, OSError)
* Update supervisor/backups/backup.py
* Address agners feedback: remove metadata flag, add mount feature check
- Remove ATTR_SUPERVISOR boolean flag from backup metadata; instead
check for physical presence of supervisor.tar (like folder backups)
- Remove has_supervisor_config property
- Always attempt supervisor config restore (tar existence check handles it)
- Add HostFeature.MOUNT check in _activate_restored_mount before
attempting to activate mounts on systems without mount support
---------
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
Add check_oserror() method to ResolutionManager with an extensible
errno-to-unhealthy-reason mapping. Replace ~20 inline
`if err.errno == errno.EBADMSG` checks across 14 files with a single
call to self.sys_resolution.check_oserror(err). This makes it easy to
add handling for additional filesystem errors (e.g. EIO, ENOSPC) in
one place.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate builder workflow to new builder actions
Migrate Supervisor image build to new builder actions. The resulting images
should be identical to those built by the builder.
Refs #6646 - does not implement multi-arch manifest publishing (will be done in
a follow-up)
* Update devcontainer version to 3
systemd only emits bus signals (including PropertiesChanged) when at
least one client has called Subscribe() on the Manager interface. On
regular HAOS systems, systemd-logind calls Subscribe which enables
signals for all bus clients. However, in environments without
systemd-logind (such as the Supervisor devcontainer with systemd), no
signals are emitted, causing the firewall unit wait to time out.
Explicitly calling Subscribe() has no downsides and makes it clear
that the Supervisor relies on these signals. There is no need to call
Unsubscribe() as systemd automatically tracks clients and stops
emitting signals when all subscribers have disconnected from the bus.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add iptables rules via a systemd transient unit to drop traffic
addressed to the bridge gateway IP from non-bridge interfaces.
The firewall manager waits for the transient unit to complete and
verifies success via D-Bus property change signals. On failure, the
system is marked unhealthy and host-network add-ons are prevented
from booting.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Further slow down automatic update rollout to reduce pressure on container
registry infrastructure (GHCR rate limiting). Plugins are staggered 2 minutes
apart starting at 12h, Supervisor moves from 12h to 24h.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Slow down the automatic Supervisor update rollout to reduce pressure
on the container registry infrastructure (GHCR rate limiting).
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Wait for addon startup task before unload to prevent data access race
Replace the cancel-based approach in unload() with an await of the outer
_wait_for_startup_task. The container removal and state change resolve the
startup event naturally, so we just need to ensure the task completes
before addon data is removed. This prevents a KeyError on self.name access
when _wait_for_startup times out after data has been removed.
Also simplify _wait_for_startup by removing the unnecessary inner task
wrapper — asyncio.wait_for can await the event directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Drop asyncio.sleep() in test_manager.py
* Only clear startup task reference if still the current task
Prevent a race where an older _wait_for_startup task's finally block
could wipe the reference to a newer task, causing unload() to skip
the await.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Reuse existing pending startup wait task when addon is already running
If start() is called while the addon is already running and a startup
wait task is still pending, return the existing task instead of creating
a new one.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
aiohttp's default max_msg_size of 4MB causes the ingress WebSocket proxy
to silently drop connections when an add-on sends messages larger than
that limit (e.g. Zigbee2MQTT's bridge/devices payload with many devices).
Setting max_msg_size=0 removes the limit on both the server-side
WebSocketResponse and the upstream ws_connect, fixing dropped connections
for add-ons that produce large WebSocket messages.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
* Fix fallback time sync, create repair issue if time is out of sync
The "poor man's NTP" using the whois service didn't work because it attempted
to sync the time when the NTP service was enabled, which is rejected by the
timedated service. To fix this, Supervisor now first disables the
systemd-timesyncd service and creates a repair issue before adjusting the time.
The timesyncd service stays disabled until submitting the fixup. Theoretically,
if the time moves backwards from an invalid time in the future,
systemd-timesyncd could otherwise restore the wrong time from a timestamp if we
did that after the time was set.
Also, the sync is now performed if the time is more that 1 hour off and in both
directions (previously it only intervened if it was more than 3 days in the
past).
Fixes#6015, refs #6549
* Update test_adjust_system_datetime_if_time_behind
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>