Skip to content

feat(auth): partner region login (Jio, bro.game, etc.) - #64

Open
owenselles wants to merge 4 commits into
mainfrom
feat/partner-region-login
Open

feat(auth): partner region login (Jio, bro.game, etc.)#64
owenselles wants to merge 4 commits into
mainfrom
feat/partner-region-login

Conversation

@owenselles

Copy link
Copy Markdown
Owner

Summary

GeForce NOW is available via third-party partners in some regions — Jio in India, bro.game in Brazil, and potentially others. NVIDIA already exposes these via https://pcs.geforcenow.com/v1/serviceUrls, the same endpoint we already call in NVIDIAAuthAPI.fetchProviders(). The auth plumbing (idpId, streamingServiceUrl, AuthSession.provider) was already wired end-to-end — the only missing piece was surfacing provider choice in the UI.

  • LoginView: fetches providers on appear; shows one button per provider when multiple are returned (e.g. "NVIDIA", "Jio", "bro.game"), falling back to the existing single NVIDIA button if the fetch fails or only one provider is available
  • GamesViewModel: guards measureTopZones() to skip PrintedWaste zone discovery for non-nvidiagrid.net providers — prevents NVIDIA zone URLs from being passed to a partner session API; partner sessions use a nil zone hint and let the provider's own CloudMatch route the connection

No changes to auth endpoints, CloudMatchClient, GamesClient, or any streaming code — partner idpId federation happens server-side on login.nvidia.com.

Test plan

  • NVIDIA login: single button still appears, zone auto-selection still runs — no regression
  • In a partner region (or by temporarily hardcoding a partner provider): multiple buttons appear on login screen
  • Selecting a partner provider completes the QR/device flow and loads the games library
  • Launching a game creates the session against the partner's streamingServiceUrl with no zone hint

@aarikmudgal aarikmudgal added the enhancement New feature or request label Jul 10, 2026
@aarikmudgal

Copy link
Copy Markdown
Collaborator

@owenselles Thinking out loud, maybe we can check if this PR have some overlap with #67 . Maybe we should unify and combine the server/location feature.

@owenselles

Copy link
Copy Markdown
Owner Author

Agree also for testing this we need someone with a partner account

@RichiX9711

Copy link
Copy Markdown

Hi! I have an active GeForce NOW account through Digevo and I can help test the partner-provider implementation in PR #64.

I also have an Apple TV 4K 3rd generation, so I can test the complete flow on real hardware: provider detection, authentication, library loading, game launch, server routing, and streaming.

If needed, I can build and test the PR branch myself and provide logs or other diagnostic information.

@owenselles

Copy link
Copy Markdown
Owner Author

Yea @RichiX9711 if you can that would be of great help as no one else I know has a partner account

@owenselles

Copy link
Copy Markdown
Owner Author

So I have no real way to test and implement this. Current implementation is based on other open source apps

@RichiX9711

Copy link
Copy Markdown

I tested this PR on real hardware with an active Digevo GeForce NOW partner account in Peru.

Test environment

Apple TV 4K (3rd generation)
Active GeForce NOW subscription through Digevo
Tested with Rocket League
Branch: feat/partner-region-login
What works

The core partner authentication flow is working:

Digevo is correctly returned and displayed as a login provider.
Selecting Digevo starts the QR/PIN device authentication flow successfully.
Authentication completes successfully.
The game library loads correctly.
The account is recognized with the expected subscription tier (ULTIMATE in my test).
Rocket League launches successfully.
WebRTC signaling, audio/video tracks, and the game stream work.

The logs also confirm that the selected partner service URL is initially resolved correctly:

startSession: base=https://prod.DIG.geforcenow.nvidiagrid.net

So the provider selection and streamingServiceUrl plumbing are working with a real partner account.

Routing issue found

I found an issue with the current partner detection in GamesViewModel.measureTopZones().

The current check is:

let streamingUrl = authManager.session?.provider.streamingServiceUrl ?? ""
guard streamingUrl.contains("nvidiagrid.net") else { return }

This does not reliably distinguish NVIDIA's default service from a partner service.

Digevo's actual partner URL is:

https://prod.DIG.geforcenow.nvidiagrid.net

Because it also contains nvidiagrid.net, NVIDIA global zone discovery still runs for the Digevo account.

During my test, CloudNow measured zones such as:

