Skip to content

Channels overview

The channel trait model, delivery modes, allowlist semantics, and the build feature-flag matrix.

A channel is how your Revka agent reaches people on a messaging platform, a voice system, or an automation endpoint. Revka connects to 37 channels through a single unified subsystem: every channel implements one Channel trait, is configured under the [channels_config] namespace in ~/.revka/config.toml, and is managed by a supervisor that provides per-sender conversation history, concurrent message processing, and exponential-backoff reconnection.

This page is the reference hub for that subsystem. It covers the channel trait and its optional capabilities, how each channel receives messages (and which ones need a public address), how allowlists gate inbound senders, the in-chat commands users can run, and the Cargo feature flags that gate the optional channels at build time.

If you just want to wire up your first channel, start with Connect a messaging channel. For per-platform setup, jump to the catalog pages linked at the bottom of this page.

Every channel is enabled by adding its sub-table under [channels_config] in ~/.revka/config.toml. The simplest possible channel is the built-in CLI channel, which needs no credentials:

[channels_config]
cli = true

Each platform adds its own table — for example [channels_config.telegram] — with the credentials and options for that platform:

[channels_config.telegram]
bot_token = "123456:TELEGRAM-TOKEN"
allowed_users = ["myusername"]
stream_mode = "off"
mention_only = false

Channels run inside revka daemon (gateway + channels + heartbeat + cron) or can be started on their own with revka channel start. They do not run under a bare revka gateway, which serves only the dashboard and API. The supervisor restarts a crashed channel task automatically and applies exponential backoff on reconnection.

All channels implement the Channel trait. Beyond the two mandatory methods (send and listen), every capability is optional and defaults to a no-op, so a channel only advertises what its platform supports. This is why streaming, reactions, and pinning work on Discord and Slack but are silently absent on, say, IRC or email.

CapabilityPurposeChannels that implement it
send()Outbound message deliveryAll
listen()Inbound message loopAll (keepalive no-op for webhook channels)
health_check()Liveness probe for revka channel doctorSelected (Discord, Slack, Mattermost, etc.)
start_typing() / stop_typing()Typing indicatorTelegram, Discord, Slack, Mattermost, Matrix
supports_draft_updates()Progressive edit streamingTelegram (partial), Discord, Slack, Matrix
supports_multi_message_streaming()Paragraph-split deliveryDiscord, Matrix
send_draft() / update_draft() / finalize_draft() / cancel_draft()Draft lifecycleTelegram, Discord, Slack, Matrix
update_draft_progress()Tool-execution status updatesSlack, Matrix
add_reaction() / remove_reaction()Emoji reactionsDiscord, Telegram (ack), Slack, Matrix
pin_message() / unpin_message()Message pinningMatrix (Voice Call)
redact_message()Message deletionMatrix

Channels that support draft updates expose a stream_mode config key with up to three values. Not every channel supports every mode. Note that Slack is an exception: it uses a boolean stream_drafts key (not stream_mode) and supports only off/partial-style draft streaming — there is no multi_message mode for Slack.

stream_modeBehaviorSupported on
offThe full reply is sent once, when generation completes.All streaming-capable channels (default for Telegram)
partialAn editable draft message is posted and updated token-by-token as the model streams, then finalized. Edit throttle is draft_update_interval_ms.Telegram, Discord, Slack, Matrix
multi_messageThe reply is delivered as separate messages, split at paragraph boundaries (\n\n) as tokens arrive. Code fences are never split. Pacing is multi_message_delay_ms.Discord, Matrix
[channels_config.discord]
bot_token = "DISCORD-BOT-TOKEN"
stream_mode = "multi_message" # off | partial | multi_message
draft_update_interval_ms = 1000 # edit throttle for partial mode
multi_message_delay_ms = 800 # delay between paragraph sends in multi_message mode

How a channel receives inbound messages determines whether it needs a public address. Polling / outbound-connection channels dial out from Revka (long-poll, WebSocket/gateway, periodic REST) and work behind NAT with no public port. Webhook / push channels need the platform to POST events to a reachable HTTPS callback URL.

ChannelReceive modePublic inbound port?Feature flag
CLIstdinNo
Telegramlong-pollingNo
DiscordGateway WebSocketNo
SlackSocket Mode / pollingNo
MattermostREST pollingNo
Matrixsync API (E2EE)Nochannel-matrix
Signalsignal-cli SSE bridgeNo
WhatsApp Cloud APIwebhook (push)Yes
WhatsApp WebWebSocket (wa-rs)Nowhatsapp-web
IRCTLS socketNo
Nostrrelay WebSocketNochannel-nostr
BlueskypollingNo
Twitter / Xfiltered streamNo
RedditpollingNo
NotionpollingNo
Lark / FeishuWebSocket (default) or webhookWebhook mode onlychannel-lark
DingTalkStream Mode WSNo
WeComoutbound-only (webhook)No
WATIwebhook (push)Yes
QQbot gateway WSNo
MochatpollingNo
Nextcloud Talkwebhook (push)Yes
Linqwebhook (push)Yes
iMessagemacOS AppleScriptNo (macOS only)
Email (IMAP)IMAP IDLENo
Gmail PushPub/Sub webhookYes
Generic WebhookHTTP serverUsually yes
MQTTbroker subscriberNo (outbound conn)
Voice Calltelephony webhookYes
Voice Wakemicrophone captureNovoice-wake
ACP Serverstdio JSON-RPCNo

