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
4.6 KiB
Backup
VibeSonic keeps all of its state under one directory,
VIBESONIC_DATA_DIR (default /var/lib/vibesonic, or /data in the
provided Docker image). Everything that must be backed up lives there:
<DataDir>/
vibesonic.db SQLite database — users, library index,
playlists, favorites, sessions, API keys,
podcast subscriptions, shares, ...
vibesonic.db-wal SQLite write-ahead log (see below)
vibesonic.db-shm SQLite shared-memory index for the WAL
cache/covers/ resized cover-art cache (safe to delete —
regenerated on demand from source images)
podcasts/ downloaded podcast episodes
Your music library itself (the folders configured via
VIBESONIC_MUSIC_DIRS / the admin "Music Folders" page) is not inside
DataDir and has its own backup story — it's your source media, and
VibeSonic never modifies it.
What must be backed up
vibesonic.db(plus-wal/-shmif present — see below). This is the only thing whose loss is unrecoverable: it holds every user account, password/API-key hash, the entire scanned library index, playlists, favorites/ratings, play history, podcast subscriptions, share links, and configuration stored in thesettingstable.podcasts/, if you don't want to re-download subscribed episodes after a restore.- Anything you passed via
VIBESONIC_*environment variables (systemd unit, Docker Compose file,.env) — these aren't stored in the database and won't come back from a database restore alone.
What does not need to be backed up
cache/covers/— purely a derived cache, regenerated automatically the first time each cover art size is requested again. Safe to delete at any time, including while VibeSonic is running.- Your music files — back these up through whatever process you already use for your media library; VibeSonic treats them as read-only input, never as its own state.
Correct SQLite backup: mind the WAL
VibeSonic runs SQLite in WAL (write-ahead log) mode for concurrency
(internal/database/database.go: PRAGMA journal_mode=WAL). In WAL
mode, recently-committed data can live in the vibesonic.db-wal file
rather than in vibesonic.db itself, and isn't merged back
("checkpointed") into the main file on any fixed schedule.
Do not just cp vibesonic.db from a live server. A plain file copy
of vibesonic.db alone, taken while VibeSonic is running, can miss data
that only exists in the -wal file, or copy vibesonic.db and
vibesonic.db-wal at slightly different points in time and produce an
inconsistent pair. Either failure mode can leave you with a backup that
looks fine (opens without error) but is silently missing recent writes
or is outright corrupt.
Use one of these instead:
Option A — stop the server, then copy (simplest, requires downtime)
systemctl stop vibesonic
cp /var/lib/vibesonic/vibesonic.db* /path/to/backup/
systemctl start vibesonic
On a clean shutdown SQLite checkpoints and closes the WAL cleanly, so a plain copy of the (possibly WAL-less) database directory taken while the process is stopped is always consistent.
Option B — the SQLite online backup API, via the sqlite3 CLI (no downtime)
sqlite3's .backup command uses SQLite's own backup API, which
correctly handles an in-progress WAL and produces a consistent snapshot
without stopping VibeSonic or blocking writers for more than the brief
final copy step:
sqlite3 /var/lib/vibesonic/vibesonic.db ".backup /path/to/backup/vibesonic.db"
Do this on a schedule (cron/systemd timer) and rotate the output.
Option C — filesystem snapshot (LVM, ZFS, Btrfs)
A crash-consistent filesystem snapshot of the whole DataDir (taken
atomically, e.g. zfs snapshot or an LVM snapshot) is also safe — SQLite
in WAL mode is designed to recover cleanly from a snapshot equivalent to
an ungraceful power loss, the same way it recovers on the next open
after a crash. Plain rsync/cp while the server is running is not
equivalent to this, since it isn't atomic across the .db/-wal/-shm
trio.
Restoring
- Stop VibeSonic.
- Replace
vibesonic.db(and-wal/-shmif you backed those up too) with the backup copy. - Restore
podcasts/if you backed it up. - Start VibeSonic. It runs pending migrations automatically on startup, same as any other launch.
If you only have a vibesonic.db from Option B/C without matching
-wal/-shm files, that's fine — .backup and filesystem snapshots
both produce a database file that's already fully checkpointed/consistent
on its own.