NP-ATL-04
NP-CHI-04
NP-PHX-02

At game launch it selected:

[Zones] best at launch:
https://np-atl-04.cloudmatchbeta.nvidiagrid.net/
ping=101ms

Then the session was created with:

sessionBase=https://np-atl-04.cloudmatchbeta.nvidiagrid.net/
routingZoneUrl=https://np-atl-04.cloudmatchbeta.nvidiagrid.net/

and the POST was sent directly to:

https://np-atl-04.cloudmatchbeta.nvidiagrid.net/v2/session

Therefore, although the selected provider initially resolves to Digevo, the auto-zone logic replaces the partner streamingServiceUrl with a global NVIDIA CloudMatch zone before session creation.

This appears to be caused by the interaction between:

measureTopZones() allowing Digevo because its URL also contains nvidiagrid.net.
createNewSession() using bestZoneUrl() as both sessionBase and routingZoneUrl.

I think partner detection should be based on the selected provider/default NVIDIA service rather than a substring check. Partner sessions should keep the provider's streamingServiceUrl and use no NVIDIA global zone hint, unless partner-specific zone discovery is implemented.

It may also be worth enforcing this at session creation, not only in measureTopZones(), so a partner session cannot accidentally reuse previously measured or persisted NVIDIA zones.

Build issue

The branch also failed to compile because measureTopZones() references authManager, but authManager is not available in that method's scope.

I locally fixed it by changing:

func measureTopZones() async

to:

func measureTopZones(authManager: AuthManager) async

and changing the call in MainTabView to:

.task { await viewModel.measureTopZones(authManager: authManager) }

After that change, the branch compiled and I was able to complete the full Digevo test.

tvOS provider picker issue

I also found a UI/focus issue in the provider picker. There are more providers than can fit on screen. The focus can continue moving to providers outside the visible area, but the UI does not scroll correctly to keep the focused provider visible.

During this interaction I also saw repeated tvOS focus-engine warnings:

Ignoring attempt to add focus items in already-visited container.
This can potentially cause infinite recursion.

I cannot confirm yet whether those warnings are the direct cause of the scrolling issue, but the provider picker needs focus-aware scrolling on tvOS.

Suggested next step

I can prepare a small fix that:

fixes the authManager compile error;
distinguishes the default NVIDIA service from partner providers correctly;
prevents NVIDIA global auto-zone selection from overriding a partner's streamingServiceUrl.

I would keep the provider-picker scrolling issue separate because it is an independent tvOS UI/focus problem.

I can also continue testing the changes with my real Digevo account and Apple TV hardware.

@owenselles

Copy link
Copy Markdown
Owner Author

Working on fixes for those issues will push to this pr in a few mins @RichiX9711 Thanks a lot for testing and please check again when you got time :)

@aarikmudgal aarikmudgal linked an issue Jul 14, 2026 that may be closed by this pull request
@RichiX9711

Copy link
Copy Markdown

Hi guys, it's been a few days since my last comment,

I have completed the real-hardware validation of PR #64 using an active Digevo GeForce NOW account in Peru.

PR scope validation

The original goal of this PR — authenticating and launching games through a third-party GeForce NOW partner — is working successfully in my environment.

Test environment:

  • Apple TV 4K, 3rd generation
  • Active GeForce NOW subscription through Digevo
  • Provider region: Peru
  • Game used for testing: Rocket League
  • Branch: feat/partner-region-login

The following flow now works end to end:

  • Digevo is returned and displayed as an available login provider.
  • Selecting Digevo starts the QR/PIN authentication flow.
  • Authentication completes successfully.
  • The game library loads correctly.
  • The expected subscription tier is recognized.
  • Rocket League launches successfully.
  • WebRTC signaling, audio, video and controller input work.
  • The stream uses HEVC through GFNVideoToolboxH265.
  • The media connection uses direct UDP.
  • No TURN relay was used during the successful tests.

Based on these results, I consider the original functional scope of PR #64 complete.

Partner routing and automatic region selection

The official GeForce NOW macOS client only exposes one selectable region for my account, named “LATAM West”.

For CloudNow, however, I tested Digevo using provider-managed automatic routing instead of forcing that NVIDIA region manually.

