Three independent bugs surfaced during pic1 clean-install testing:
1. Tor _exit_status hardcoded configured=True regardless of whether Tor was
actually installed. Status now flows through the same store-installed /
container-running bridge used by every other optional service, so Tor only
reports installed when the container is present and running.
2. check_port_open compared the port from wg0.conf against the kernel-reported
listening port, causing false "port closed" results whenever the conf and the
running container were momentarily out of sync. The function is now an honest
liveness check: any wg0 interface that is up and has a "listening port:" line
in `wg show` is considered open. The check-port API endpoint now also returns
the actual kernel listening_port and a port_mismatch flag so the UI can inform
the user when a container recreate is needed. (The recreate machinery already
exists via the port-change pending-restart path; this fix makes the mismatch
visible rather than silently lying about reachability.)
3. upload_backup only handled .zip archives; encrypted .age blobs were rejected
with a generic error. The endpoint now calls backup_crypto.is_encrypted() to
detect Age-encrypted blobs and stores them verbatim as <id>.tar.gz.age with
mode 0600 so they can be uploaded and then restored with a passphrase. The
plaintext zip path is unchanged.
Tests added/updated: test_connectivity_manager.py (Tor status bridge),
test_wireguard_manager.py + test_wireguard_endpoints.py (port-check liveness
and mismatch flag), test_config_backup_restore_http.py (encrypted upload
round-trip).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously, CaddyManager and NetworkManager contained hardcoded lists of
service names (calendar, files, mail, webdav, etc.), meaning every new
service required a code change to appear in Caddy routes and DNS records.
Now both managers accept a service_registry parameter and derive their
service lists dynamically from the registry at runtime.
- CaddyManager: new _build_registry_service_routes() and
_http01_service_pairs() methods pull routes from the registry
- NetworkManager: new _get_service_subdomains() method returns registry
subdomains with a hardcoded fallback when no registry is wired in;
_build_dns_records, stale-record detection, and service name sets all
use the registry
- managers.py: service_registry constructed before network_manager so it
can be injected into both CaddyManager and NetworkManager
- service_registry.py: validation chokepoint in get_caddy_routes() rejects
invalid subdomain/backend values and reserved service names
- service_store_manager.py: _validate_manifest now validates top-level
subdomain, backend, extra_subdomains, and extra_backends fields
- tests: 24 new tests covering registry-driven routing and DNS subdomain
generation (test_caddy_registry_integration.py)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>