Canot — Multi-Platform Messaging
Standalone Rust library for unified bidirectional messaging across WhatsApp, Slack, Discord, Telegram, and Messenger.
Instead of integrating with each platform's API separately (different auth schemes, webhook formats, payload structures), Canot provides a single trait-based interface — MessagingChannel — that normalizes inbound webhooks into IncomingMessage and renders outbound OutgoingMessage into each platform's native format. Webhook signature verification, replay protection, and retry logic are built in.
Table of Contents
- Install
- Supported Channels
- Quick Start
- REST API Server
- MCP Server
- Channel Config Store
- Environment Variables
- Docker
- Architecture
- License
Install
Homebrew (macOS / Linux) — recommended
This installs two binaries:
dravr-canot-server— REST API + MCP server (start with env vars for your channels)dravr-canot-mcp— standalone MCP server for editor/agent integration
Once installed, set your channel credentials and start the server:
Docker
Cargo (library)
[]
= { = "0.3", = ["channel-slack", "channel-whatsapp"] }
Supported Channels
| Channel | Feature Flag | Signature Verification | Outbound Format |
|---|---|---|---|
| Slack | channel-slack |
HMAC-SHA256 v0 | Block Kit JSON |
channel-whatsapp |
HMAC-SHA256 (Meta) | Cloud API v22.0 | |
| Messenger | channel-messenger |
HMAC-SHA256 (Meta) | Graph API templates |
| Discord | channel-discord |
Ed25519 | Embeds + components |
| Telegram | channel-telegram |
Secret token header | Bot API + HTML |
All channels support: text, media (image/video/audio/document), location, and rich cards with action buttons.
Use features = ["all-channels"] to compile all five, or pick only what you need.
Quick Start
Use the MessagingChannel trait programmatically:
use create_adapter_from_config;
use ;
use json;
use Uuid;
async
REST API Server (dravr-canot-server)
A unified HTTP server for webhook ingress and outbound message sending with built-in MCP support. Reads channel credentials from environment variables at startup.
Usage
# Start with channels auto-configured from env vars
# MCP-only mode via stdio (for editor/agent integration)
Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/api/messaging/webhook/:channel |
Receive inbound webhook (slack, whatsapp, telegram, discord, messenger) |
POST |
/api/messaging/send |
Send an outbound message |
GET |
/health |
Server health check |
POST |
/mcp |
MCP Streamable HTTP (JSON-RPC 2.0) |
Webhook Example
Point your Slack Events API URL to:
https://your-domain.com/api/messaging/webhook/slack
Canot verifies the signature, parses the payload into a normalized IncomingMessage, and returns it as JSON. Slack's mrkdwn auto-formatting (<mailto:x|x>, <url|label>) is automatically stripped.
Authentication
Optional. Set DRAVR_API_KEY to require bearer token auth. When unset, all requests pass through (localhost dev mode).
DRAVR_API_KEY=my-secret
MCP Server (dravr-canot-mcp)
Exposes messaging channels via the Model Context Protocol. Any MCP-compatible client (Claude Desktop, editors, custom agents) can send messages and manage channel configs.
Usage
# Stdio transport (default — for editor/client integration)
# HTTP transport
MCP Tools
| Tool | Description |
|---|---|
list_channels |
List registered messaging channels and their status |
send_message |
Send a message to a channel (text, media, card) |
get_channel_config |
Get configuration for a channel type |
set_channel_config |
Set or update channel credentials |
Client Configuration
Add to your MCP client config (e.g. Claude Desktop claude_desktop_config.json):
Channel Config Store
Canot uses the ChannelConfigStore trait for pluggable configuration backends. This follows the same pattern as embacle's LlmProvider trait.
Built-in: Environment Variables
EnvConfigStore reads channel credentials from env vars at startup:
use EnvConfigStore;
use ChannelConfigStore;
let store = from_env;
let channels = store.list_configured_channels.await;
let config = store.get_config.await;
Custom: Database Backend
Implement the trait for your storage layer:
use ChannelConfigStore;
use ;
Environment Variables
Channel Credentials
| Variable | Channel | Maps to |
|---|---|---|
SLACK_BOT_TOKEN |
Slack | api_key |
SLACK_SIGNING_SECRET |
Slack | webhook_secret |
TELEGRAM_BOT_TOKEN |
Telegram | bot_token |
TELEGRAM_WEBHOOK_SECRET |
Telegram | webhook_secret |
META_WHATSAPP_ACCESS_TOKEN |
api_key |
|
META_WHATSAPP_APP_SECRET |
webhook_secret |
|
META_WHATSAPP_PHONE_NUMBER_ID |
phone_number |
|
META_WHATSAPP_VERIFY_TOKEN |
verify_token |
|
DISCORD_BOT_TOKEN |
Discord | bot_token |
DISCORD_PUBLIC_KEY |
Discord | webhook_secret |
DISCORD_APPLICATION_ID |
Discord | account_id |
MESSENGER_PAGE_ACCESS_TOKEN |
Messenger | api_key |
MESSENGER_APP_SECRET |
Messenger | webhook_secret |
MESSENGER_VERIFY_TOKEN |
Messenger | verify_token |
Runtime Configuration
| Variable | Default | Description |
|---|---|---|
DRAVR_MAX_RETRY_ATTEMPTS |
3 |
Max delivery retry attempts before dead-lettering |
DRAVR_LINK_CODE_TTL_MINUTES |
10 |
Link verification code expiration |
DRAVR_OTP_TTL_MINUTES |
10 |
OTP code expiration |
DRAVR_MAX_OTP_ATTEMPTS |
3 |
Max OTP verification attempts per flow |
DRAVR_MAX_OTP_FLOWS_PER_HOUR |
5 |
Rate limit: OTP flows per channel user per hour |
DRAVR_API_KEY |
(none) | Bearer token auth for server endpoints |
Docker
Pass channel credentials as environment variables:
Architecture
Your Application
└── dravr-canot (this library)
│
├── Channel Adapters (transport + renderer per platform)
│ ├── SlackChannel → HMAC-SHA256 v0, Block Kit, Events API
│ ├── WhatsAppChannel → HMAC-SHA256 (Meta), Cloud API v22.0
│ ├── MessengerChannel → HMAC-SHA256 (Meta), Graph API templates
│ ├── DiscordChannel → Ed25519, Embeds + components
│ └── TelegramChannel → Secret token, Bot API + HTML
│
├── Core Traits
│ ├── MessagingChannel → verify_signature, receive, render, send, send_raw
│ ├── TransportAdapter → wire protocol: signature, parse, send_raw
│ ├── ResponseRenderer → format OutgoingMessage → channel-native payload
│ ├── ChannelDescriptor → metadata: name, webhook path, capabilities
│ └── ChannelConfigStore → pluggable config backend (env, database, custom)
│
├── Infrastructure
│ ├── ChannelRegistry → route webhooks to the correct adapter
│ ├── EnvConfigStore → read channel credentials from env vars
│ ├── MessagingConfig → runtime settings from DRAVR_* env vars
│ └── RetryWorker → exponential backoff with dead-letter queue
│
├── MCP Server (library + binary crate, powered by dravr-tronc)
│ └── dravr-canot-mcp → JSON-RPC 2.0 over stdio or HTTP/SSE
│
└── Unified REST API + MCP Server (binary crate, powered by dravr-tronc)
└── dravr-canot-server → webhook ingress, message sending, health, MCP
All channel adapters implement the same MessagingChannel trait:
verify_signature()— cryptographic webhook verification (constant-time)receive()— parse inbound webhook into normalizedIncomingMessagerender()— formatOutgoingMessageinto channel-native payloadsend()— render + deliver outbound messagesend_raw()— deliver a pre-rendered payload (for retry queue)
For detailed API docs see docs.rs/dravr-canot.
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.