The important behavior is:

  • Digevo retains its own streamingServiceUrl.
  • NVIDIA global zone measurement is not used as an override for the partner.
  • A previously measured or persisted NVIDIA zone is not reused for the Digevo session.
  • No NVIDIA global routingZoneHost is sent for the partner session.
  • Digevo CloudMatch selects the infrastructure automatically.
  • The successful sessions were assigned through NPA-DIG-SCL-01.
  • The resulting WebRTC path was direct UDP.

This is consistent with the changes already added to the PR that identify NVIDIA-direct sessions using the provider idpId, rather than checking whether the service URL contains nvidiagrid.net.

That distinction is necessary because Digevo’s own service URL also uses an NVIDIA Grid domain.

Additional local telemetry work

While testing Rocket League, I noticed visible stuttering during rapid camera movement. This appeared more frequently at 4K.

To determine whether the stuttering came from Digevo/NVIDIA, the Apple TV decoder, or CloudNow’s rendering pipeline, I added local diagnostic telemetry.

These diagnostics are not required for the partner-login functionality and are currently outside the intended scope of PR #64.

The local telemetry uses CloudNow’s existing one-second WebRTC statistics timer. It does not create another timer or perform an additional WebRTC statistics query.

In Diagnostic mode, it records:

  • one valid sample per second;
  • ten-second chronological windows;
  • per-second FPS and bitrate series;
  • FPS minimum, average and maximum;
  • counts below 60, 58, 55 and 50 FPS;
  • RTT, jitter and packet-loss statistics;
  • jitter-buffer delay;
  • VideoToolbox decode time;
  • WebRTC processing delay;
  • reset-safe deltas for cumulative counters;
  • dropped frames;
  • pipeline drops;
  • AVFoundation drops and corrupted frames;
  • freezes;
  • backpressure;
  • renderer failures and flushes;
  • candidate-pair changes;
  • rate-limited anomaly events.

I also corrected the interval packet-loss calculation so that it is reset for every statistics sample and cannot reuse the previous interval’s percentage.

Comparative tests

I performed separate 1080p and 4K Rocket League sessions on the same Apple TV and provider account.

1080p

During the stable gameplay portion:

  • Average FPS was approximately 59.97.
  • FPS standard deviation was approximately 0.33.
  • No samples fell below 55 FPS.
  • Only one sample fell below 58 FPS.
  • Decode time averaged approximately 8.1 ms.
  • WebRTC processing delay averaged approximately 13.3 ms.
  • The stream remained effectively locked to 60 FPS.

4K

During the stable gameplay portion:

  • Average FPS was approximately 57.96.
  • FPS standard deviation was approximately 2.96.
  • Approximately 52% of samples were below 60 FPS.
  • Approximately 32% were below 58 FPS.
  • Approximately 13% were below 55 FPS.
  • The minimum observed value was 42 FPS.
  • Some sequences remained below 60 FPS for more than 20 consecutive seconds.
  • Decode time averaged approximately 12.5 ms.
  • WebRTC processing delay averaged approximately 18.1 ms.

The camera-motion stuttering was consistent with these chronological FPS drops.

Current findings

The network does not currently appear to be the main cause:

  • RTT remained close to 32–33 ms.
  • Jitter was normally close to 5 ms.
  • Packet loss was zero or negligible during almost all severe FPS drops.
  • The jitter buffer remained stable.
  • The connection remained direct UDP.
  • There were no candidate-pair changes.

The existing local pipeline counters also remained at zero:

  • no WebRTC frame drops;
  • no pipeline frame drops;
  • no AVFoundation frame drops;
  • no corrupted frames;
  • no freezes;
  • no backpressure;
  • no renderer failures;
  • no renderer flushes.

The Apple TV requires more decode time at 4K than at 1080p, as expected, but the measured 4K decode time remained below the approximately 16.67 ms frame interval for 60 Hz.

There were also complete 4K windows that maintained 60 FPS with similar decode times. Therefore, the current data does not demonstrate decoder saturation or that the Apple TV is incapable of decoding the stream.

The most significant observation is that, during severe 4K FPS reductions, bitrate decreases almost exactly in proportion to the number of frames.

For example, a reduction from approximately 60 FPS at 95 Mbps to 42 FPS resulted in approximately 66.6 Mbps. The approximate number of bits per frame remained almost constant.

This suggests that fewer frames may already be produced or delivered during those intervals, rather than CloudNow receiving 60 complete frames and dropping them locally.

However, the current fps metric does not conclusively separate:

  • frames received;
  • frames decoded;
  • frames submitted to the renderer;
  • frames actually presented.

