SiriusXM: sign in with a password, and each side's own feed found for them - #23
Merged
Merged
Conversation
For the leagues SiriusXM carries by team -- NFL, NBA, MLB, NHL, WNBA, the college conferences, MLS -- a fixture's page and a team's page now draw an "On SiriusXM" section that looks up each side's own broadcast as soon as the page is up, rather than behind a button. The team name is parsed (packages/radio/src/teams.js), SiriusXM is searched for it on the reader's session, and only channels that actually name the team are kept: the full name, a distinctive nickname, or the school for a college. Lookups are cached across readers for ten minutes; one search per side per fixture, not per view. Nothing changes for leagues without team feeds, and nothing at all on genrewatch, where the rail stays off. SiriusXM failures are now logged with what SXM said, so a failed sign-in can be read from the service logs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ac4GWmd6Nfd8JReX9u7kH1
The settings form asked for an email and had SiriusXM send a code, which is
one of the two doors the SiriusXM player offers and not the one people reach
for. Read from the player's bundle: authenticatePassword posts {handle,
password} with the anonymous session's token and answers with an identity
grant, which sessions/authenticated turns into a session exactly as the OTP
grant is. So the form now takes email and password, with 'Email me a code
instead' beside it. The password is used for that one call and is never
stored, logged or echoed; what is kept is the session, sealed as before.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac4GWmd6Nfd8JReX9u7kH1
Merged
This was referenced Sep 22, 2026
ralyodio
added a commit
that referenced
this pull request
Sep 22, 2026
…79) Twice now Redis has gone away and tipoffwatch has stayed down until a person noticed: 2026-09-13, and again on 2026-09-22 while the volume was being cleared. Both times the shape was identical. `/healthz` answered 200 in 50ms, `/` returned nothing at all, Railway reported the service Online, and restarting Redis did not fix it -- the wedge is on this side of the socket, so only redeploying the web service brought the site back. The reason it cannot recover on its own is one line in the shared client. BullMQ requires `maxRetriesPerRequest: null`, and that turns a disconnect into a hang: a command issued while the socket is down is queued until it returns rather than rejected. Verified rather than assumed -- a bare `ping()` against a dead port with the app's own options never settles. The page render catches Redis errors and falls through to Postgres, but there is no error to catch, so the request simply stops. `/healthz` touches neither dependency, which is why every liveness signal Railway has stayed green through both outages. genrewatch already solved this for Postgres in its PR #23, after the same failure on its pool: probe through the client the requests actually use, and exit when it stops answering. A separate connection is the one thing guaranteed to look healthy during this failure, which is why the probe has to share the client. So the watchdog is ported here and given a subject, and both dependencies get one: `select 1` on the shared `sql` handle, `PING` on the shared ioredis connection. Exiting reads as drastic for a web server and is the cheapest correct move -- the failure is process-local state no request can repair, a restart demonstrably clears it, and Railway replaces the container in about a minute. Hanging forever is not the safer option, it is the outage. Redis gets one more failure than the pool, four probes rather than three. A healthy Redis here is routinely unreachable for a while, because it restarts by reading an RDB off the volume before accepting anything: 26 seconds now, 124 before the event streams were trimmed. Boot already throws if Redis is absent, so an impatient watchdog would turn one Redis deploy into a deploy loop on this service. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things, both from feedback on #22.
The password door
The settings form asked for an email and had SiriusXM send a code — one of the two doors the SiriusXM player offers, and not the one people reach for. Read from the player's own bundle (endpoint table, 2026-09-03):
authenticatePasswordposts{handle, password}toidentity/v1/identities/authenticate/passwordwith the anonymous session's token and answers with an identity grant, whichsessions/authenticatedturns into a session exactly as the OTP grant is. So the form now takes email and password, with Email me a code instead beside it. The password is used for that one call and is never stored, logged or echoed (a test pins that); what is kept is the session, sealed as before. Tried first without a bearer like the code path; the anonymous session is minted only if the gateway insists.Each side's own feed, found for them
For the leagues SiriusXM carries by team — NFL, NBA, MLB, NHL, WNBA, the college conferences, MLS — a fixture's page and a team's page draw an On SiriusXM section that looks up each side's own broadcast as soon as the page is up, rather than behind a button. The team name is parsed (
packages/radio/src/teams.js: the provider's nickname when there is one, else the last word with club suffixes stripped), SiriusXM is searched for it on the reader's session, and only channels that actually name the team are kept — full name, a distinctive nickname of four letters or more, or the school for a college. Cached across readers for ten minutes (misses three), so a busy fixture costs one search per side, not one per view. Both sides settle separately, and the section says in words whether a side has no feed yet or the lookup broke. Nothing upstream at render:app.jsfetches/radio/find?event=|team=once the section is up.Also: SiriusXM failures are logged with what SXM said, so a failed sign-in can be read from the service logs. Genrewatch stays untouched; a test pins the brand default.
Tests: 1112 pass at the previous push; the password path adds three more against the fake gateway.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ac4GWmd6Nfd8JReX9u7kH1