Skip to main content

Provider Management

Overview

Provider Management allows admins to globally enable or disable platform providers (Twitch, YouTube, Kick, Trovo, Discord, Spotify) with granular controls per connection type. Each provider has a kill-switch that disables everything, plus sub-flags for the connection types it actually supports — login, channel, and/or bot. The specific mix of sub-flags varies per platform:

  • Twitch, YouTube, Kick, Trovo — login + channel + bot.
  • Discord — login + bot (no channel: Discord is used for auth + the bot, not per-channel OAuth streams).
  • Spotify — channel only (per-account OAuth for music/event integration; there is no Spotify login and no Spotify chat bot). The public enabledPlatforms GraphQL query filters platforms down to those with an enabled :login or :bot sub-flag, so Spotify is intentionally excluded from the "Supported Platforms" list on pricing cards while still being usable as a per-account music integration.

Capability registry

apps/api/src/platforms.rs (PLATFORMS) is the single source of truth for what each platform can do. It declares four booleans — has_login, has_channel, has_bot, has_integration — and both the admin Providers page and the capability-aware GraphQL guards read it:

PlatformLoginChannelBotIntegration
twitchxxx
youtubexxx
kickxxx
trovoxxx
discordxxx
spotifyx
shopifyx

Shopify is registry-only: it has no login, channel or bot capability, so GET /v1/admin/providers and adminProviders filter it out and it never appears on the Provider Management page. Its enablement lives on the integration:shopify feature flag instead. Discord's integration capability covers discord_guild_connections, which is managed on its own screens, not through the sub-flag toggles.

Feature Flag Hierarchy

Each provider has a kill-switch and sub-flags for different connection types:

platform:twitch <- Kill-switch (disables everything)
+-- platform:twitch:login <- Login via Twitch
+-- platform:twitch:channel <- Channel Connection
+-- platform:twitch:bot <- Bot Connection

The seeded flags (seed_default_feature_flags in apps/api/src/db/admin.rs) are the six kill-switches plus: :login for twitch/youtube/kick/trovo/discord, :channel for twitch/youtube/kick/trovo/spotify, and :bot for twitch/youtube/kick/trovo/discord.

Resolution Logic

  1. If the kill-switch (platform:{provider}) is disabled -- everything is blocked
  2. If the kill-switch is enabled -- check the sub-flag:
    • platform:{provider}:login -- controls login availability
    • platform:{provider}:channel -- controls channel connections
    • platform:{provider}:bot -- controls bot connections
  3. If a sub-flag doesn't exist -- denied. Resolution is fail-closed: FeatureService::is_enabled treats a key that is absent from the global cache as disabled, and get_enabled_providers skips any platform whose platform:{p}:{subtype} key is missing. An unseeded combination (e.g. platform:spotify:bot) is therefore never "allowed by default".

Both levels also honour per-account overrides, so is_provider_enabled(platform, subtype, Some(account_id)) can differ from the global answer.

The global flag cache is refreshed on a 5-minute cycle, but every admin provider toggle calls refresh_global_cache() inline, so a toggle takes effect immediately.

Soft-Block Behavior

When a provider or sub-flag is disabled:

  • New connections/logins are blocked
  • Existing connections continue working
  • Token refresh continues
  • Global bots keep running
  • Per-account overrides are possible via the admin panel

Architecture

Backend

  • Platform registry (apps/api/src/platforms.rs) -- PLATFORMS capability table plus lookup(), used to validate the {platform} path segment (an unknown slug returns 400 Unknown platform: …) and to decide which sub-flag toggles exist.
  • FeatureService (apps/api/src/services/feature_service.rs) -- Core service with is_provider_enabled(platform, subtype, account_id) for two-level flag checks, get_enabled_providers(subtype) for listing enabled platforms per connection type, get_enabled_platforms() for kill-switch-level queries, and get_streaming_platforms() backing enabledPlatforms.
  • GraphQL (apps/api/src/graphql/connections.rs, apps/api/src/graphql/admin.rs) -- enabledProviders(connectionType), enabledPlatforms, connectionStatuses, and the admin adminProviders query with its four set/toggle mutations. Primary path used by all frontend apps.
  • REST (apps/api/src/routes/features.rs) -- Public GET /v1/providers/enabled?type={login|channel|bot} endpoint, kept for external SDK consumers (CLIs, bots, scripts). Frontend apps do not call this — they route through the GraphQL resolver via serverGqlSSR to keep the "Frontend → Next.js proxy → GraphQL" pattern consistent.
  • Admin REST (apps/api/src/routes/admin.rs) -- Admin endpoints for provider stats, enable/toggle, and account connection management.

Frontend

  • ID App -- SSR login page (apps/id/src/app/login/page.tsx) and signIn callback (apps/id/src/auth.ts) call enabledProviders(connectionType: "login") via serverGqlSSR to gate login buttons.
  • Web App -- Connection flow checks provider enablement before showing connect options.
  • Admin App -- Dedicated Provider Management page for toggling flags and viewing stats.