Every inbound channel applies a sender allowlist, and the semantics are uniform across platforms:

  • Empty list [] → deny all. This is the default — a freshly added channel connects and listens but ignores everyone until you add an identity.
  • Single entry "*" → allow all senders. Use only for temporary verification.
  • Explicit list → allow only the listed senders.

The field name differs by channel because each platform identifies senders differently:

ChannelsAllowlist field
Telegram, Discord, Slack, Mattermost, Matrix, IRC, Lark, Feishu, DingTalk, QQ, Nextcloud Talk, WeCom, Mochat, Twitterallowed_users
Signalallowed_from
WhatsApp (Cloud API & Web), WATIallowed_numbers
Email, Linq, Gmail Pushallowed_senders
iMessageallowed_contacts
Nostrallowed_pubkeys
ClawdTalkallowed_destinations

For Telegram there is a CLI helper that appends to the allowlist for you:

Terminal window
revka channel bind-telegram revka_user # by username (no leading @)
revka channel bind-telegram 123456789 # by numeric user ID

For every other channel, edit the appropriate allowlist field in config.toml. Tighten from "*" to explicit identities once you have confirmed a reply arrives.

On channels that accept text commands (notably Telegram and Discord), allowlisted users can steer the session inline — no CLI, no restart. These are chat commands, not revka subcommands.

CommandEffect
/modelsShow the available providers and current selection
/models <provider>Switch the provider for the current sender session
/modelShow the current model (and cached model IDs, if available)
/model <model-id>Switch the model for the current sender session
/newClear conversation history and start a fresh session

Switching provider or model clears only that sender’s in-memory conversation history, to avoid cross-model context contamination. /new clears history without changing the provider or model. Model previews come from revka models refresh --provider <ID>. The CLI channel additionally supports /quit and /exit to leave an interactive terminal session.

Most channels are compiled into every build. A handful of channels pull in heavy dependencies and are therefore gated behind Cargo features — they are not present in lean default builds and must be enabled at compile time.

Feature flagEnablesNotes
channel-matrixMatrix (Client-Server API, E2EE)Pulls in matrix-sdk.
channel-larkLark and Feishuchannel-feishu is an alias for channel-lark — Lark and Feishu are the same platform.
channel-nostrNostr (NIP-04 / NIP-17)Pulls in nostr-sdk.
whatsapp-webWhatsApp Web mode (wa-rs)WhatsApp Cloud API mode needs no flag; only the native Web client is gated.
voice-wakeLocal microphone wake-word detectionUses cpal; needs libasound2-dev on Linux.

Enable them with --features at build or check time:

Terminal window
# Matrix
cargo build --release --locked --features channel-matrix
# Matrix + Lark together
cargo build --release --locked --features channel-matrix,channel-lark
# WhatsApp Web mode
cargo build --release --locked --features whatsapp-web

For the full Cargo feature catalog and architecture decision records, see Cargo feature flags & ADRs.

revka channel is the command-line surface for managing channels without hand-editing config.toml. The subcommands:

SubcommandWhat it does
revka channel listList all configured channels
revka channel startStart all configured channels
revka channel doctorRun health_check() for every configured channel
revka channel add <type> <config_json>Add a channel from an inline JSON config blob
revka channel remove <name>Remove a channel by its config name
revka channel bind-telegram <identity>Add a Telegram username (no @) or numeric user ID to the allowlist
revka channel send <message> --channel-id <NAME> --recipient <TARGET>Send a one-off message

revka channel add supports the types telegram, discord, slack, whatsapp, matrix, imessage, and email; every other channel is configured by editing config.toml directly or via the onboarding wizard (revka onboard --channels-only).

Terminal window
# Inspect, then start, the configured channels
revka channel list
revka channel start
# Add a Telegram bot and allowlist yourself
revka channel add telegram '{"bot_token":"123456:TELEGRAM-TOKEN","name":"my-bot"}'
revka channel bind-telegram 123456789
# Health-check everything when a channel looks stuck
revka channel doctor
# Send a scripted alert through a channel and exit
revka channel send 'Nightly backup completed.' --channel-id slack --recipient C1234567890

revka channel send delivers a single message and exits — built for scripted notifications (sensor alerts, cron results, deploy notices). --channel-id is the channel’s config name; --recipient is the platform-specific target. For the complete CLI reference see revka channel & integrations.

When channels are running, the channel runtime watches config.toml and applies a defined set of changes live, on the next inbound message, with no daemon restart:

  • default_provider
  • default_model
  • default_temperature
  • api_key / api_url
  • reliability.*

Other changes — adding or removing a channel, rotating tokens, editing allowlists — are not hot-reloaded; restart the channels (revka channel start) or the daemon to apply them. Hot-reload applies to the channel runtime under the daemon, not to interactive revka agent sessions.

If a channel connects but never replies, the cause is almost always one of: the sender is not on the correct allowlist field; the bot lacks membership/permissions in the target room or channel; a token is invalid or expired; a webhook channel has no reachable HTTPS callback; or the daemon was not restarted after a config change. Run revka channel doctor for per-channel health and revka doctor for whole-system diagnostics.

The per-platform setup details, config keys, and platform-specific notes live on the catalog pages below.