Complete Phase 4: security hardening, player/playlist/admin UI, docs
Security: - New internal/netguard package: podcast feed/enclosure fetching now refuses to dial non-public IPs (loopback/RFC1918/link-local/cloud metadata/CGNAT), validated per-connection including across redirects, closing an SSRF gap where a malicious or compromised feed URL could probe the server's internal network. Configurable via VIBESONIC_PODCASTS_ALLOW_PRIVATE_NETWORKS for intentional LAN feeds. - Podcast episode downloads capped at 4GB (was unbounded, disk-exhaustion risk) - Cover art decoding now rejects images above a 40-megapixel budget before full decode (decompression-bomb guard) - HTTP server sets ReadHeaderTimeout (slowloris hardening) - Admin folder-scan redirect now validated as same-site instead of trusting the raw Referer header (open-redirect fix) - Legacy Subsonic token/salt auth now uses a constant-time comparison Web UI completion: - Persistent player: shuffle and repeat (off/all/one) controls, wired into queue advance/prev logic and persisted like other player state - Admin user-permissions form: adminRole toggle (with a guard against demoting the last remaining admin) and per-user music-folder access checkboxes, backed by existing schema/enforcement that was previously unreachable from the UI - Playlist M3U8 import/export Docs: added docs/client-compatibility.md (honest tracking template — no client testing has been performed), docs/deployment.md, docs/security.md, docs/backup.md; refreshed README and cross-links; added a footnote in docs/subsonic-compatibility.md distinguishing spec-standard from VibeSonic-original OpenSubsonic extensions. Also added Go fuzz tests for the external-auth CIDR/IP parser and the LRC lyrics parser. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014r98AtcBbevpscWLsfwVvv
This commit is contained in:
@@ -1,71 +1,57 @@
|
||||
# VibeSonic
|
||||
|
||||
VibeSonic is a lightweight, self-hosted music/media server written in Go.
|
||||
It follows the same philosophy as other small `Vibe*` tools: a single Go
|
||||
binary, SQLite storage, server-rendered Bootstrap 5 templates, dark mode
|
||||
by default, minimal JavaScript, and no external services (no Redis, no
|
||||
message broker, no microservices, nothing Kubernetes-specific).
|
||||
VibeSonic is a lightweight, self-hosted music/media server written in
|
||||
Go. Single binary, SQLite storage, server-rendered Bootstrap 5
|
||||
templates, dark mode by default, minimal JavaScript, no external
|
||||
services (no Redis, no message broker, no microservices).
|
||||
|
||||
**This is Phase 2 of 4.** Phase 1 built the foundation (config, schema,
|
||||
auth, scanner, metadata, streaming, cover art, web UI shell). Phase 2
|
||||
adds a **complete implementation of the original Subsonic REST API
|
||||
through version 1.16.1** under `/rest/*` — every endpoint, backed by
|
||||
real data, including transcoding, HLS, a real mpv-backed jukebox, and a
|
||||
real podcast subsystem. See
|
||||
It implements the **original Subsonic REST API through version
|
||||
1.16.1**, in full, plus a set of **OpenSubsonic extensions**, so it
|
||||
works with existing Subsonic/OpenSubsonic client apps, alongside a
|
||||
complete server-rendered web UI for browser use. See
|
||||
[`docs/subsonic-compatibility.md`](docs/subsonic-compatibility.md) for
|
||||
the full endpoint-by-endpoint matrix. OpenSubsonic extensions are not
|
||||
yet implemented (Phase 3).
|
||||
the endpoint-by-endpoint matrix and
|
||||
[`docs/client-compatibility.md`](docs/client-compatibility.md) for
|
||||
real-client testing status.
|
||||
|
||||
## Features
|
||||
|
||||
### Phase 1: foundation
|
||||
|
||||
- SQLite storage (WAL mode, foreign keys, versioned migrations) with a
|
||||
schema already modeling artists, albums, songs, genres, contributors,
|
||||
playlists, favorites, ratings, scrobbles, playback history, bookmarks,
|
||||
play queues, shares, podcasts, internet radio, and settings
|
||||
- Native authentication: Argon2id password hashing, HTTP-only signed
|
||||
session cookies, CSRF-protected forms, session revocation
|
||||
- Optional Navidrome-style external authentication via a trusted reverse
|
||||
proxy header, gated on the real TCP peer address (never on
|
||||
spoofable `X-Forwarded-For`/`X-Real-IP`/`Forwarded` headers)
|
||||
- A library scanner supporting multiple music folders, incremental
|
||||
rescans, new/changed/deleted/renamed file detection, and a bounded
|
||||
worker pool — never a blind full rebuild
|
||||
- Real metadata extraction via `ffprobe` (multi-artist, multi-genre,
|
||||
MusicBrainz IDs, ReplayGain, embedded artwork/lyrics, sort fields, and
|
||||
more)
|
||||
- Stable, opaque, URL-safe IDs that survive restarts and rescans
|
||||
- Direct media streaming with HTTP range request support, correct MIME
|
||||
types, and no full-file buffering
|
||||
- Cover art discovery (embedded artwork or folder images) with a
|
||||
resized-image disk cache
|
||||
- A server-rendered, dark-mode, keyboard-friendly web UI: browsing,
|
||||
search, playlists, favorites, a persistent bottom player, and an admin
|
||||
section (users, music folders, scanner status, system settings)
|
||||
- SQLite storage (WAL mode, foreign keys, versioned migrations)
|
||||
- Native authentication (Argon2id, HTTP-only session cookies, CSRF) and
|
||||
optional Navidrome-style external authentication via a trusted
|
||||
reverse-proxy header, gated on the real TCP peer address — see
|
||||
[`docs/external-auth.md`](docs/external-auth.md)
|
||||
- A library scanner: multiple music folders, incremental rescans,
|
||||
new/changed/deleted/renamed detection, bounded worker pool
|
||||
- Real metadata extraction via `ffprobe` (multi-artist/genre,
|
||||
MusicBrainz IDs, ReplayGain, embedded artwork/lyrics, sort fields)
|
||||
- Direct streaming with HTTP range support, plus real ffmpeg-backed
|
||||
transcoding (mp3/opus/aac) and HLS segmenting, bounded concurrency,
|
||||
cancelled on client disconnect
|
||||
- Structured lyrics: plain, line-synced, and (where the source
|
||||
provides it) word/syllable-level timing, exposed via the
|
||||
`songLyrics` OpenSubsonic extension and rendered in the web player
|
||||
with click-to-seek and active-line highlighting
|
||||
- Sonic similarity: real audio-feature analysis (tempo, loudness,
|
||||
spectral shape, chroma) computed during scanning, used for
|
||||
similar-track lookups and path-finding between songs
|
||||
- Optional MusicBrainz/Cover Art Archive metadata enrichment (off by
|
||||
default — VibeSonic works fully offline otherwise)
|
||||
- Podcasts (RSS subscriptions, bounded background downloads, restart
|
||||
recovery), Internet radio stations, shares (public, no-login-required
|
||||
links), bookmarks, and multi-device play queues
|
||||
- A real jukebox backed by `mpv`'s JSON IPC — server-side playback, not
|
||||
a simulation; cleanly unavailable if `mpv` isn't installed
|
||||
- API keys, with the full Subsonic per-user permission model
|
||||
(admin/settings/stream/download/upload/playlist/cover art/comment/
|
||||
podcast/share/jukebox/video-conversion roles, plus per-user music
|
||||
folder access)
|
||||
- A server-rendered, dark-mode, keyboard-friendly web UI covering
|
||||
browsing, search, playlists (including M3U import/export),
|
||||
favorites, a persistent player with shuffle/repeat and the Media
|
||||
Session API, and a full admin section
|
||||
- `/healthz` and `/readyz`
|
||||
|
||||
### Phase 2: Subsonic REST API
|
||||
|
||||
- All 76 endpoints of the original Subsonic API through 1.16.1 —
|
||||
browsing (filesystem and ID3), search, playlists, favorites/ratings/
|
||||
scrobbling, sharing, user management with the full permission model,
|
||||
bookmarks, play queue, chat, and library scan status
|
||||
- Real ffmpeg-backed transcoding (mp3/opus/aac) and HLS segmenting for
|
||||
`stream`/`hls`, bounded concurrency, killed on client disconnect
|
||||
- A real podcast subsystem: RSS feed refresh, bounded background
|
||||
downloads, restart recovery, downloaded episodes stream like any other
|
||||
song
|
||||
- A real jukebox backed by `mpv` over its JSON IPC socket — server-side
|
||||
playback, not a simulation; administratively unavailable (with a clear
|
||||
error) if `mpv` isn't installed
|
||||
- Both original Subsonic auth modes (token/salt and password, including
|
||||
`enc:` hex), verified against an AES-256-GCM-encrypted credential kept
|
||||
separate from the Argon2id hash used for native login — see
|
||||
[`docs/subsonic-compatibility.md`](docs/subsonic-compatibility.md#authentication)
|
||||
- `/rest/*` authentication is entirely independent of the browser
|
||||
session/CSRF/external-auth flow
|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
@@ -84,6 +70,11 @@ cover art extraction, and transcoding/HLS to work. `mpv` should be on
|
||||
available — without it, `jukeboxControl` returns a clear "unavailable"
|
||||
error rather than pretending to work.
|
||||
|
||||
Point any Subsonic/OpenSubsonic client at `http://<host>:8080` (base
|
||||
URL only, no `/rest` suffix — clients construct that themselves) with a
|
||||
VibeSonic username/password, or an API key generated from account
|
||||
settings.
|
||||
|
||||
### Docker
|
||||
|
||||
```
|
||||
@@ -92,10 +83,14 @@ docker compose up -d
|
||||
|
||||
See [`docker-compose.yml`](docker-compose.yml) — mount your music
|
||||
directory read-only at `/music` and set `VIBESONIC_SESSION_SECRET`.
|
||||
Docker is convenient but never required; see
|
||||
[`docs/deployment.md`](docs/deployment.md) for running natively.
|
||||
|
||||
### systemd
|
||||
|
||||
See [`systemd/vibesonic.service`](systemd/vibesonic.service).
|
||||
See [`systemd/vibesonic.service`](systemd/vibesonic.service) and
|
||||
[`docs/deployment.md`](docs/deployment.md) for install steps and
|
||||
reverse-proxy examples (Caddy/nginx/Traefik).
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -108,7 +103,11 @@ full list and defaults. There is no separate config file.
|
||||
- [`docs/architecture.md`](docs/architecture.md) — how the pieces fit together
|
||||
- [`docs/configuration.md`](docs/configuration.md) — every environment variable
|
||||
- [`docs/external-auth.md`](docs/external-auth.md) — trusted reverse-proxy authentication
|
||||
- [`docs/subsonic-compatibility.md`](docs/subsonic-compatibility.md) — protocol endpoint status
|
||||
- [`docs/subsonic-compatibility.md`](docs/subsonic-compatibility.md) — protocol endpoint/extension status
|
||||
- [`docs/client-compatibility.md`](docs/client-compatibility.md) — real-client testing tracker
|
||||
- [`docs/deployment.md`](docs/deployment.md) — native/Docker/systemd/reverse-proxy deployment
|
||||
- [`docs/security.md`](docs/security.md) — security-relevant design decisions
|
||||
- [`docs/backup.md`](docs/backup.md) — what to back up and how (SQLite WAL caveats included)
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Reference in New Issue
Block a user