#14640·onyx

Feature Request: only_index_joined_channels option for the Slack (indexed) connector

Author: jrotensteinCreated Sep 11, 2026Updated Sep 11, 2026

Written by OnyxBot See chat

Is your feature request related to a problem?

Yes. The indexed Slack connector has no way to scope indexing to "channels the bot has been invited to."

Channel discovery is enumerate-then-join:

  1. get_channels() calls conversations.list with public_channel, which returns every public channel in the workspace regardless of bot membership.
  2. filter_channels() filters that list purely by name (include list → exclude list). is_member is never a filter input.
  3. is_member is only read in order to auto-join:
# join so that the bot can access messages
if not channel["is_member"]:
    slack_client.join_channel(channel_id=channel["id"])
    logger.info("Successfully joined '%s'", channel["name"])

So bot membership is an output of the connector, never an input. Consequences:

  • In a large workspace the connector joins and indexes hundreds of irrelevant channels (alerts, bots, social, archived-but-not-archived, etc.).
  • Kicking the bot out of a channel doesn't stick — the next run re-joins it.
  • The only way to scope is a name-based channels allowlist, which has to be maintained centrally. Because the admin UI config block is display-only, adding a channel means a PATCH to /api/manage/admin/connector/ with the full body — not something a channel owner can self-serve.
  • Workspaces that rename channels or add them frequently need constant config churn.

The natural, Slack-native signal for "this channel should be indexed" is /invite @Onyx, and the connector can't use it.

Describe the solution you'd like

A boolean connector option, e.g. only_index_joined_channels (default false`, so existing behavior is unchanged): When true:

  • Skip the auto-join entirely. Never call conversations.join.
  • Filter on membership. In filter_channels() (or immediately afterget_channels()), drop any channel where is_member is False.
  • Channels are therefore opted in by inviting the bot, and opted out by removing it.
  • channels / exclude_channels should still apply, composed as an AND: a channel is indexed iff (bot is a member) AND (matches include rules) AND (not excluded). That lets an admin set a hard boundary while still delegating opt-in to channel owners.
  • The channels:join scope becomes unnecessary in this mode. Onyx's Slack capability check already models this as a legitimate, non-required deployment("a bot manually invited to every configured channel indexes fine without the scope"), so the capability check could be made aware of the flag and suppress the warning when it's set.

Ideally exposed as a checkbox in the connector form, labelled something like "Only index channels the bot has been invited to".

Describe alternatives you've considered

  • channels allowlist — works, but it's a central, API-edited list rather than an invite-driven workflow, and exact mode hard-fails the run on a typo'd/renamed channel (Channel 'X' not found in workspace).
  • Regex allowlist (channel_regex_enabled) — only helps if the channels you want share a naming convention, and re.fullmatch semantics trip people up. Removing the channels:join scope — approximates the behavior, but by failing: every non-member public channel raises missing_scope, which surfaces as a ConnectorFailure / EntityFailure on every single run. Permanent red index attempts that bury real errors, wasted rate-limit budget on calls guaranteed to fail, and it doesn't un-join anything the bot already joined. Using the failure list as the channel-config UI is inverted: an allowlist expresses intent, a pile of missing_scope errors expresses an accident.
  • Private channels — already have exactly these semantics, since conversations.list with private_channel only returns channels the bot is a member of. Converting public channels to private just to get this behavior isn't viable.
  • Federated Slack connector — has search_all_channels: false + a glob-basedchannels list editable in the UI, which is easier to maintain, but it's still an allowlist (not invite-driven), and Onyx recommends the indexed connector for result quality.

Additional context

This was previously requested in #236 ("Index only channels that the bot has been invited to"), which was auto-closed as stale without being implemented.

The implementation looks small: gate the join_channel call and add an is_member predicate to the channel filter. The trickiest part is probably deciding how it composes with the existing include/exclude rules (proposal above: AND) and whether already-indexed out-of-scope documents get cleaned up by the normal pruning path. Relevant code:

backend/onyx/connectors/slack/connector.py
(get_channels, filter_channels, _load_from_checkpoint).