Skip to main content

AgentIM — Rust Multi-Channel IM Bridge

AgentIM is a Rust bridge that connects chat platforms to a local AI agent backend (Codex). It supports 8+ platforms through webhooks and long polling, with session persistence, context limits, and routing rules.

AgentIM

Rust IM bridge — 8+ platforms, session persistence, health endpoints.

Supported Platforms

PlatformDeliveryVerification
TelegramLong polling (getUpdates)Bot token
DiscordPOST /discordEd25519
Feishu/LarkPOST /feishuVerification token + URL challenge
SlackPOST /slackHMAC-SHA256
DingTalkPOST /dingtalkHMAC-SHA256
LINEPOST /lineHMAC-SHA256
QQPOST /qq
WeChat WorkPOST /wechatwork

Tech Stack

ComponentTechnologyPurpose
HTTP serverAxum 0.7Routing, middleware, webhooks
Async runtimeTokio (full features)Async I/O, timers, channels
HTTP clientReqwestOutbound API calls
ConcurrencyDashMapLock-free concurrent state
CLIClap 4Command-line argument parsing
LoggingTracing + Tracing-subscriberStructured logging
SerializationSerde + Serde_jsonJSON codec
Error handlingAnyhow + ThiserrorErgonomic error types
AuthHMACWebhook signature verification

Architecture

┌─────────┐  ┌─────────┐  ┌─────────┐
│Telegram  │  │Discord  │  │Feishu   │  ...more platforms
└───┬─────┘  └───┬─────┘  └───┬─────┘
    │            │            │
    ▼            ▼            ▼
┌───────────────────────────────────┐
│         AgentIM (Axum)            │
│  ┌──────────┐ ┌────────────────┐  │
│  │Ingress   │ │Session Store   │  │
│  │(webhooks │ │(DashMap)       │  │
│  │+polling) │ │                │  │
│  └──────────┘ └────────────────┘  │
│  ┌──────────┐ ┌────────────────┐  │
│  │Router    │ │Context Trimmer │  │
│  │(rules)   │ │                │  │
│  └──────────┘ └────────────────┘  │
└───────────┬───────────────────────┘


┌───────────────────┐
│   Codex Backend   │
│   (app-server)    │
└───────────────────┘

Key Design Patterns

Platform Abstraction

Each platform handler implements a common trait:
#[async_trait]
pub trait PlatformHandler: Send + Sync {
    async fn handle_inbound(&self, msg: InboundMessage) -> Result<()>;
    async fn send_response(&self, target: ReplyTarget, text: &str) -> Result<()>;
}
This lets you add new platforms without touching the core routing logic.

Session Persistence

User sessions are stored in DashMap — a lock-free concurrent hashmap:
use dashmap::DashMap;

pub struct SessionStore {
    sessions: DashMap<String, Session>,
}

pub struct Session {
    pub user_id: String,
    pub platform: Platform,
    pub history: Vec<Message>,
    pub created_at: DateTime<Utc>,
}
DashMap provides sharded locks internally, so concurrent reads/writes from multiple Tokio tasks don’t block each other. Much faster than Mutex<HashMap> for IM workloads.

Context Trimming

Chat history grows over time. AgentIM trims context to stay within token limits:
pub fn trim_context(history: &mut Vec<Message>, max_tokens: usize) {
    // Keep the most recent messages that fit within max_tokens
    let mut token_count = 0;
    let cutoff = history.iter().rev().position(|msg| {
        token_count += estimate_tokens(&msg.text);
        token_count > max_tokens
    }).unwrap_or(history.len());

    history.drain(0..cutoff);
}

Webhook Signature Verification

Each platform has its own signature scheme. AgentIM validates before processing:
// Discord: Ed25519
pub fn verify_discord_signature(body: &[u8], signature: &str, timestamp: &str, public_key: &str) -> bool {
    let message = format!("{}{}", timestamp, body);
    ed25519_verify(message.as_bytes(), signature, public_key)
}

// Slack / DingTalk: HMAC-SHA256
pub fn verify_hmac_signature(body: &[u8], signature: &str, secret: &str) -> bool {
    let expected = hmac_sha256(body, secret);
    signature == expected
}

Routing Rules

Channel-based routing lets you direct messages to different behaviors:
pub struct RoutingRule {
    pub channel_pattern: String, // Regex
    pub user_pattern: Option<String>,
    pub target: ReplyTarget,
}

// Example: route all #help channel messages to a FAQ bot
// Example: route DMs from specific users to a custom agent

Docker Deployment

# docker-compose.yml
services:
  agentim:
    build: .
    ports:
      - "8080:8080"
    environment:
      - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
      - DISCORD_PUBLIC_KEY=${DISCORD_PUBLIC_KEY}
      - CODEX_API_URL=http://codex:3002
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s

Health Endpoints

// /health — service status
// /ready   — can process requests
// /review  — pending human-review items (optional)

Best Practices

  1. Platform trait abstraction — Common interface per platform, easy to add new ones
  2. DashMap for concurrent state — Lock-free, fast for multi-platform message handling
  3. Context trimming — Keep history within token limits to avoid runaway memory
  4. Webhook signature verification — Always validate before trusting inbound messages
  5. Health + readiness endpoints — Let orchestrators (Docker, K8s) monitor service state
  6. Shared-secret auth — Simple but effective for internal service communication
  7. Tokio full features — IM bridges need timers, I/O, and sync — don’t trim the runtime

References

Last modified on April 17, 2026