Skip to main content
OpenFang connects to messaging platforms through 40 channel adapters, allowing users to interact with their agents across every major communication platform. Adapters span consumer messaging, enterprise collaboration, social media, community platforms, privacy-focused protocols, and generic webhooks.

Common Features

All adapters share a common foundation:

Graceful Shutdown

Coordinated shutdown via watch channel

Resilient Connections

Exponential backoff on connection failures

Secret Management

Zeroizing strings for secure token handling

Message Splitting

Automatic splitting for platform limits

Flexible Overrides

Per-channel model and prompt customization

Policy Enforcement

DM/group policy control per channel

Rate Limiting

Per-user rate limiting protection

Format Support

Markdown, Telegram HTML, Slack Mrkdwn, plain text

All 40 Channels

Core Platforms (7)

ChannelProtocolEnvironment VariablesType
TelegramBot API long-pollingTELEGRAM_BOT_TOKENTelegram
DiscordGateway WebSocket v10DISCORD_BOT_TOKENDiscord
SlackSocket Mode WebSocketSLACK_BOT_TOKEN, SLACK_APP_TOKENSlack
WhatsAppCloud API webhookWA_ACCESS_TOKEN, WA_PHONE_ID, WA_VERIFY_TOKENWhatsApp
Signalsignal-cli REST/JSON-RPC(system service)Signal
MatrixClient-Server API /syncMATRIX_TOKENMatrix
EmailIMAP + SMTPEMAIL_PASSWORDEmail

Enterprise (8)

ChannelProtocolEnvironment VariablesType
Microsoft TeamsBot Framework v3 webhook + OAuth2TEAMS_APP_ID, TEAMS_APP_SECRETTeams
MattermostWebSocket + REST v4MATTERMOST_TOKEN, MATTERMOST_URLMattermost
Google ChatService account webhookGOOGLE_CHAT_SA_KEY, GOOGLE_CHAT_SPACECustom("google_chat")
WebexBot SDK WebSocketWEBEX_BOT_TOKENCustom("webex")
Feishu / LarkOpen Platform webhookFEISHU_APP_ID, FEISHU_APP_SECRETCustom("feishu")
Rocket.ChatREST pollingROCKETCHAT_TOKEN, ROCKETCHAT_URLCustom("rocketchat")
ZulipEvent queue long-pollingZULIP_EMAIL, ZULIP_API_KEY, ZULIP_URLCustom("zulip")
XMPPXMPP protocolXMPP_JID, XMPP_PASSWORD, XMPP_SERVERCustom("xmpp")

Social Media (8)

ChannelProtocolEnvironment VariablesType
LINEMessaging API webhookLINE_CHANNEL_SECRET, LINE_CHANNEL_TOKENCustom("line")
ViberBot API webhookVIBER_AUTH_TOKENCustom("viber")
Facebook MessengerPlatform API webhookMESSENGER_PAGE_TOKEN, MESSENGER_VERIFY_TOKENCustom("messenger")
MastodonStreaming API WebSocketMASTODON_TOKEN, MASTODON_INSTANCECustom("mastodon")
BlueskyAT Protocol WebSocketBLUESKY_HANDLE, BLUESKY_APP_PASSWORDCustom("bluesky")
RedditOAuth2 pollingREDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USERNAME, REDDIT_PASSWORDCustom("reddit")
LinkedInMessaging API pollingLINKEDIN_ACCESS_TOKENCustom("linkedin")
TwitchIRC gatewayTWITCH_TOKEN, TWITCH_CHANNELCustom("twitch")

Community Platforms (6)

ChannelProtocolEnvironment VariablesType
IRCRaw TCP PRIVMSGIRC_SERVER, IRC_NICK, IRC_PASSWORDCustom("irc")
GuildedWebSocketGUILDED_BOT_TOKENCustom("guilded")
RevoltWebSocketREVOLT_BOT_TOKENCustom("revolt")
KeybaseBot API pollingKEYBASE_USERNAME, KEYBASE_PAPERKEYCustom("keybase")
DiscourseREST pollingDISCOURSE_API_KEY, DISCOURSE_URLCustom("discourse")
GitterStreaming APIGITTER_TOKENCustom("gitter")

