This release includes 2 security fixes for security teams reviewing exposed deployments.
Affected surfaces
ReleasePort's take
Moderate signalThe release fixes a launch PIN regression that caused the full setup wizard to re‑trigger on every visit and adds security hardening by restricting destructive API endpoints to admin users only.
Why it matters: Severity 70 regression prevents normal use; severity 80 restriction blocks non‑admin abuse of critical endpoints. Operators must deploy immediately to restore functionality and protect data integrity.
Summary
AI summaryUpdates quick switch for admin, bug fixes, and security + hardening across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | High |
Fixes launch PIN regression that re‑triggered full setup wizard on every visit (#842). Fixes launch PIN regression that re‑triggered full setup wizard on every visit (#842). Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Security | High |
Restricts destructive endpoints (db restore/backup, library delete, cache clear) to admin users only. Restricts destructive endpoints (db restore/backup, library delete, cache clear) to admin users only. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Security | Medium |
Adds PIN brute‑force limit: after 10 wrong tries from one IP, access is throttled. Adds PIN brute‑force limit: after 10 wrong tries from one IP, access is throttled. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Feature | Low |
Adds per‑profile **My Accounts** modal for connecting personal Spotify, Tidal, Listenbrainz accounts while keeping metadata global. Adds per‑profile **My Accounts** modal for connecting personal Spotify, Tidal, Listenbrainz accounts while keeping metadata global. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Feature | Low |
Runs background automations as their owning profile, using each user’s personal accounts for playlist reads. Runs background automations as their owning profile, using each user’s personal accounts for playlist reads. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Feature | Low |
Introduces opt‑in native login (username/password), forgot‑password recovery, and reverse‑proxy authentication support, all off by default. Introduces opt‑in native login (username/password), forgot‑password recovery, and reverse‑proxy authentication support, all off by default. Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Feature | Low |
Makes the sidebar **Service Status** panel clickable for admins to quickly switch active metadata, media server, and download sources via a modal. Makes the sidebar **Service Status** panel clickable for admins to quickly switch active metadata, media server, and download sources via a modal. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Feature | Low |
Revamps the **Manage Profiles** modal with visual improvements. Revamps the **Manage Profiles** modal with visual improvements. Source: granite4.1:30b@2026-06-11-audit Confidence: low |
— |
| Bugfix | Medium |
Fixes slash‑in‑title bug that truncated search targets and quarantined downloads (#835). Fixes slash‑in‑title bug that truncated search targets and quarantined downloads (#835). Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Resolves hung slskd download tasks by adding a terminal‑grace backstop (#836). Resolves hung slskd download tasks by adding a terminal‑grace backstop (#836). Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Corrects manual find & add to append single track instead of recreating entire playlist (#837). Corrects manual find & add to append single track instead of recreating entire playlist (#837). Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Removes 100‑track cap on public Spotify playlists in auto‑sync, enabling full paginated fetch (#838). Removes 100‑track cap on public Spotify playlists in auto‑sync, enabling full paginated fetch (#838). Source: llm_adapter@2026-06-11 Confidence: high |
— |
| Bugfix | Medium |
Fixes "discovery state not found" error after restart/import by correcting cache write logic (#843). Fixes "discovery state not found" error after restart/import by correcting cache write logic (#843). Source: llm_adapter@2026-06-11 Confidence: high |
— |
Full changelog
2.7.0
multi-user gets real — and so does securing it. each profile can connect its own streaming accounts and pull its own playlists, the admin gets a quick way to switch active sources, plus a full opt-in login system (real username/password accounts, or sit behind a reverse proxy), a stack of bug fixes, and some hardening.
living doc — i'll keep adding to this as 2.7.0 comes together (deezer/qobuz per-profile still to come).
per-profile accounts (the headline)
- new My Accounts modal (the ♫ button by your profile, open to everyone): connect your own spotify / tidal / listenbrainz and from then on your playlist browsing/pulling uses your account — not the admin's.
- metadata + downloads stay global (the admin's accounts run the app's background work). only playlist reads use the personal account. so two people see their own playlists at the same time without stepping on each other.
- shared-app model: you don't make your own dev app — you just log in (spotify/tidal) or paste a token (listenbrainz) against the admin's existing app.
show_dialogforces the account picker so you can't accidentally grab the admin's session. - non-regressive by design: admin, anyone who hasn't connected, and all background workers fall back to the global accounts exactly as before — so single-user setups notice nothing. tidal's per-profile token refresh is redirected to the profile so it can never clobber the admin's tokens.
per-profile automations (auto-sync runs as you)
- background automations now run as their owner. before, anything on a timer had no logged-in session so it fell back to admin — meaning a non-admin's auto-sync pipeline would've used the admin's account. now the engine tags each run with its owner (a contextvar it sets around the handler), and
get_current_profile_id()honours it only when there's no real request. - the auto-sync playlist read then resolves that owner's account (the source adapters already grabbed their client through a getter — just pointed it at the per-profile resolver). so a non-admin's scheduled pipeline pulls their playlist.
- admin's existing auto-sync pipelines are untouched — they're profile 1, which resolves to the global account exactly as before. proven by tests (admin → global; non-admin automation → its owner; context resets even if the handler throws).
- watchlist scan + wishlist download were already per-profile (they loop every profile). deezer/qobuz auto-sync still uses the global account (deferred).
quick switch for admin
- the sidebar Service Status panel is now clickable (admin only) — opens a manage-workers-style modal to switch the active metadata source / media server / download source (+ real hybrid drag), with brand logos and a hero header. surfaces the configured-vs-effective source so "spotify" vs "spotify (no auth)" is never confusing.
- Manage Profiles modal got a visual revamp too.
secure access (new — all opt-in, off by default)
a normal direct/LAN setup behaves exactly as before. none of this turns on unless you flip it on, and the launch PIN keeps working untouched if that's what you use.
- native login — flip on "require login" and each profile becomes a real account: a username + password sign-in screen replaces the picker + PIN. password is separate from the quick-switch PIN (a 4-digit PIN shouldn't be the thing guarding a public instance), hashed, never sent to the browser. brute-force limited per IP, generic errors so usernames don't leak, and anti-lockout — you can't enable it until the admin has a password.
- forgot-password recovery — set a security question (pick one or write your own) + answer; if you forget your password you answer it on the sign-in screen to reset. answer is hashed, matched forgivingly (case/spacing).
- reverse proxy mode — for running behind nginx / caddy / traefik: trusts
X-Forwarded-*(correct client IP + https detection), marks the session cookie secure, adds security headers (HSTS etc.). off by default so direct http:// LAN access is untouched (it'd otherwise break the cookie). + a fullSupport/REVERSE-PROXY.mdguide with the socket.io upgrade headers everyone forgets. - auth-proxy trust — let authelia / authentik / oauth2-proxy be the gatekeeper: trust a
Remote-User-style header and skip the launch PIN for already-authenticated requests. spoof-safe (ignored unless you explicitly name the header). - PIN brute-force limit — the launch PIN now backs off after 10 wrong tries from one IP (correct entry never affected, self-heals).
- it all lives in a reorganized Settings → Security page: three labelled groups (PIN / user accounts / reverse proxy), step-ordered, with the "require login" toggle visibly greyed out until you set an admin password, confirm-password fields, and "✓ saved" state so it doesn't look unset after a refresh.
bug fixes
- #835 — a
/in a song title (sawano's "YouSeeBIGGIRL/T:T") was treated as a path separator, truncating the search target and quarantining valid youtube/tidal downloads. - #836 — a rejected slskd download hung the task at "downloading" forever; added a terminal-grace backstop so a stuck error state gets marked failed and frees the worker.
- #837 — manual find & add recreated the whole jellyfin/emby playlist instead of appending the one track.
- #838 — auto-sync capped public spotify playlists at 100 tracks (embed scraper); now uses the full paginated fetch.
- #843 — "discovery state not found" when fixing a match after a restart/import; the durable cache write was wrongly gated on in-memory state, and keyed by the full artist string instead of the first artist.
- find & add search — exact-title matches were buried under a case-sensitive sort (lowercase "bad guy" lost to every capitalized "Bad Guy" and fell off the limit); now relevance-ordered.
artist sync = a mini deep scan
- the artist-detail sync button is now genuinely a single-artist deep scan: it reuses the same server-diff stale-removal as the whole-library deep scan, scoped to one artist. the old disk-existence check (which could mass-delete on an unreachable mount) is gone — removal only runs when the server pull succeeds, with the same >50%-unseen safety guard, admin-only.
security + hardening
- #842 — fixed the launch PIN re-triggering the full first-run setup wizard on every visit (regression from the #832 server-side PIN gate; the setup check was getting blocked before the PIN screen).
- destructive endpoints (db restore/backup/vacuum, library/track delete, cache clear, plex clear-library, api-key mint) are now admin-only server-side — they were UI-hidden but API-open. profile-scoped stuff (e.g. clearing your own wishlist) deliberately left open.
notes
- version constant still reads 2.6.9 (inherited base) — bump to 2.7.0 + what's-new entry at release time.
- every piece landed with seam-level + endpoint + regression tests; full suite green (only the pre-existing soundcloud mock failures, unrelated).
- still on the global account for now (next up): per-profile deezer + qobuz (their playlist login is tangled with the download login — needs careful isolation). last.fm is out (it's a scrobbler, no playlists to pull).
Security Fixes
- #842 – Fixed launch PIN re‑triggering the full first‑run setup wizard on every visit
- Destructive endpoints (db restore/backup/vacuum, library/track delete, cache clear, Plex clear‑library, API‑key mint) now enforced as admin‑only server‑side
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
Related context
Related tools
Beta — feedback welcome: [email protected]