Commit Graph
2 Commits
  • Add single-user GitHub OAuth, hardened for internet exposure
    muse drives a download client and writes to the media library, so exposing it
    publicly needs more than a login form.
    
    It fails closed. Auth is on unless MUSE_AUTH=off is set explicitly, and when
    it is on with incomplete config the app refuses to start rather than serving
    open. The mode worth engineering against is not a broken login page, it is a
    misconfigured deploy quietly publishing an unauthenticated download client.
    
    Sessions are server-side: the cookie carries a 256-bit random token and
    nothing else, and only its SHA-256 is stored. There is no signed payload to
    get subtly wrong, a stolen database yields no usable cookies, and logout
    revokes for real. The allowlist is re-checked on every request so removing a
    user takes effect immediately rather than at session expiry.
    
    OAuth state is compared with secrets.compare_digest, so a callback the user
    did not initiate is rejected. MUSE_BASE_URL comes from config rather than
    X-Forwarded-* because those headers are attacker-controllable and this one is
    a redirect target.
    
    CSP is default-src 'self' with no unsafe-inline, which is why the stylesheet
    and script moved out of index.html into static files -- an injected <script>
    now cannot execute. Also frame-ancestors 'none', base-uri 'none', nosniff,
    DENY, no-referrer and HSTS. FastAPI's /docs, /redoc and /openapi.json are
    disabled so the login page does not hand out a map of every endpoint.
    
    /healthz is the only unauthenticated route and returns a bare ok, so the
    container healthcheck works without leaking anything.
    
    Verified: misconfigured auth refuses to boot; unauthenticated / redirects and
    /api/* returns 401; a forged state is rejected 400; a junk cookie is 401; a
    valid session for a non-allowlisted user is 401; all headers present; and the
    UI renders with no CSP violations in the console.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
  • muse: request a single song, get it tagged in your library
    Lidarr's smallest unit is a MusicBrainz release, so asking for one track
    means monitoring a whole discography. Soulseek trades individual files but
    has no concept of a request. muse bridges the two: search, click, and the
    song lands in the library correctly tagged.
    
    The design point is that the user picks the exact track before anything
    downloads, so muse holds artist, album, year and track number as ground
    truth and tags deterministically rather than inferring them afterwards from
    whatever a stranger named the file.
    
    Search is Deezer rather than MusicBrainz, decided on measurement:
    MusicBrainz put eight covers above the real recording for "radiohead creep"
    and never surfaced Queen for "bohemian rhapsody" across 90 results, because
    its index matches a word in a cover's title as readily as in the artist
    field. Re-ranking cannot rescue a result set missing the right row.
    
    Imports are os.link(), so downloads keep seeding from the Soulseek share
    while the library gets a second name for the same bytes.