Self-Hosted (1)

ChannelProtocolEnvironment VariablesType
Nextcloud TalkREST pollingNEXTCLOUD_TOKEN, NEXTCLOUD_URLCustom("nextcloud")

Privacy-Focused (3)

ChannelProtocolEnvironment VariablesType
ThreemaGateway API webhookTHREEMA_ID, THREEMA_SECRETCustom("threema")
NostrNIP-01 relay WebSocketNOSTR_PRIVATE_KEY, NOSTR_RELAYCustom("nostr")
MumbleTCP text protocolMUMBLE_SERVER, MUMBLE_USERNAME, MUMBLE_PASSWORDCustom("mumble")

Workplace (4)

ChannelProtocolEnvironment VariablesType
PumbleWebhookPUMBLE_WEBHOOK_URL, PUMBLE_TOKENCustom("pumble")
FlockWebhookFLOCK_TOKENCustom("flock")
TwistAPI v3 pollingTWIST_TOKENCustom("twist")
DingTalkRobot API webhookDINGTALK_TOKEN, DINGTALK_SECRETCustom("dingtalk")

Notification Services (2)

ChannelProtocolEnvironment VariablesType
ntfySSE pub/subNTFY_TOPIC, NTFY_SERVERCustom("ntfy")
GotifyWebSocketGOTIFY_TOKEN, GOTIFY_URLCustom("gotify")

Integration (1)

ChannelProtocolEnvironment VariablesType
WebhookGeneric HTTP with HMAC-SHA256WEBHOOK_URL, WEBHOOK_SECRETCustom("webhook")

Channel Configuration

