Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Gateway & Channels

Octos runs as a gateway that bridges messaging platforms to your LLM agent. Each platform connection is called a channel. You can run multiple channels simultaneously – for example, Telegram and Slack in the same gateway process.

Channel Overview

Channels are configured in the gateway.channels array of your config.json. Each entry specifies a type, optional allowed_senders for access control, and platform-specific settings.

Check which channels are compiled and configured:

octos channels status

This shows a table with each channel’s compile status (feature flags) and config summary (environment variables set or missing).


Telegram

Requires a bot token from @BotFather.

export TELEGRAM_BOT_TOKEN="123456:ABC..."
{
  "type": "telegram",
  "allowed_senders": ["your_user_id"],
  "settings": {
    "token_env": "TELEGRAM_BOT_TOKEN"
  }
}

Telegram supports bot commands, inline keyboards, voice messages, images, and files.


Slack

Requires a Socket Mode app with both a bot token and an app-level token.

export SLACK_BOT_TOKEN="xoxb-..."
export SLACK_APP_TOKEN="xapp-..."
{
  "type": "slack",
  "settings": {
    "bot_token_env": "SLACK_BOT_TOKEN",
    "app_token_env": "SLACK_APP_TOKEN"
  }
}

Discord

Requires a bot token from the Discord Developer Portal.

export DISCORD_BOT_TOKEN="..."
{
  "type": "discord",
  "settings": {
    "token_env": "DISCORD_BOT_TOKEN"
  }
}

DingTalk

DingTalk supports outbound custom-robot sends and incoming outgoing-robot callbacks.

export DINGTALK_BOT_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=..."
export DINGTALK_BOT_SECRET="SEC..."
{
  "type": "dingtalk",
  "allowed_senders": ["staff-id-1"],
  "settings": {
    "webhook_url_env": "DINGTALK_BOT_WEBHOOK",
    "secret_env": "DINGTALK_BOT_SECRET",
    "webhook_port": 8650
  }
}

For inbound events, configure the DingTalk outgoing robot callback URL. Behind octos serve, use the proxy route; in standalone octos gateway mode, point at the channel’s own webhook server on webhook_port:

# behind octos serve (proxy)
https://YOUR_OCTOS_HOST/webhook/dingtalk/<profile_id>
# standalone octos gateway
http://YOUR_OCTOS_HOST:<webhook_port>/dingtalk/webhook

Build with the dingtalk feature flag:

cargo build --release -p octos-cli --features dingtalk

WhatsApp

Requires a Node.js bridge (Baileys) running at a WebSocket URL.

{
  "type": "whatsapp",
  "settings": {
    "bridge_url": "ws://localhost:3001"
  }
}

Feishu (China)

Feishu uses WebSocket long-connection mode by default (no public URL needed).

export FEISHU_APP_ID="cli_..."
export FEISHU_APP_SECRET="..."
{
  "type": "feishu",
  "settings": {
    "app_id_env": "FEISHU_APP_ID",
    "app_secret_env": "FEISHU_APP_SECRET"
  }
}

Build with the feishu feature flag:

cargo build --release -p octos-cli --features feishu

Lark (International)

Larksuite (international) does not support WebSocket mode. Use webhook mode instead, where Lark pushes events to your server via HTTP POST.

Lark Cloud --> ngrok --> localhost:9321/webhook/event --> Gateway --> LLM

Developer Console Setup

  1. Go to open.larksuite.com/app and create (or select) an app
  2. Add Bot capability under Features
  3. Configure event subscription:
    • Events & Callbacks > Event Configuration > Edit subscription method
    • Select “Send events to developer server”
    • Set request URL to https://YOUR_NGROK_URL/webhook/event
  4. Add event: im.message.receive_v1 (Receive Message)
  5. Enable permissions: im:message, im:message:send_as_bot, im:resource
  6. Publish the app: App Release > Version Management > Create Version > Apply for Online Release

Config

export LARK_APP_ID="cli_..."
export LARK_APP_SECRET="..."
{
  "type": "lark",
  "allowed_senders": [],
  "settings": {
    "app_id_env": "LARK_APP_ID",
    "app_secret_env": "LARK_APP_SECRET",
    "region": "global",
    "mode": "webhook",
    "webhook_port": 9321
  }
}