Because of that, I do not think the logs are sufficient to assign the problem definitively to Digevo/NVIDIA, VideoToolbox, or CloudNow’s presentation pipeline.

There is also one uncontrolled variable in the comparison: the 1080p and 4K sessions were assigned to different Digevo infrastructure hosts, although both were routed through the same Digevo CloudMatch service.

Suggested follow-up outside PR #64

The next useful diagnostic step would be to instrument the complete frame path:

received → decoded → queued → rendered → presented

In particular:

  • framesReceivedDelta
  • framesDecodedDelta
  • frames submitted to the CloudNow renderer
  • frames replaced or dropped before presentation
  • frames actually presented
  • late or duplicated frames
  • renderer queue depth
  • presentation interval variance
  • effective output refresh rate, including 59.94 Hz versus 60.00 Hz

This would establish the responsible stage:

  • If framesReceived falls, the issue is upstream in the remote stream, encoder or infrastructure.
  • If received frames remain at 60 but decoded frames fall, the issue is in VideoToolbox or its integration.
  • If decoded frames remain at 60 but presented frames fall, the issue is in CloudNow’s renderer or frame pacing.
  • If all stages remain at 60 while stuttering is visible, the remaining area would be output cadence, HDMI or television processing.

Sharing the evidence

I can provide sanitized versions of the 1080p and 4K telemetry logs. Before sharing them publicly, I will remove or normalize authentication data, account identifiers, request identifiers, local addresses, session-specific values and any potentially sensitive infrastructure details.

I also have the diagnostic instrumentation as a local code change. Since it is outside the scope of PR #64, I have not assumed that it should be added to this branch.

Please let me know which form would be most useful:

  • sanitized log files attached to this PR;
  • a separate issue containing the comparison and conclusions;
  • a minimal .patch containing only the diagnostic instrumentation;
  • or a separate draft PR from a fork, if the maintainers want to review the telemetry implementation.

For now, my conclusion is that the third-party login and provider-managed Digevo routing are working correctly, while the 4K stuttering investigation should be treated as a separate diagnostic topic.

@owenselles

Copy link
Copy Markdown
Owner Author

Nicee good that it works now!

owenselles commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

@RichiX9711 Thank you for the thorough validation report — this is exactly the kind of real-hardware evidence needed to confirm the partner routing is correct.

Two things addressed based on your findings:

Provider picker scrolling — fixed. The ScrollView + VStack was replaced with a ScrollViewReader + @FocusState approach that programmatically scrolls to whichever button gains focus. This should eliminate the "already-visited container" warnings and keep the focused provider visible regardless of how many providers are returned.

Partner routing guard — the isNvidiaProvider check is now also applied to the new serverRoutingMode switch added in #92 (Automatic / Region / Servers). Partner sessions now always use (base, nil) — the provider's own streamingServiceUrl with no NVIDIA zone or region override — even if the user has a Region or Servers routing mode selected from Settings.

For the 4K diagnostic work — a separate draft PR from your fork with just the telemetry instrumentation would be ideal. That keeps the frame-path instrumentation reviewable on its own and avoids scope creep here. If you open it I'll take a look. The 4K stuttering investigation findings (frames-received vs decoded vs rendered breakdown) are worth tracking separately too — feel free to open an issue for that so it doesn't get lost.

@owenselles
owenselles force-pushed the feat/partner-region-login branch from 6f85002 to bd2071c Compare July 19, 2026 17:05
- LoginView: fetch providers on appear and show a scrollable picker when
  multiple providers are available; @focusstate drives ScrollViewReader to
  keep the focused button centred (fixes tvOS focus-engine blind spot)
- StreamView: guard NVIDIA zone/region selection with isNvidiaProvider so
  partner sessions route through their own infrastructure unchanged
- README: document partner provider support and update requirements line
@owenselles
owenselles force-pushed the feat/partner-region-login branch from bd2071c to 6f6ecad Compare July 19, 2026 17:13
@owenselles
owenselles marked this pull request as ready for review July 19, 2026 17:13
@owenselles
owenselles requested a review from aarikmudgal July 19, 2026 17:13
@aarikmudgal

aarikmudgal commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@owenselles The approach looks directionally good to me. I especially like that the routing fix moved away from checking whether the streaming URL contains nvidiagrid.net; using the provider idpId against the default NVIDIA ID is much safer, since some partner endpoints can still live under nvidiagrid.net.

