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
enabledPlatformsGraphQL query filters platforms down to those with an enabled:loginor:botsub-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:
| Platform | Login | Channel | Bot | Integration |
|---|---|---|---|---|
twitch | x | x | x | |
youtube | x | x | x | |
kick | x | x | x | |
trovo | x | x | x | |
discord | x | x | x | |
spotify | x | |||
shopify | x |
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
- If the kill-switch (
platform:{provider}) is disabled -- everything is blocked - If the kill-switch is enabled -- check the sub-flag:
platform:{provider}:login-- controls login availabilityplatform:{provider}:channel-- controls channel connectionsplatform:{provider}:bot-- controls bot connections
- If a sub-flag doesn't exist -- denied. Resolution is fail-closed:
FeatureService::is_enabledtreats a key that is absent from the global cache as disabled, andget_enabled_providersskips any platform whoseplatform:{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) --PLATFORMScapability table pluslookup(), used to validate the{platform}path segment (an unknown slug returns400 Unknown platform: …) and to decide which sub-flag toggles exist. - FeatureService (
apps/api/src/services/feature_service.rs) -- Core service withis_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, andget_streaming_platforms()backingenabledPlatforms. - GraphQL (
apps/api/src/graphql/connections.rs,apps/api/src/graphql/admin.rs) --enabledProviders(connectionType),enabledPlatforms,connectionStatuses, and the adminadminProvidersquery with its four set/toggle mutations. Primary path used by all frontend apps. - REST (
apps/api/src/routes/features.rs) -- PublicGET /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 viaserverGqlSSRto 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) callenabledProviders(connectionType: "login")viaserverGqlSSRto 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
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /v1/providers/enabled?type={login|channel|bot} | none (public) | List enabled providers |
Admin Endpoints
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /v1/admin/providers | providers:read | List all providers with stats (integration-only platforms filtered out) |
| PATCH | /v1/admin/providers/{platform}/enabled | providers:edit | Set the kill-switch to an explicit value; returns the refreshed provider stats |
| PATCH | /v1/admin/providers/{platform}/{subtype}/enabled | providers:edit | Set a sub-flag to an explicit value; returns the refreshed provider stats |
| PATCH | /v1/admin/providers/{platform}/toggle | features:edit | Flip the kill-switch; returns the raw feature flag |
| PATCH | /v1/admin/providers/{platform}/{subtype}/toggle | features:edit | Flip a sub-flag; returns the raw feature flag |
| GET | /v1/admin/accounts/{id}/connections | accounts:read | List channel connections |
| DELETE | /v1/admin/accounts/{id}/connections/{platform} | accounts:edit | Delete channel connection |
| GET | /v1/admin/accounts/{id}/bot-connections | accounts:read | List bot connections |
| DELETE | /v1/admin/accounts/{id}/bot-connections/{platform} | accounts:edit | Delete bot connection |
| GET | /v1/admin/accounts/{id}/se-tokens | se-tokens:read | List SE tokens |
| DELETE | /v1/admin/accounts/{id}/se-tokens/{token_id} | se-tokens:delete | Delete SE token |
| DELETE | /v1/admin/users/{id}/login-connections/{provider} | users:edit | Delete 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
| Type | Name | Guard | Description |
|---|---|---|---|
| Query | enabledProviders(connectionType: String!): [String!]! | none | List enabled providers for login / channel / bot |
| Query | enabledPlatforms: [String!]! | none | Kill-switch-enabled platforms that also have an enabled :login or :bot sub-flag. Used by the public pricing page. |
| Query | connectionStatuses: [ConnectionStatus!]! | connections:read | Platform statuses with enabled field |
| Query | botConnectionStatuses: [BotConnectionStatus!]! | bot-connections:read | Bot platform statuses with enabled field |
| Query | adminProviders: [ProviderInfo!]! | providers:read (admin) | Providers with capabilities, connection counts and flag status — the query the admin page uses |
| Mutation | adminSetProviderEnabled(platform, enabled): Boolean! | providers:edit (admin) | Set the kill-switch to an explicit value |
| Mutation | adminSetProviderSubtypeEnabled(platform, subtype, enabled): Boolean! | providers:edit (admin) | Set a sub-flag to an explicit value |
| Mutation | adminToggleProvider(platform): ProviderInfo! | providers:edit (admin) | Flip the kill-switch, return refreshed ProviderInfo |
| Mutation | adminToggleProviderSubtype(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
| Path | Description |
|---|---|
apps/api/src/services/feature_service.rs | Core feature flag resolution logic |
apps/api/src/routes/features.rs | Public providers REST endpoint |
apps/api/src/routes/admin.rs | Admin provider management endpoints |
apps/api/src/db/admin.rs | Feature flag DB operations and seed data |
apps/api/src/graphql/connections.rs | GraphQL queries with provider enablement |
apps/api/src/platforms.rs | PLATFORMS capability registry (login / channel / bot / integration) |
apps/api/src/graphql/admin.rs | adminProviders + the four admin provider mutations |
apps/admin/src/app/(admin)/providers/provider-management.tsx | Admin Provider Management UI |
See Also
- Bot Connections -- Custom bot identities per account
- Connections -- Channel connection OAuth flow
- Token Refresh -- Automatic token refresh worker