Settings Reference

SettingDescriptionDefault
app_id_envEnv var name for App IDFEISHU_APP_ID
app_secret_envEnv var name for App SecretFEISHU_APP_SECRET
region"cn" (Feishu) or "global" / "lark" (Larksuite)"cn"
mode"ws" (WebSocket) or "webhook" (HTTP)"ws"
webhook_portPort for webhook HTTP server9321
encrypt_keyEncrypt Key from Lark console (for AES-256-CBC)none
verification_tokenVerification Token from Lark consolenone

Encryption (Optional)

If you configure an Encrypt Key in the Lark console (Events & Callbacks > Encryption Strategy), add it to your config:

{
  "type": "lark",
  "settings": {
    "app_id_env": "LARK_APP_ID",
    "app_secret_env": "LARK_APP_SECRET",
    "region": "global",
    "mode": "webhook",
    "webhook_port": 9321,
    "encrypt_key": "your-encrypt-key-here",
    "verification_token": "your-verification-token"
  }
}

With encryption enabled, Lark sends encrypted POST bodies. The gateway decrypts using AES-256-CBC with SHA-256 key derivation and validates signatures via the X-Lark-Signature header.

Supported Message Types

Inbound: text, images, files (PDF, docs), audio, video, stickers

Outbound: markdown (via interactive cards), image upload, file upload

Running

# Start ngrok tunnel
ngrok http 9321

# Start gateway
LARK_APP_ID="cli_xxxxx" LARK_APP_SECRET="xxxxx" octos gateway --cwd /path/to/workdir

Troubleshooting

IssueSolution
404 on WS endpointLarksuite international does not support WebSocket. Use "mode": "webhook"
Challenge verification failsEnsure ngrok is running and the URL matches the Lark console
No events receivedPublish the app version after adding events. Check Event Log in the console
Bot does not replyVerify im:message:send_as_bot permission is granted
Ngrok URL changedFree ngrok URLs change on restart. Update the request URL in Lark console

Email (IMAP/SMTP)

Polls an IMAP inbox for inbound messages and replies via SMTP. Feature-gated behind email.

export EMAIL_USERNAME="bot@example.com"
export EMAIL_PASSWORD="app-specific-password"
{
  "type": "email",
  "allowed_senders": ["trusted@example.com"],
  "settings": {
    "imap_host": "imap.gmail.com",
    "imap_port": 993,
    "smtp_host": "smtp.gmail.com",
    "smtp_port": 465,
    "username_env": "EMAIL_USERNAME",
    "password_env": "EMAIL_PASSWORD",
    "from_address": "bot@example.com",
    "poll_interval_secs": 30,
    "max_body_chars": 10000
  }
}

WeCom (WeChat Work)

Requires a Custom App with a message callback URL. Feature-gated behind wecom.

export WECOM_CORP_ID="ww..."
export WECOM_AGENT_SECRET="..."
{
  "type": "wecom",
  "settings": {
    "corp_id_env": "WECOM_CORP_ID",
    "agent_secret_env": "WECOM_AGENT_SECRET",
    "agent_id": "1000002",
    "verification_token": "...",
    "encoding_aes_key": "...",
    "webhook_port": 9322
  }
}

WeChat (via WorkBuddy Bridge)

Regular WeChat users can connect to your agent through a WorkBuddy desktop bridge. WorkBuddy handles the WeChat transport; Octos handles the AI logic via its WeCom Bot channel.

WeChat (mobile) --> WorkBuddy (desktop) --> WeCom group robot (WSS) --> octos wecom-bot channel

Setup

  1. Create a WeCom group robot in the WeCom Admin Console under Applications > Group Robot. Note the Bot ID and Secret.

  2. Configure the wecom-bot channel:

export WECOM_BOT_SECRET="your_robot_secret_here"
{
  "type": "wecom-bot",
  "allowed_senders": [],
  "settings": {
    "bot_id": "YOUR_BOT_ID",
    "secret_env": "WECOM_BOT_SECRET"
  }
}
  1. Build and start:
cargo build --release -p octos-cli --features "wecom-bot"
octos gateway
  1. Install the WorkBuddy desktop client, link it to your WeChat via QR scan, and connect it to the same WeCom group robot.

Connection Details

