8.5 KiB
8.5 KiB
v0.3.0 (2026-05-22)
Production-readiness hardening release: security, performance, accessibility, and observability. Substantial new functionality (HTTPS, audit log, OS mediaSession integration, rate limiter, X-Request-ID, ETag-cached artwork) alongside the security defaults flip described below.
Behavioral Changes (worth reading before upgrade)
- Admin scope is now required for management endpoints, and
scripts_management,callbacks_management,links_management,media_folders_managementdefault toFalse. Legacy bare-stringapi_tokensentries are auto-promoted toadminscope, so existing single-token deployments keep working. If you ran with a non-admin token and used CRUD on/api/scripts,/api/callbacks,/api/links, or/api/media-folders, you'll need an admin-scope token (see newTokenSpecformat inconfig.example.yaml). (d131ba4) cors_origins: ["*"]is now refused at startup — set explicit origins instead. (d131ba4)- Thumbnail cache directory moved from project-root
.cacheto%LOCALAPPDATA%/media-server/cacheon Windows and$XDG_CACHE_HOME/media-server/thumbnailson POSIX. The old.cachedirectory can be deleted. (d131ba4) - WebSocket auth prefers the
Sec-WebSocket-Protocol: media-server.token.<T>subprotocol so the token no longer ends up in URL/history/Referer. The?token=query fallback is retained for HA integration back-compat. (d131ba4)
Features
- HTTPS support via
ssl_certfile+ssl_keyfile(+ optionalssl_keyfile_password); startup refuses to launch with only one of the pair set. (d131ba4) - Reverse-proxy support:
proxy_headers+forwarded_allow_ipsplumbed throughSettingstouvicorn.Config. (d131ba4) - OS media session integration: headset / lockscreen / Bluetooth media-key buttons now drive play/pause/next/prev/seek and the browser-level mediaSession shows track metadata + artwork. (d131ba4)
- Token scope hierarchy (
read | control | admin) with structuredTokenSpecentries; legacy bare-string tokens promote toadmin. (d131ba4) - In-process token-bucket rate limiter: 5/min for failed auths, 10/min for
/api/scripts/executeand/api/callbacks/execute. (d131ba4) - WebSocket Origin allow-list check (CSWSH defence). (d131ba4)
- Script parameter validation: per-parameter
patternregex inScriptParameterConfigplusshell=False(shlex.split) execution path to harden against parameter injection oncmd.exe. (d131ba4) - CSP tightened with
form-action,worker-src,manifest-srcdirectives. (d131ba4) noopener noreferrer+no-referrerreferrerpolicy applied to every outbound link in the WebUI. (d131ba4)- Windows config.yaml ACL hardening via
icacls(current user + SYSTEM + Administrators only);0600continues to be enforced on POSIX. (d131ba4) - PWA installability:
manifest.jsongetsid,scope, andtheme_color/background_colormatching the Studio Reference base (#0E0D0B). (d131ba4) - Accessibility: ARIA labels on mini-player icon buttons; inner SVGs marked
aria-hidden. (d131ba4)
Performance
- Album-art read in
windows_mediagated by track key — was decoding the WinRT thumbnail twice per second regardless of track changes. (d131ba4) /api/media/artworkreturns content-derivedETag+Cache-Controlso the browser sendsIf-None-Matchand gets304on track repeats. (d131ba4)- Foreground-service
ctypesargtypes hoisted to one-time module init — was re-declaring ~14 prototypes per probe. (d131ba4) display_service._static_cachekeyed by(edid_hash, ...)tuple with eviction of disappeared monitors — fixes stale capabilities on hot-plug swaps where the new topology has the same monitor count. (d131ba4)- Visualizer
requestAnimationFrameloop paused ondocument.hidden, resumed onvisible. (d131ba4)
Bug Fixes
- Lifespan rewritten as
try/yield/finallyso a partial-startup failure cannot orphan background tasks or executors. (d131ba4) _run_callbackinroutes/media.pykeeps a strong task reference (GC-safe) and uses the dedicated callback executor instead of the default pool. (d131ba4)macos_media.set_volume()no longer always returnsTrueregardless of the underlying AppleScript result. (d131ba4)TrayManager._restart_requestedinitialised in__init__and set before signalling exit so the main thread observes it correctly. (d131ba4)- Missing
static_dirnow logs aWARNINGinstead of silently disabling the UI. (d131ba4) - WebSocket volume handler clamps input and never drops the socket on bad messages. (d131ba4)
- Gitea release tag validated against a strict SemVer regex before being used in a release URL. (d131ba4)
Observability
X-Request-IDmiddleware — accepts an upstream id if it matches a safe regex, otherwise generates aUUID4.request_id_varadded toContextVarsand included in every log line alongside the token label. (d131ba4)- Append-only JSONL audit log for every script + callback execution (including
on_play/on_pause/ etc. event callbacks). Background-thread writer; queue capped; flushed in lifespan teardown. (d131ba4) token=...stripped from uvicorn access logs. (d131ba4)
Development / Internal
Tests
- 35 new tests across auth scopes, the rate limiter, browser path traversal (
../,NUL, UNC, absolute paths), script-parameter validation including the regex, the Gitea tag whitelist, and atomic config writes + POSIX permissions. Suite: 47 passed / 4 skipped. (d131ba4)
All Commits
| Hash | Message | Author |
|---|---|---|
| d131ba4 | fix: production-readiness hardening — security, perf, a11y, observability | alexei.dolgolyov |