Enable matrix-sdk markdown support and send Matrix messages with text_markdown so clients can render formatted_body. Add listener startup diagnostics for device verification and backup state to reduce confusion around matrix_sdk_crypto backup warnings. Expand Matrix docs with backup-warning interpretation, unverified-device guidance, markdown formatting expectations, and updated log keyword appendix.
12 KiB
Channels Reference
This document is the canonical reference for channel configuration in ZeroClaw.
For encrypted Matrix rooms, also read the dedicated runbook:
Quick Paths
- Need a full config reference by channel: jump to Per-Channel Config Examples.
- Need a no-response diagnosis flow: jump to Troubleshooting Checklist.
- Need Matrix encrypted-room help: use Matrix E2EE Guide.
- Need deployment/network assumptions (polling vs webhook): use Network Deployment.
FAQ: Matrix setup passes but no reply
This is the most common symptom (same class as issue #499). Check these in order:
- Allowlist mismatch:
allowed_usersdoes not include the sender (or is empty). - Wrong room target: bot is not joined to the configured
room_id/ alias target room. - Token/account mismatch: token is valid but belongs to another Matrix account.
- E2EE device identity gap:
whoamidoes not returndevice_idand config does not provide one. - Key sharing/trust gap: room keys were not shared to the bot device, so encrypted events cannot be decrypted.
- Stale runtime state: config changed but
zeroclaw daemonwas not restarted.
1. Configuration Namespace
All channel settings live under channels_config in ~/.zeroclaw/config.toml.
[channels_config]
cli = true
Each channel is enabled by creating its sub-table (for example, [channels_config.telegram]).
In-Chat Runtime Model Switching (Telegram / Discord)
When running zeroclaw channel start (or daemon mode), Telegram and Discord now support sender-scoped runtime switching:
/models— show available providers and current selection/models <provider>— switch provider for the current sender session/model— show current model and cached model IDs (if available)/model <model-id>— switch model for the current sender session
Notes:
- Switching clears only that sender's in-memory conversation history to avoid cross-model context contamination.
- Model cache previews come from
zeroclaw models refresh --provider <ID>. - These are runtime chat commands, not CLI subcommands.
Channel Matrix
2. Delivery Modes at a Glance
| Channel | Receive mode | Public inbound port required? |
|---|---|---|
| CLI | local stdin/stdout | No |
| Telegram | polling | No |
| Discord | gateway/websocket | No |
| Slack | events API | No (token-based channel flow) |
| Mattermost | polling | No |
| Matrix | sync API (supports E2EE) | No |
| Signal | signal-cli HTTP bridge | No (local bridge endpoint) |
| webhook | Yes (public HTTPS callback) | |
| Webhook | gateway endpoint (/webhook) |
Usually yes |
| IMAP polling + SMTP send | No | |
| IRC | IRC socket | No |
| Lark/Feishu | websocket (default) or webhook | Webhook mode only |
| DingTalk | stream mode | No |
| bot gateway | No | |
| iMessage | local integration | No |
3. Allowlist Semantics
For channels with inbound sender allowlists:
- Empty allowlist: deny all inbound messages.
"*": allow all inbound senders (use for temporary verification only).- Explicit list: allow only listed senders.
Field names differ by channel:
allowed_users(Telegram/Discord/Slack/Mattermost/Matrix/IRC/Lark/DingTalk/QQ)allowed_from(Signal)allowed_numbers(WhatsApp)allowed_senders(Email)allowed_contacts(iMessage)
4. Per-Channel Config Examples
4.1 Telegram
[channels_config.telegram]
bot_token = "123456:telegram-token"
allowed_users = ["*"]
4.2 Discord
[channels_config.discord]
bot_token = "discord-bot-token"
guild_id = "123456789012345678" # optional
allowed_users = ["*"]
listen_to_bots = false
mention_only = false
4.3 Slack
[channels_config.slack]
bot_token = "xoxb-..."
app_token = "xapp-..." # optional
channel_id = "C1234567890" # optional
allowed_users = ["*"]
4.4 Mattermost
[channels_config.mattermost]
url = "https://mm.example.com"
bot_token = "mattermost-token"
channel_id = "channel-id" # required for listening
allowed_users = ["*"]
4.5 Matrix
[channels_config.matrix]
homeserver = "https://matrix.example.com"
access_token = "syt_..."
user_id = "@zeroclaw:matrix.example.com" # optional, recommended for E2EE
device_id = "DEVICEID123" # optional, recommended for E2EE
room_id = "!room:matrix.example.com" # or room alias (#ops:matrix.example.com)
allowed_users = ["*"]
See Matrix E2EE Guide for encrypted-room troubleshooting.
Notes:
- Outbound Matrix replies are emitted as markdown-capable
m.room.messagetext content so common clients can render lists, emphasis, and code blocks. - If you still see
matrix_sdk_crypto::backupswarnings, follow the backup/recovery section in the Matrix E2EE guide.
4.6 Signal
[channels_config.signal]
http_url = "http://127.0.0.1:8686"
account = "+1234567890"
group_id = "dm" # optional: "dm" / group id / omitted
allowed_from = ["*"]
ignore_attachments = false
ignore_stories = true
4.7 WhatsApp
[channels_config.whatsapp]
access_token = "EAAB..."
phone_number_id = "123456789012345"
verify_token = "your-verify-token"
app_secret = "your-app-secret" # optional but recommended
allowed_numbers = ["*"]
4.8 Webhook Channel Config (Gateway)
channels_config.webhook enables webhook-specific gateway behavior.
[channels_config.webhook]
port = 8080
secret = "optional-shared-secret"
Run with gateway/daemon and verify /health.
4.9 Email
[channels_config.email]
imap_host = "imap.example.com"
imap_port = 993
imap_folder = "INBOX"
smtp_host = "smtp.example.com"
smtp_port = 465
smtp_tls = true
username = "bot@example.com"
password = "email-password"
from_address = "bot@example.com"
poll_interval_secs = 60
allowed_senders = ["*"]
4.10 IRC
[channels_config.irc]
server = "irc.libera.chat"
port = 6697
nickname = "zeroclaw-bot"
username = "zeroclaw" # optional
channels = ["#zeroclaw"]
allowed_users = ["*"]
server_password = "" # optional
nickserv_password = "" # optional
sasl_password = "" # optional
verify_tls = true
4.11 Lark / Feishu
[channels_config.lark]
app_id = "cli_xxx"
app_secret = "xxx"
encrypt_key = "" # optional
verification_token = "" # optional
allowed_users = ["*"]
use_feishu = false
receive_mode = "websocket" # or "webhook"
port = 8081 # required for webhook mode
4.12 DingTalk
[channels_config.dingtalk]
client_id = "ding-app-key"
client_secret = "ding-app-secret"
allowed_users = ["*"]
4.13 QQ
[channels_config.qq]
app_id = "qq-app-id"
app_secret = "qq-app-secret"
allowed_users = ["*"]
4.14 iMessage
[channels_config.imessage]
allowed_contacts = ["*"]
5. Validation Workflow
- Configure one channel with permissive allowlist (
"*") for initial verification. - Run:
zeroclaw onboard --channels-only
zeroclaw daemon
- Send a message from an expected sender.
- Confirm a reply arrives.
- Tighten allowlist from
"*"to explicit IDs.
6. Troubleshooting Checklist
If a channel appears connected but does not respond:
- Confirm the sender identity is allowed by the correct allowlist field.
- Confirm bot account membership/permissions in target room/channel.
- Confirm tokens/secrets are valid (and not expired/revoked).
- Confirm transport mode assumptions:
- polling/websocket channels do not need public inbound HTTP
- webhook channels do need reachable HTTPS callback
- Restart
zeroclaw daemonafter config changes.
For Matrix encrypted rooms specifically, use:
7. Operations Appendix: Log Keywords Matrix
Use this appendix for fast triage. Match log keywords first, then follow the troubleshooting steps above.
7.1 Recommended capture command
RUST_LOG=info zeroclaw daemon 2>&1 | tee /tmp/zeroclaw.log
Then filter channel/gateway events:
rg -n "Matrix|Telegram|Discord|Slack|Mattermost|Signal|WhatsApp|Email|IRC|Lark|DingTalk|QQ|iMessage|Webhook|Channel" /tmp/zeroclaw.log
7.2 Keyword table
| Component | Startup / healthy signal | Authorization / policy signal | Transport / failure signal |
|---|---|---|---|
| Telegram | Telegram channel listening for messages... |
Telegram: ignoring message from unauthorized user: |
Telegram poll error: / Telegram parse error: / Telegram polling conflict (409): |
| Discord | Discord: connected and identified |
Discord: ignoring message from unauthorized user: |
Discord: received Reconnect (op 7) / Discord: received Invalid Session (op 9) |
| Slack | Slack channel listening on # |
Slack: ignoring message from unauthorized user: |
Slack poll error: / Slack parse error: |
| Mattermost | Mattermost channel listening on |
Mattermost: ignoring message from unauthorized user: |
Mattermost poll error: / Mattermost parse error: |
| Matrix | Matrix channel listening on room / Matrix room ... is encrypted; E2EE decryption is enabled via matrix-sdk. / Matrix room-key backup is enabled for this device. / Matrix device '...' is verified for E2EE. |
Matrix whoami failed; falling back to configured session hints for E2EE session restore: / Matrix whoami failed while resolving listener user_id; using configured user_id hint: / Matrix room-key backup is not enabled for this device... / Matrix device '...' is not verified... |
Matrix sync error: ... retrying... |
| Signal | Signal channel listening via SSE on |
(allowlist checks are enforced by allowed_from) |
Signal SSE returned ... / Signal SSE connect error: |
| WhatsApp (channel) | WhatsApp channel active (webhook mode). |
WhatsApp: ignoring message from unauthorized number: |
WhatsApp send failed: |
| Webhook / WhatsApp (gateway) | WhatsApp webhook verified successfully |
Webhook: rejected — not paired / invalid bearer token / Webhook: rejected request — invalid or missing X-Webhook-Secret / WhatsApp webhook verification failed — token mismatch |
Webhook JSON parse error: |
Email polling every ... / Email sent to ... |
Blocked email from ... |
Email poll failed: / Email poll task panicked: |
|
| IRC | IRC channel connecting to ... / IRC registered as ... |
(allowlist checks are enforced by allowed_users) |
IRC SASL authentication failed (...) / IRC server does not support SASL... / IRC nickname ... is in use, trying ... |
| Lark / Feishu | Lark: WS connected / Lark event callback server listening on |
Lark WS: ignoring ... (not in allowed_users) / Lark: ignoring message from unauthorized user: |
Lark: ping failed, reconnecting / Lark: heartbeat timeout, reconnecting / Lark: WS read error: |
| DingTalk | DingTalk: connected and listening for messages... |
DingTalk: ignoring message from unauthorized user: |
DingTalk WebSocket error: / DingTalk: message channel closed |
QQ: connected and identified |
QQ: ignoring C2C message from unauthorized user: / QQ: ignoring group message from unauthorized user: |
QQ: received Reconnect (op 7) / QQ: received Invalid Session (op 9) / QQ: message channel closed |
|
| iMessage | iMessage channel listening (AppleScript bridge)... |
(contact allowlist enforced by allowed_contacts) |
iMessage poll error: |
7.3 Runtime supervisor keywords
If a specific channel task crashes or exits, the channel supervisor in channels/mod.rs emits:
Channel <name> exited unexpectedly; restartingChannel <name> error: ...; restartingChannel message worker crashed:
These messages indicate automatic restart behavior is active, and you should inspect preceding logs for root cause.