PropertyValue
ProtocolWebSocket (WSS)
Endpointwss://openws.work.weixin.qq.com
HeartbeatPing/pong every 30 seconds
Auto-reconnectYes, exponential backoff (5s–60s)
Max message length4096 characters
Message formatMarkdown

The wecom-bot channel uses an outbound WebSocket connection – no public URL or port forwarding is required. This makes it suitable for servers behind NAT or firewalls.

Limitations

  • Text only – voice and image messages are passed as placeholders
  • No message editing – responses are sent as new messages
  • One direction – WeChat-to-Octos is automatic; for proactive messages, use cron jobs

Matrix

Matrix is a first-class channel and the transport behind the human-approval and management-bot features referenced throughout this chapter. Feature-gated behind matrix. Two modes are supported via the mode setting:

  • user — logs in as an ordinary Matrix account via the Client-Server API and /sync. Works with any account on any homeserver; no server-side registration needed.
  • appservice — registers as an application service against a homeserver you control (Conduit/conduwuit/Synapse), enabling per-user bot puppets.

When mode is omitted the gateway falls back to appservice (which then requires as_token/hs_token), so set "mode": "user" explicitly for ordinary account login.

User mode

{
  "type": "matrix",
  "settings": {
    "mode": "user",
    "homeserver": "https://matrix.org",
    "user_id": "@mybot:matrix.org",
    "access_token": "syt_...",
    "device_name": "octos",
    "rooms": ["!roomid:matrix.org"],
    "auto_join": "allowlist",
    "auto_join_allowlist": ["!roomid:matrix.org", "#alias:matrix.org"]
  }
}

Log in with an access_token (preferred), or with user_id + password (both are required for password login). These credentials are read as literal settings values — they are not expanded from environment variables. auto_join (off / allowlist / always) controls invite auto-accept; under allowlist, auto_join_allowlist lists the room IDs / aliases (or *) whose invites are accepted — matched against rooms, not inviter user IDs — and falls back to rooms when empty.

Appservice mode

{
  "type": "matrix",
  "settings": {
    "mode": "appservice",
    "homeserver": "http://localhost:6167",
    "server_name": "localhost",
    "as_token": "...",
    "hs_token": "...",
    "sender_localpart": "bot",
    "user_prefix": "bot_"
  }
}

Build with the matrix feature flag. In appservice / management-bot rooms, Matrix renders native Approve/Deny cards for Human Approval Rules (via Robrix) and handles the /schedule, /schedules, /unschedule, and /allbots chat commands (see Cron Jobs below). A plain mode: "user" account channel does not interpret those management commands; it forwards message text to the agent, but by default only when the bot is mentioned (require_mention defaults to true — set "require_mention": false in its settings to forward every message).


LINE

Inbound webhook + outbound Messaging API. Feature-gated behind line.

export LINE_CHANNEL_SECRET="..."
export LINE_CHANNEL_ACCESS_TOKEN="..."
{
  "type": "line",
  "settings": {
    "channel_secret_env": "LINE_CHANNEL_SECRET",
    "channel_access_token_env": "LINE_CHANNEL_ACCESS_TOKEN",
    "webhook_port": 9323,
    "bot_user_id": "U...",
    "require_mention": false
  }
}

In standalone octos gateway mode, LINE pushes events to the channel’s own webhook server at http://YOUR_OCTOS_HOST:<webhook_port>/line/webhook; behind octos serve, use the proxy route https://YOUR_OCTOS_HOST/webhook/line/<profile_id> instead. Inbound signatures are verified over the request body with the channel secret (HMAC-SHA256), so either URL works. Build with the line feature flag.


Twilio (SMS)

Two-way SMS via Twilio’s Programmable Messaging webhook. Feature-gated behind twilio.

export TWILIO_ACCOUNT_SID="AC..."
export TWILIO_AUTH_TOKEN="..."
{
  "type": "twilio",
  "settings": {
    "account_sid_env": "TWILIO_ACCOUNT_SID",
    "auth_token_env": "TWILIO_AUTH_TOKEN",
    "from_number": "+15551234567",
    "webhook_port": 9324
  }
}