Admin Management

Provider Page

The admin panel includes a dedicated Provider Management page at /providers (apps/admin/src/app/(admin)/providers/), gated on providers:read. Admins can:

  • Toggle the kill-switch per platform
  • Toggle individual sub-flags (Login / Channel / Bot) — only the toggles the platform's capabilities declare are rendered
  • View system credential status (system_credentials_configured) with a link to configure credentials when unset
  • View connection counts (login_connection_count, channel_connection_count, bot_connection_count, integration_count)
  • Connect or disconnect the global bot for platforms with has_bot, which requires system credentials to be configured first

Account Connections

On the account detail page, admins can:

  • View and delete channel connections
  • View and delete bot connections
  • View and delete StreamElements tokens

User Login Connections

On the user detail page, admins can delete individual login connections.

API

Public Endpoints

MethodPathPermissionDescription
GET/v1/providers/enabled?type={login|channel|bot}none (public)List enabled providers

Admin Endpoints

MethodPathPermissionDescription
GET/v1/admin/providersproviders:readList all providers with stats (integration-only platforms filtered out)
PATCH/v1/admin/providers/{platform}/enabledproviders:editSet the kill-switch to an explicit value; returns the refreshed provider stats
PATCH/v1/admin/providers/{platform}/{subtype}/enabledproviders:editSet a sub-flag to an explicit value; returns the refreshed provider stats
PATCH/v1/admin/providers/{platform}/togglefeatures:editFlip the kill-switch; returns the raw feature flag
PATCH/v1/admin/providers/{platform}/{subtype}/togglefeatures:editFlip a sub-flag; returns the raw feature flag
GET/v1/admin/accounts/{id}/connectionsaccounts:readList channel connections
DELETE/v1/admin/accounts/{id}/connections/{platform}accounts:editDelete channel connection
GET/v1/admin/accounts/{id}/bot-connectionsaccounts:readList bot connections
DELETE/v1/admin/accounts/{id}/bot-connections/{platform}accounts:editDelete bot connection
GET/v1/admin/accounts/{id}/se-tokensse-tokens:readList SE tokens
DELETE/v1/admin/accounts/{id}/se-tokens/{token_id}se-tokens:deleteDelete SE token
DELETE/v1/admin/users/{id}/login-connections/{provider}users:editDelete login connection

The /enabled and /toggle variants both exist and act on the same feature flags. /enabled is idempotent (you send the target state) and is what the admin UI uses; /toggle flips the current value and is guarded by the broader features:edit rather than providers:edit.

GET /v1/admin/accounts/{id}/connections carries no utoipa::path annotation, so it is absent from apps/api/openapi.json even though the route is live.

GraphQL

TypeNameGuardDescription
QueryenabledProviders(connectionType: String!): [String!]!noneList enabled providers for login / channel / bot
QueryenabledPlatforms: [String!]!noneKill-switch-enabled platforms that also have an enabled :login or :bot sub-flag. Used by the public pricing page.
QueryconnectionStatuses: [ConnectionStatus!]!connections:readPlatform statuses with enabled field
QuerybotConnectionStatuses: [BotConnectionStatus!]!bot-connections:readBot platform statuses with enabled field
QueryadminProviders: [ProviderInfo!]!providers:read (admin)Providers with capabilities, connection counts and flag status — the query the admin page uses
MutationadminSetProviderEnabled(platform, enabled): Boolean!providers:edit (admin)Set the kill-switch to an explicit value
MutationadminSetProviderSubtypeEnabled(platform, subtype, enabled): Boolean!providers:edit (admin)Set a sub-flag to an explicit value
MutationadminToggleProvider(platform): ProviderInfo!providers:edit (admin)Flip the kill-switch, return refreshed ProviderInfo
MutationadminToggleProviderSubtype(platform, subtype): ProviderInfo!providers:edit (admin)Flip a sub-flag, return refreshed ProviderInfo

Admin mutations use AdminPermissionGuard (admin-role permissions), not the account-scoped PermissionGuard.

WebSocket

Provider enablement has no WebSocket channel — there is no providers arm in channel_gate_for. Clients pick up a flag change on their next query; the server-side cache is refreshed inline on every toggle.

Per-Account Overrides

Admins can override provider flags per account using the existing account feature override system. On the account detail page, toggle any platform:* or platform:*:* flag in the Feature Overrides tab.

Key Files

PathDescription
apps/api/src/services/feature_service.rsCore feature flag resolution logic
apps/api/src/routes/features.rsPublic providers REST endpoint
apps/api/src/routes/admin.rsAdmin provider management endpoints
apps/api/src/db/admin.rsFeature flag DB operations and seed data
apps/api/src/graphql/connections.rsGraphQL queries with provider enablement
apps/api/src/platforms.rsPLATFORMS capability registry (login / channel / bot / integration)
apps/api/src/graphql/admin.rsadminProviders + the four admin provider mutations
apps/admin/src/app/(admin)/providers/provider-management.tsxAdmin Provider Management UI

See Also