All channel configurations live in ~/.openfang/config.toml under the [channels] section.
[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
default_agent = "assistant"
allowed_users = ["123456789"]

[channels.discord]
bot_token_env = "DISCORD_BOT_TOKEN"
default_agent = "coder"

[channels.slack]
bot_token_env = "SLACK_BOT_TOKEN"
app_token_env = "SLACK_APP_TOKEN"
default_agent = "ops"

Common Configuration Fields

The environment variable holding the bot/access token. OpenFang reads the token from this env var at startup. All secrets are stored as Zeroizing<String> and wiped from memory on drop.
The agent name (or ID) that receives messages when no specific routing applies.
Optional list of platform user IDs allowed to interact. Empty means allow all.
Optional per-channel behavior overrides. See Channel Overrides section below.

Channel Overrides

Every channel adapter supports ChannelOverrides, which let you customize behavior per channel without modifying the agent manifest.
[channels.telegram.overrides]
model = "gemini-2.5-flash"
system_prompt = "You are a concise Telegram assistant. Keep replies under 200 words."
dm_policy = "respond"
group_policy = "mention_only"
rate_limit_per_user = 10
threading = true
output_format = "telegram_html"
usage_footer = "compact"

Override Fields

FieldTypeDefaultDescription
modelOption<String>Agent defaultOverride the LLM model for this channel
system_promptOption<String>Agent defaultOverride the system prompt
dm_policyDmPolicyRespondHow to handle direct messages
group_policyGroupPolicyMentionOnlyHow to handle group/channel messages
rate_limit_per_useru320 (unlimited)Max messages per minute per user
threadingboolfalseSend replies as thread responses
output_formatOption<OutputFormat>MarkdownOutput format for this channel
usage_footerOption<UsageFooterMode>NoneAppend token usage to responses

Output Formats and Policies

Output Formatter

The formatter module converts Markdown output from the LLM into platform-native formats:
FormatTargetNotes
MarkdownStandard MarkdownDefault; passed through as-is
TelegramHtmlTelegram HTML subsetConverts **bold** to <b>, `code` to <code>
SlackMrkdwnSlack mrkdwnConverts **bold** to *bold*, links to <url|text>
PlainTextPlain textStrips all formatting

DM Policy

Controls how the adapter handles direct messages:
PolicyBehavior
RespondRespond to all DMs (default)
AllowedOnlyOnly respond to DMs from users in allowed_users
IgnoreSilently drop all DMs

Group Policy

Controls how the adapter handles messages in group chats, channels, and rooms:
PolicyBehavior
AllRespond to every message in the group
MentionOnlyOnly respond when the bot is @mentioned (default)
CommandsOnlyOnly respond to /command messages
IgnoreSilently ignore all group messages
Policy enforcement happens in dispatch_message() before the message reaches the agent loop. This means ignored messages consume zero LLM tokens.

Setup Guides

Telegram

1

Create a bot

Open Telegram and message @BotFather. Send /newbot and follow the prompts.
2

Set environment variable

export TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
3

Configure in config.toml

[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
default_agent = "assistant"

[channels.telegram.overrides]
output_format = "telegram_html"
group_policy = "mention_only"
4

Restart the daemon

openfang start
How It Works: Uses long-polling via the getUpdates API with 30-second timeout. Automatically splits responses over 4096 characters. Interactive Setup:
openfang channel setup telegram

Discord

1

Create a Discord application

Go to Discord Developer Portal and create a new application.
2

Add a bot

Go to the Bot section, click “Add Bot”, and enable Message Content Intent under Privileged Gateway Intents.
3

Invite the bot

Go to OAuth2 > URL Generator, select scope bot and permissions Send Messages + Read Message History. Use the generated URL to invite the bot.
4

Set environment variable

export DISCORD_BOT_TOKEN=MTIzNDU2Nzg5.ABCDEF.ghijklmnop
5

Configure in config.toml

[channels.discord]
bot_token_env = "DISCORD_BOT_TOKEN"
default_agent = "coder"
How It Works: Connects to Discord Gateway via WebSocket (v10). Handles reconnection, heartbeating, and session resumption automatically.

Slack

1

Create a Slack app

Go to Slack API, click “Create New App” > “From Scratch”.
2

Enable Socket Mode

Go to Settings > Socket Mode, enable it, and generate an App-Level Token with scope connections:write.
3

Add bot scopes

Go to OAuth & Permissions and add: chat:write, app_mentions:read, im:history, im:read, im:write.
4

Install to workspace

Install the app to your workspace and copy the Bot User OAuth Token.
5

Set environment variables

export SLACK_APP_TOKEN=xapp-1-...
export SLACK_BOT_TOKEN=xoxb-...
6

Configure in config.toml

[channels.slack]
bot_token_env = "SLACK_BOT_TOKEN"
app_token_env = "SLACK_APP_TOKEN"
default_agent = "ops"

[channels.slack.overrides]
output_format = "slack_mrkdwn"
threading = true
How It Works: Uses Socket Mode WebSocket connection. When threading = true, replies are sent to the message’s thread via thread_ts.

WhatsApp

1

Set up Meta Business account

Go to Meta for Developers and create a Business App.
2

Add WhatsApp product

Add the WhatsApp product and set up a test phone number.
3

Get credentials

Copy the Phone Number ID, Permanent Access Token, and choose a Verify Token.
4

Set environment variables

export WA_PHONE_ID=123456789012345
export WA_ACCESS_TOKEN=EAABs...
export WA_VERIFY_TOKEN=my-secret-verify-token
5

Configure in config.toml

[channels.whatsapp]
mode = "cloud_api"
phone_number_id_env = "WA_PHONE_ID"
access_token_env = "WA_ACCESS_TOKEN"
verify_token_env = "WA_VERIFY_TOKEN"
webhook_port = 8443
default_agent = "assistant"
6

Set up webhook

In the Meta dashboard, configure webhook URL: https://your-domain.com:8443/webhook/whatsapp with your verify token.
How It Works: Runs an HTTP server to receive webhooks from WhatsApp Cloud API. Handles verification (GET) and message reception (POST).

Matrix

1

Create bot account

Create a bot account on your Matrix homeserver and generate an access token.
2

Set environment variable

export MATRIX_TOKEN=syt_...
3

Configure in config.toml

[channels.matrix]
homeserver_url = "https://matrix.org"
access_token_env = "MATRIX_TOKEN"
user_id = "@openfang-bot:matrix.org"
default_agent = "assistant"
4

Invite the bot

Invite the bot to the rooms you want it to monitor.
How It Works: Uses Matrix Client-Server API with long-polling /sync. Processes new messages from joined rooms.

Email

1

Create app password

For Gmail, create an App Password.
2

Set environment variable

export EMAIL_PASSWORD=abcd-efgh-ijkl-mnop
3

Configure in config.toml

[channels.email]
imap_host = "imap.gmail.com"
imap_port = 993
smtp_host = "smtp.gmail.com"
smtp_port = 587
username = "[email protected]"
password_env = "EMAIL_PASSWORD"
poll_interval = 30
default_agent = "email-assistant"
How It Works: Polls IMAP inbox at configured interval. Sends replies via SMTP, preserving subject line threading.

Agent Routing

The AgentRouter determines which agent receives an incoming message:
1

Per-channel default

Each channel config has a default_agent field. Messages from that channel go to that agent.
2

User-agent binding

If a user has been associated with a specific agent, messages from that user route to that agent.
3

Command prefix

Users can switch agents by sending /agent coder in chat. Subsequent messages route to the “coder” agent.
4

Fallback

If no routing applies, messages go to the first available agent.

WebChat (Built-in)

The WebChat UI is embedded in the daemon and requires no configuration:
http://127.0.0.1:4200/

Real-time Chat

WebSocket-based communication

Streaming Responses

Text deltas arrive as generated

Agent Selection

Switch between running agents

Token Usage Display

Live token and cost tracking

Writing Custom Adapters

To add support for a new messaging platform, implement the ChannelAdapter trait.

The ChannelAdapter Trait

pub trait ChannelAdapter: Send + Sync {
    fn name(&self) -> &str;
    fn channel_type(&self) -> ChannelType;
    
    async fn start(
        &self,
    ) -> Result<Pin<Box<dyn Stream<Item = ChannelMessage> + Send>>, Box<dyn std::error::Error>>;
    
    async fn send(
        &self,
        user: &ChannelUser,
        content: ChannelContent,
    ) -> Result<(), Box<dyn std::error::Error>>;
    
    async fn send_typing(&self, _user: &ChannelUser) -> Result<(), Box<dyn std::error::Error>> {
        Ok(())
    }
    
    async fn stop(&self) -> Result<(), Box<dyn std::error::Error>>;
    
    fn status(&self) -> ChannelStatus {
        ChannelStatus::default()
    }
    
    async fn send_in_thread(
        &self,
        user: &ChannelUser,
        content: ChannelContent,
        _thread_id: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        self.send(user, content).await
    }
}

Implementation Steps

1

Define your adapter

Create crates/openfang-channels/src/myplatform.rs:
use crate::types::{ChannelAdapter, ChannelType, ...};
use tokio::sync::watch;
use zeroize::Zeroizing;

pub struct MyPlatformAdapter {
    token: Zeroizing<String>,
    client: reqwest::Client,
    shutdown: watch::Receiver<bool>,
}
2

Implement the trait

Use ChannelType::Custom("myplatform".to_string()) for the channel type. Wrap secrets in Zeroizing<String>. Use exponential backoff for connection failures.
3

Register the module

In crates/openfang-channels/src/lib.rs:
pub mod myplatform;
4

Wire into the bridge

Add initialization logic in crates/openfang-api/src/channel_bridge.rs.
5

Add config support

Define config struct in openfang-types and add to ChannelsConfig.
6

Add CLI setup wizard

Add case to cmd_channel_setup in crates/openfang-cli/src/main.rs.
Key points for new adapters:
  • Only the 9 most common channels have named ChannelType variants. All others use Custom(String).
  • Wrap secrets in Zeroizing<String> so they are wiped from memory on drop.
  • Accept a watch::Receiver<bool> for coordinated shutdown with the daemon.
  • Use the shared split_message(text, max_len) utility for platforms with message length limits.