Admin Settings:
- Theme section: pick Dark Theme, Light Theme, and Default Mode
- 5 dark options (Cyberpunk/Dark/Midnight/Hacker/Neon)
- 4 light options (Light/Paper/Arctic/Solarized)
- Persisted in SQLite — survives container rebuilds
- Served via /api/config/public so frontend loads admin defaults
Visitor behavior:
- Page loads with admin's chosen default (dark or light theme)
- Sun/moon icon toggles between admin's dark and light pair
- Preference stored in cookie — persists within browser session
- No theme dropdown for visitors — admin controls the pair
Header icon simplified back to clean dark/light toggle
API Key (Sonarr/Radarr style):
- Admin panel → Settings: Generate / Show / Copy / Regenerate / Revoke
- Persisted in SQLite via settings system
- When set, POST /api/downloads requires X-API-Key header or browser origin
- Browser users unaffected (X-Requested-With: XMLHttpRequest auto-sent)
- No key configured = open access (backward compatible)
Container hardening:
- Strip SUID/SGID bits from all binaries in image
- Make /app source directory read-only (only /downloads and /data writable)
Download endpoint:
- New _check_api_access guard on POST /api/downloads
- Timing-safe key comparison via secrets.compare_digest
- Admin enabled by default (was opt-in via env var)
- New /admin/status (public) and /admin/setup (first-run only) endpoints
- Setup endpoint locked after first use (returns 403)
- Admin password persisted to SQLite config table (survives restarts)
- Change password now persists to DB (was in-memory only)
- Frontend router guard forces /admin redirect until setup is complete
- AdminSetup.vue wizard: username + password + confirm
- Public config exposes admin_enabled/admin_setup_complete for frontend
- TLS warning only fires when password is actually configured