Skip to content

fix(api): stop signing media links for non-streamable tracks - #1024

Merged
dylanjeffers merged 2 commits into
mainfrom
fix/gate-media-links-on-streamable
Aug 21, 2026
Merged

fix(api): stop signing media links for non-streamable tracks#1024
dylanjeffers merged 2 commits into
mainfrom
fix/gate-media-links-on-streamable

Conversation

@dylanjeffers

Copy link
Copy Markdown
Contributor

Follow-up to #1023.

#1023 guarded /v1/tracks/{id}/stream and /download, but that only closed two routes. dbv1.TracksKeyed builds the Stream/Download/Preview media links with no reference to is_streamable, so /v1/tracks/{id} and /v1/resolve still hand out a working signed content-node URL for tracks whose owner is deactivated or delisted by the trusted notifier.

Verified against the delisted account from the original report (audius.co/rehoxx/just-for-tonight-wmellark-hoonds, track ENxw4): /stream correctly 404s, but the stream.url in the track response returned 206 audio/mpeg with the full file.

Change

Hoist the is_streamable expression above the media-link block and gate all three links on it. This matches the empty-cid case directly above, which already leaves the link nil so the endpoints report the track as unavailable — same intent, and now the two reasons sit under one comment.

preview is included because a preview clip is still the artist's audio.

Notes

  • Scoped to non-streamable tracks only — ordinary tracks are untouched, covered by TestGetTrack_StreamableKeepsMediaLinks.
  • Content nodes reject signatures older than 48h (serve_blob.go:544), so URLs already in the wild age out on their own. The leak was that the API minted a fresh one on every request.
  • The client-side half of this is apps#14570, which is deployed.

Test plan

  • TestGetTrack_NonStreamableOmitsMediaLinks — deactivated owner and deleted track both get stream/download/preview = null
  • TestGetTrack_StreamableKeepsMediaLinks — active owner still gets all three, signed
  • Full go test ./api/... passes

🤖 Generated with Claude Code

#1023 guarded the stream and download endpoints, but that only closed two
routes. `dbv1.TracksKeyed` builds the Stream/Download/Preview media links with
no reference to `is_streamable`, so `/v1/tracks/{id}` and `/v1/resolve` keep
handing out a signed content-node URL for tracks whose owner is deactivated or
delisted. Verified against the delisted account from the original report: the
signed URL from the track response served the full mp3.

Hoist the is_streamable expression above the media-link block and gate all
three on it, matching the empty-cid case directly above, which already leaves
the link nil and lets the endpoints report the track as unavailable. Preview is
included because a preview clip is still the artist's audio.

Signatures expire after 48h on the content node, so URLs already handed out age
out on their own - the leak is that the API mints a fresh one per request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dylanjeffers dylanjeffers self-assigned this Aug 20, 2026
dylanjeffers added a commit to AudiusProject/apps that referenced this pull request Aug 20, 2026
…der (#14571)

Follow-up to #14570, which gated the web and mobile track pages on
`is_streamable`. The embed player is its own app and was missed — it
still rendered the full card (title, artist, artwork, play button) for a
track whose owner deactivated their own account or was delisted by the
trusted notifier.

AudiusProject/api#1023 already made `/v1/tracks/{id}/stream` 404, so the
player couldn't actually play these. It just showed the metadata and
then failed silently on press.

## Change

Route non-streamable tracks into the existing not-available treatment
(the same path a 404 takes), with its own copy rather than reusing the
deleted-by-creator string — the same flag covers a self deactivation and
a delisted account, and we shouldn't tell listeners the creator removed
a track when moderation suppressed it. Wording matches the web tombstone
from #14570.

The check is an explicit `=== false`, matching `isTrackUnavailable` in
common: an absent field must not read as unavailable. (The embed depends
on `@audius/sdk` rather than `@audius/common`, so the helper isn't
importable here.)

## Verification

Ran against prod data using
`audius.co/rehoxx/just-for-tonight-wmellark-hoonds` (`ENxw4`), the track
from the original report:

| | |
|---|---|
| `card` | "This track can no longer be streamed on Audius." |
| `compact` | same |
| `tiny` | "Track Unavailable" |

Both routes covered — hash id (`getTrack`) and permalink
(`getBulkTracks`). A streamable trending track still renders normally
with artwork and play button. `vite build`, `eslint`, and `jest` all
pass.

## Note

The remaining gap is server-side: `/v1/tracks/{id}` still returns a
signed content-node URL for these tracks, which AudiusProject/api#1024
fixes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

@raymondjacobson raymondjacobson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good but want to confirm only users get treatment here because tracks have their own handling elsewhere?

@dylanjeffers

Copy link
Copy Markdown
Contributor Author

want to confirm only users get treatment here because tracks have their own handling elsewhere?

Not quite — it covers both, because is_streamable is !is_delete && !user.is_deactivated (tracks.go:236). But for deleted tracks it's a no-op in practice:

#1023 already made /stream and /download 404 on !IsStreamable with no owner exemption, so a deleted track's media links were already dead ends — the URL was minted, then rejected. This just stops minting it.

The separate "deleted by artist" handling you're thinking of is client-side and keys off is_delete, not the media links, so it's unaffected — the tombstone renders from metadata and never needed a stream URL. apps#14570's isTrackUnavailable helper explicitly excludes deleted tracks for exactly that reason, so they keep their own copy rather than getting the generic unavailable treatment.

So the only behavior change for deleted tracks is "stream": null instead of a signed URL that 404s on use.

@dylanjeffers
dylanjeffers merged commit b6b5b26 into main Aug 21, 2026
2 checks passed
@dylanjeffers
dylanjeffers deleted the fix/gate-media-links-on-streamable branch August 21, 2026 23:07
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.

2 participants