Before merging, I think there are a few things worth tightening up:

  1. Avoid showing the default NVIDIA login before provider loading finishes

    Right now the UI appears to fall back to the normal NVIDIA sign-in button until the provider fetch completes. In a partner region, that means a user could potentially click “Sign in with NVIDIA” before the provider list has loaded and accidentally start the wrong auth flow.

    I think we should model provider loading explicitly, e.g. loading / loaded / failed, and show a loading state or disabled button while the provider list is being fetched. If the fetch fails, then falling back to the current NVIDIA sign-in flow is fine.

  2. Make partner routing behavior visible in Settings

    The session-routing change makes sense: partner providers should use their own streamingServiceUrl and not apply NVIDIA manual region / zone overrides.

    However, if a user is signed in through a partner provider, the Settings UI can still make it look like NVIDIA server location options are available. Those settings would then be silently ignored when creating a partner session, which could be confusing.

    I think we should either hide those server-location controls for partner sessions or disable them with a short explanation like “Server location is managed by your selected partner provider.”

  3. Ensure provider picker focus is deterministic on tvOS

    The scrolling/focus fix looks like the right direction, but I’d like to make sure the picker always has a sensible initial focused item once providers load. If focusedProvider is only updated after the user moves focus, tvOS may choose something unpredictable, especially in a scrollable list.

    It would be good to explicitly set the initial focused provider after loading, probably to the first/default provider if focusedProvider == nil.

  4. Consider centralizing the NVIDIA-direct check

    Since the distinction between NVIDIA-direct and partner providers is important for routing, settings behavior, and possibly future UI, I think it would be cleaner to centralize this in one helper instead of repeating the comparison inline.

    For example, something like:

    var isNvidiaDirect: Bool {
        idpId == NVIDIAAuth.defaultIdpId
    }

- LoginProvider.isNvidiaDirect: centralizes the idpId==defaultIdpId check
  so it is not repeated inline across StreamView and SettingsView
- LoginView: replace the nil-providers fallback with an explicit
  ProvidersState enum (.loading / .loaded); shows a spinner while the
  provider list is fetching so a partner-region user cannot accidentally
  tap the NVIDIA button before providers arrive; sets initial @focusstate
  to the first provider for deterministic tvOS focus on load
- SettingsView: hide the server-location picker for partner sessions and
  replace it with a "Managed by partner provider" note, since those
  settings are silently ignored for partner routing anyway
- Localization: add managed_by_partner key to all 34 locale tables
@aarikmudgal

Copy link
Copy Markdown
Collaborator

@owenselles I reviewed the latest head (153159a) against the previous feedback, the full provider-routing path, current CI, and today’s main.

The latest commit adds the requested loading state, partner-managed Settings message, initial provider focus, and centralized isNvidiaDirect helper. The PR still needs the following changes.

MAJOR — Partner session creation can still fall back to NVIDIA

  • Prevent NVIDIA’s default CloudMatch endpoint from being used as a fallback for partner sessions.

CloudMatchClient.createSession() currently builds:

let bases = preferredBase == fallbackBase
    ? [preferredBase]
    : [preferredBase, fallbackBase]

Every partner endpoint differs from Self.defaultBase, so a non-200 response from the partner causes the same session request and token to be sent to NVIDIA’s global endpoint.

This bypasses the new partner-routing guarantee and could:

  • create a session through NVIDIA instead of the selected partner;
  • send a partner token to the wrong endpoint;
  • replace the useful partner error with the fallback error;
  • interfere with the existing active-session conflict recovery.

The fallback policy should be explicit:

  • NVIDIA-direct session using a manual region/server: partner-independent NVIDIA fallback may remain allowed.
  • Partner session: only attempt the provider’s streamingServiceUrl; never append NVIDIA’s default endpoint.

Please test a simulated partner HTTP failure and confirm that no request is sent to prod.cloudmatchbeta.nvidiagrid.net.


MAJOR — A single returned partner is treated as NVIDIA

  • Preserve and display a successfully fetched single provider.

LoginView only uses the fetched provider objects when providers.count > 1.

For .loaded([oneProvider]), it falls into the generic .loaded branch, displays “Sign in with NVIDIA,” and calls authManager.login() without the known provider.