Point your Twilio number’s inbound webhook at the channel’s own webhook server: http://YOUR_OCTOS_HOST:<webhook_port>/twilio/webhook. Twilio’s X-Twilio-Signature is verified against the full reconstructed URL, which the channel builds from the request’s Host and X-Forwarded-Proto headers (scheme defaults to http). Behind an HTTPS reverse proxy, the proxy must preserve the /twilio/webhook path and forward both the public host and X-Forwarded-Proto: https — otherwise the reconstructed URL is http://… and signature verification fails (403). Build with the twilio feature flag.


Session Control Commands

In any gateway channel, the following commands manage conversation sessions:

CommandDescription
/newClear the current session (bare /new wipes history, like /clear)
/new <name>Switch to (or create) a named session
/s <name>Switch to a named session
/sSwitch to the default session
/sessionsList all sessions for this chat
/backSwitch to the previously active session
/deleteDelete the current session

Only one session is active at a time per chat. Messages are routed to the active session. Inactive sessions can still run background tasks (deep search, pipelines, etc.). When an inactive session finishes work, you receive a notification – use /s <name> to view the results.


Voice Transcription

Voice and audio messages from channels are automatically transcribed before being sent to the agent. The system tries local ASR first (via the OminiX engine) and falls back to cloud-based Whisper when local ASR is unavailable. The transcription is prepended as [transcription: ...].

# Local ASR (preferred) -- set automatically by octos serve
export OMINIX_API_URL="http://localhost:8080"

# Cloud fallback
export GROQ_API_KEY="gsk_..."

Voice configuration in config.json:

{
  "voice": {
    "auto_asr": true,
    "auto_tts": true,
    "default_voice": "vivian",
    "asr_language": null
  }
}
  • auto_asr – automatically transcribe incoming voice/audio messages
  • auto_tts – automatically synthesize voice replies when the user sends voice
  • default_voice – voice preset for auto-TTS
  • asr_language – force a specific language for transcription (null = auto-detect)

Access Control

Use allowed_senders to restrict who can interact with the agent. An empty list allows everyone.

{
  "type": "telegram",
  "allowed_senders": ["123456", "789012"]
}

Each channel type uses its own sender identifier format (Telegram user IDs, email addresses, WeCom user IDs, etc.).


Cron Jobs

The agent can schedule recurring tasks that deliver messages through any channel:

octos cron list                          # List active jobs
octos cron list --all                    # Include disabled jobs
octos cron add --name "report" --message "Generate daily report" --cron "0 0 9 * * * *"
octos cron add --name "check" --message "Check status" --every 3600
octos cron add --name "once" --message "Run migration" --at "2025-03-01T09:00:00Z"
octos cron remove <job-id>
octos cron enable <job-id>               # Enable a job
octos cron enable <job-id> --disable     # Disable a job

Jobs support an optional timezone field with IANA timezone names (e.g., "America/New_York", "Asia/Shanghai"). When omitted, UTC is used.

When running Matrix through a management bot (for example, a BotFather-style profile), the same cron capability can also be reached through chat commands:

/schedule 20秒之后提醒我看天气
/schedule 每天早上 9 点提醒我看天气
/schedules
/unschedule <job-id>

These commands are scoped to the current chat context. /schedules only shows jobs created for the current room/DM, and /unschedule only removes jobs visible in that same chat context. Daily/weekly schedules are stored with the server’s local IANA timezone so wall-clock times survive DST changes.

In a BotFather management room, /allbots <message> broadcasts a command to the room’s bound child bots (at most 8 per broadcast). Stale bindings are skipped, and private bots only accept broadcasts from their owner.

When approval_policy rules are configured (see Configuration → Human Approval Rules), tool calls matching a rule pause the turn and post an approval card to the room. Robrix renders native Approve/Deny buttons; the decision is only accepted from the rule’s authorized_approvers, in the originating room, before expiry.


Message Coalescing

Long responses are automatically split into channel-safe chunks:

ChannelMax chars per message
Telegram4000
Discord1900
DingTalk3600
Slack3900

Split preference: paragraph boundary > newline > sentence end > space > hard cut.


Config Hot-Reload

The gateway detects config file changes automatically:

  • Hot-reloaded (no restart): system prompt, AGENTS.md, SOUL.md, USER.md
  • Restart required: provider, model, API keys, channel settings

Changes are detected via SHA-256 hashing with debounce.