Files
owenandClaude Sonnet 5 789bf91eed 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
2026-08-23 09:18:17 -05:00

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

  1. vibesonic.db (plus -wal/-shm if 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 the settings table.
  2. podcasts/, if you don't want to re-download subscribed episodes after a restore.
  3. 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

  1. Stop VibeSonic.
  2. Replace vibesonic.db (and -wal/-shm if you backed those up too) with the backup copy.
  3. Restore podcasts/ if you backed it up.
  4. 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.