If the endpoint returns one partner provider, the UI therefore:

  • displays the wrong provider name;
  • discards the fetched LoginProvider;
  • performs another provider fetch;
  • may fall back to NVIDIA if the second fetch fails.

Please distinguish these states explicitly:

  • .loading
  • .loaded([]) or .failed
  • .loaded([singleProvider])
  • .loaded([multipleProviders])

A single provider should use its own displayName and call:

authManager.login(with: provider)

The NVIDIA fallback should only appear after an actual provider-fetch failure or when the sole provider is confirmed as NVIDIA-direct.


MAJOR — “Try Again” forgets the selected partner

  • Retry authentication with the provider the user originally selected.

failedView currently calls:

authManager.login()

If the user selected a non-first partner provider and authentication fails, “Try Again” fetches the providers again and selects providers.first. This can silently switch the retry to NVIDIA or another partner.

Please retain the attempted provider in LoginView or AuthManager and retry with:

authManager.login(with: selectedProvider)

“Cancel” can continue returning the user to the provider picker.


MAJOR — Network Test can still test a stale NVIDIA route for partner users

  • Apply the partner-routing rule to NetworkTestView.resolveTarget() and its server-info cache.

The Settings picker is hidden for partner sessions, but NetworkTestView.resolveTarget() checks persisted serverRoutingMode before checking the current provider.

A partner account can therefore still test:

  • a previously selected NVIDIA region;
  • a previously selected NVIDIA dedicated server;
  • a cached NVIDIA GFNServerInfo.

ServerInfoClient.shared.cached is also not keyed by provider or base URL. After switching providers without restarting the app, the Network Test can reuse the previous provider’s region data.

For partner sessions:

  • ignore persisted .region and .client overrides;
  • resolve the target from the current partner’s streamingServiceUrl;
  • key or validate cached server info by normalized provider base URL;
  • never display a result obtained from a previous provider’s cache.

Please test NVIDIA-direct → logout → partner login → Network Test without restarting the app.


MAJOR — Persisted session and VPC state has no provider/account identity

  • Validate or namespace provider-dependent persisted state by provider and user.

LastSessionRecord, persisted VPC data, and related cached account data do not include idpId or userId.

After signing out and selecting another provider, GamesViewModel reloads the previous state. StreamView can then attempt to claim or stop the previous session using the new provider’s token and the previous provider’s base URL.

The cached VPC ID is also used immediately while revalidation happens in the background. A newly selected partner can therefore receive initial catalog/library requests containing an NVIDIA or different-partner VPC ID.

Suggested handling:

  • store idpId and userId with LastSessionRecord;
  • discard incompatible session records locally instead of contacting the old endpoint with the new token;
  • associate persisted VPC data with the same provider/user identity;
  • synchronously resolve a new VPC when provenance changes;
  • clear or namespace account-specific subscription/library caches when switching identities.

This is especially important for partner-to-partner switching because it avoids sending one partner’s token to another partner’s endpoint.


Localization follow-up

MINOR — New text is English in every locale

  • Translate managed_by_partner in the non-English locale tables.

The new key currently contains "Managed by partner provider" in all locale files. This prevents missing-key failures but displays English for every supported non-English language.

claude added 2 commits July 22, 2026 07:56
…on identity, cache keying, l10n

- CloudMatchClient: partner sessions no longer fall back to NVIDIA's global
  endpoint; SessionCreateRequest gains skipNvidiaFallback which gates the
  [preferredBase, fallbackBase] retry list to [preferredBase] only for partners.

- LoginView: add @State selectedProvider so the correct provider is carried
  through to "Try Again" after a login failure; handle the single-provider
  case explicitly so a solo partner provider shows its own displayName and
  calls login(with:) instead of the NVIDIA fallback.

- LastSessionRecord: add idpId field (backward-compat decode defaults to
  NVIDIAAuth.defaultIdpId); GamesViewModel.load() discards a persisted session
  whose idpId doesn't match the current provider before restoring it.

- StreamView: pass skipNvidiaFallback and the current provider's idpId when
  creating and persisting a session.

- ServerInfoClient: add cachedBase tracking and cachedForBase(_:) so the
  cache is provider-keyed; SettingsView NetworkTest resolveTarget() guards
  routing-mode branches with isNvidiaSession and uses cachedForBase().

- Localization: translate managed_by_partner into all 33 non-English locales.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Login with third-party providers

4 participants