Files
Lyra/.env.example
T
Jonathan 190e0ef4b6 feat(web): minimal shared-password auth gate
The web app had no auth — anyone reaching the host could browse the library,
trigger downloads, and read/overwrite the stored credentials in Settings. Adds
an opt-in single-shared-password gate:

- Next.js middleware redirects unauthenticated page requests to /login and
  returns 401 for /api/*; static assets are excluded via matcher.
- Auth is enabled ONLY when LYRA_PASSWORD is set (unset ⇒ app stays open, so
  existing deployments are unchanged). Documented in README "Security".
- The session cookie is httpOnly and carries HMAC-SHA256(password) keyed by
  LYRA_SECRET_KEY — unforgeable, and the plaintext password never touches the
  client. Edge-safe Web Crypto so one lib/auth.ts serves middleware + route.
- Cookie is NOT Secure: Lyra serves plain HTTP on the LAN; a Secure cookie would
  loop. Works behind an HTTPS proxy too.
- /login page + form, a Sign out button (shown only when auth is enabled), and
  chrome (nav + worker-status poll) suppressed on /login.
- docker-compose passes LYRA_PASSWORD to web; .env.example documents it.

Verified live end-to-end (built + next start): no-cookie page→307 /login,
API→401, wrong password→401, correct→200+cookie, authed page/API→200, forged
cookie→401, and the auth-disabled (no LYRA_PASSWORD) path stays fully open.
web 133 tests green, tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:12:51 +02:00

37 lines
1.9 KiB
Bash

POSTGRES_USER=lyra
POSTGRES_PASSWORD=lyra
POSTGRES_DB=lyra
DATABASE_URL=postgresql://lyra:lyra@db:5432/lyra
# 32 random bytes, base64. Generate with: openssl rand -base64 32
LYRA_SECRET_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
# Shared password protecting the whole web UI + API. When set, every request must sign in
# (one shared password, no per-user accounts). LEAVE UNSET AND THE APP IS OPEN to anyone
# who can reach the host — only safe on a trusted LAN or behind a VPN/auth proxy. Strongly
# recommended for any exposed deployment. See README "Security".
# LYRA_PASSWORD=change-me
# Host directory for the downloaded music library (bind-mounted into the worker at /music)
MUSIC_DIR=./music
# Host directory for per-job download staging (bind-mounted into the worker at /staging).
# Defaults to MUSIC_DIR/.staging. Point this at a fast LOCAL disk when MUSIC_DIR is a network
# share (SMB/NFS) so temporary downloads don't hammer the share; the final import copies the
# finished album onto the library volume and swaps it in atomically.
# STAGING_DIR=/srv/lyra-staging
# Host directory where slskd (the external Soulseek daemon) writes FINISHED downloads,
# bind-mounted into the worker at /slskd-downloads. The worker moves completed Soulseek
# transfers out of here into staging, so this MUST be the same directory slskd downloads to.
# Set this only once Lyra runs on the same host as slskd; until then Soulseek can search but
# not deliver. Defaults to ./slskd-downloads (an empty local dir) so the stack still starts.
# SLSKD_DOWNLOADS_DIR=/var/lib/slskd/downloads
# Postgres backups (db-backup sidecar). Dumps go to BACKUP_DIR on the host; the sidecar
# keeps the last BACKUP_KEEP dumps and runs every BACKUP_INTERVAL_SECONDS (default daily).
# Point BACKUP_DIR at a NAS/synced path for off-box safety. See README "Backup & restore".
# BACKUP_DIR=./backups
# BACKUP_INTERVAL_SECONDS=86400
# BACKUP_KEEP=7