Skip to content

feat(auth): enforce media credentials on a tokened server (ADR-0176 Phase 4) - #482

Merged
jeffcrouse merged 3 commits into
mainfrom
feat/adr-0176-phase-4-enforce
Nov 11, 2026
Merged

jeffcrouse merged 3 commits into
mainfrom
feat/adr-0176-phase-4-enforce

Conversation

@jeffcrouse

@jeffcrouse jeffcrouse commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

ADR-0176 Phase 4: media on a tokened server now requires one of the three credentials.

Trigger (point 8), checked on 2026-11-11. iOS and macOS 1.7 (build 80) were both released that day and read READY_FOR_DISTRIBUTION. Build 80 was built from familiar-apple 2649ca93, which contains #267 (the media header) and the re-vendored visualizer page. 1.5.1 had the same minimum OS (iOS 17.0 / macOS 14.4), so no device is offered it as a last compatible version.

Consumers checked: the Apple engine, artwork, downloads and video send the header (#267). The web admin and /embed use the cookie. The visualizer uses a header plus a data: URL. Speakers get a signed URL from POST /outputs/{id}/play. ADR-0176 had recorded that nothing calls that route, but the Apple app's casting does (ServerCastOutputsSource.swift), so the signing is live and the route stays. Speaker metadata carries no artwork URL.

Changes

  • MEDIA_ENFORCED = True.
  • Artwork, track artwork, artist images, video posters and avatars switch from public to Cache-Control: private (streams already were).
  • Tests: test_phase_4_ships_enforced, test_media_is_never_served_cache_control_public, and test_the_middleware_refuses_media_with_no_credential, which replaces the test that pinned the exemption. All three fail against main.
  • The HEALTH row is removed. The ADR's Implementation block, the CHANGELOG under Unreleased, and the SITE-CLAIMS expiry are updated.

No schema change, so familiar-apple needs no re-vendor.

Old apps: when a media request is refused, the server logs a warning naming the client and the likely cause (an app older than 1.7), once per client per hour. Jeff's iPhone and Mac are both on 1.7 (80), checked.

Rollout: this reaches a listener only when their server upgrades. A tokened server on this code stops serving media to an app on 1.5.1 or an old TestFlight build. Deploying to the NAS is a separate step.

Not in this PR: the site's "Sign-in covers the library, not the music" (index.html) and the reverse-proxy answer (faq.html) become false with the release that ships this, and change with it: draft #483, to merge with that release.

Tests: make check passes. Backend suite: 2665 passed, 18 skipped.

🤖 Generated with Claude Code

jeffcrouse and others added 3 commits November 11, 2026 14:46
…hase 4)

Point 8's trigger is met: app 1.7 (build 80), which sends the header on
media (familiar-apple #267), is the only App Store build offered on iOS and
macOS since 2026-11-11, and 1.5.1 had the same minimum OS.

- MEDIA_ENFORCED is True: a media read needs the header, the media cookie
  or a server-signed URL. Tokenless servers are unchanged (ADR-0171).
- Every media route answers Cache-Control: private (point 6), with a test
  that no route under app/ goes back to public.
- The test that pinned the exemption now pins the refusal; both new tests
  fail against the Phase 3 code.
- HEALTH row removed; ADR-0176 records Phase 4, and corrects Phase 3's
  "nothing calls the speaker route": Apple casting calls it.
- SITE-CLAIMS: the site's "not the music" rows expire with this release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hase 4)

An app from before 1.7 sends no credential on media and goes silent with
no explanation. The server now logs a warning naming the client and the
likely cause, once per client an hour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e release

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jeffcrouse
jeffcrouse merged commit ffec837 into main Nov 11, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant