Skip to main content

Channels

A channel is a chat platform you can reach nanoinfra on. It is also the word the product uses: the setup surface is Settings → Channels. This page connects nanoinfra to Telegram, Discord, Slack, Signal, Email, Mattermost and other chat apps, and is the full channel reference. If you want a focused setup path for one platform, start with a guide:

PlatformGuide
TelegramBuild a Telegram AI Agent with nanoinfra
DiscordBuild a Discord AI Agent with nanoinfra
SlackBuild a Slack AI Agent with nanoinfra
WhatsAppBuild a WhatsApp AI Agent with nanoinfra
EmailBuild an Email AI Agent with nanoinfra
MattermostBuild a Mattermost AI Agent with nanoinfra

Want to build your own channel? See the Channel Package Guide.

Before configuring a chat app, make sure the local CLI path works:

nanoinfra agent -m "Hello!"

If that fails, fix installation, config, provider, or model setup first with quick-start.md, providers.md, and troubleshooting.md. Chat apps require nanoinfra gateway to stay running after the channel is configured.

For normal local setup, let the WebUI write and validate the channel config:

  1. Run nanoinfra webui.
  2. Open Settings → Channels.
  3. Search for the platform and open its setup panel.
  4. Follow the credential fields or QR flow. The screen tells you which platform-side token, permission, account, or URL it needs.
  5. Let nanoinfra install the optional channel support when prompted.
  6. Restart from the WebUI if it reports that a restart is required.
  7. Send a private test message. If the channel returns a pairing code, approve the pending request in the WebUI and send the message again.

If your installed stable release does not show Settings → Channels, continue with the manual setup pattern below or install current source.

A WebUI on the same machine may install optional packages by default. Remote browser clients cannot change the Python environment unless an administrator explicitly enables that capability. Run nanoinfra plugins enable <channel> locally when the guided install is unavailable.

The sections below explain what each chat platform requires and provide manual config for deployments that manage config.json directly.

[!NOTE] If you upgrade from a version that installed chat app SDKs by default, enable the channel in the same Python environment. That makes nanoinfra install its manifest-declared dependencies:

nanoinfra plugins enable <channel>

Replace <channel> with names such as telegram, slack, discord, matrix, whatsapp, signal, email, mattermost, or msteams. To turn a channel off later, run nanoinfra plugins disable <channel>. nanoinfra keeps the saved settings, but stops loading that channel after the next restart.

Manual Setup Pattern​

Most examples below are snippets to merge into ~/.nanoinfra/config.json. When a snippet includes allowFrom, it is showing a static allowlist. For pairing-based access on supported channels, omit allowFrom. Slack and Mattermost also need dm.policy set to "allowlist" before DMs issue pairing codes.

Every chat app uses the same shape:

  1. Create or prepare the bot/account in the chat platform.
  2. Copy the token, secret, QR login state, webhook URL, or account ID that platform gives you.
  3. Merge that platform's JSON snippet into ~/.nanoinfra/config.json.
  4. Prefer pairing for DM-capable channels: omit allowFrom, let the first DM receive a pairing code, then approve it with /pairing approve <code>.
  5. For channels without pairing, such as Email, keep access narrow with allowFrom or the platform-specific allow list.
  6. Check that nanoinfra can see the configured channel:
nanoinfra channels status
  1. Start the gateway and leave that terminal running:
nanoinfra gateway
  1. Send a test DM. If the bot returns a pairing code, approve it and send the message again. In group chats, follow that channel's group policy. Most channels read groupPolicy, and Signal reads the nested group.policy and group.requireMention instead. Many channels default to mention-only. Matrix and WhatsApp default to open group replies.

If nanoinfra channels status does not show the channel as enabled, one of these is true:

  • The config snippet is in the wrong place.
  • The channel name is misspelled.
  • The config file you edited is not the one nanoinfra reads.

If the channel is enabled but messages do not arrive, run nanoinfra gateway --verbose. Then compare the platform-side credentials, event permissions, and allow lists.

allowFrom: ["*"] bypasses pairing and allows anyone who can reach that channel to talk to the bot. Use it only when that is intentional, or temporarily while testing in a private sandbox.

Each platform has its own section below.

ChannelWhat you need
TelegramBot token from @BotFather
DiscordBot token + Message Content intent
WhatsAppQR code scan (nanoinfra channels login whatsapp)
SlackBot token + App-Level token
MattermostBot account token + server URL
MatrixHomeserver URL + Access token
EmailIMAP/SMTP credentials
Microsoft TeamsApp ID + App Password + public HTTPS endpoint
Signalsignal-cli daemon + phone number

Group Policy​

groupPolicy sets when the bot replies in a group chat or channel:

  • "mention" — the bot replies only when a message @mentions it.
  • "open" — the bot replies to every message in the group.
  • "allowlist" — the bot replies only in the groups or channels listed in groupAllowFrom.

Discord accepts "mention" and "open" only. Signal does not use groupPolicy at all. It uses the nested group.policy and group.requireMention fields instead. Each platform section below gives its own default.

Answering agent​

Every channel here may name the agent that answers it. Add agent to that channel's block:

{
"channels": {
"telegram": { "token": "...", "agent": "sre" }
}
}

Then every message arriving on that channel is answered by sre. A sender can still address another configured agent with @agent:<name>, which wins over the binding.

Set it in Settings → Channels instead, in the Answering agent section of the channel's panel. The section is present only when the deployment names agents in agents.named.

Two rules, both applied when config loads:

  • The name has to exist in agents.named. A name that does not refuses to load, and the message names it.
  • channels.websocket.agent is refused. The WebUI picks the agent for each message in the composer, so a channel-wide default there would answer every turn where you picked nothing.

See Agents for the order all four sources are read in.

Telegram​

Recommended WebUI setup

  1. Create a bot with @BotFather and copy its token.
  2. Run nanoinfra webui, then open Settings → Channels → Telegram.
  3. Paste the token. If the gateway cannot reach Telegram directly, expand Advanced and add an HTTP or SOCKS proxy.
  4. Save and enable Telegram, then send the bot a direct message.

The configuration badge means nanoinfra found a saved token. The live connection check is separate, so a temporary Telegram or proxy outage does not make an existing configuration disappear. Saved tokens and proxy URLs remain masked.

See the step-by-step Telegram guide for pairing and troubleshooting.

Manual setup

1. Create a bot

  • Open Telegram, and search for @BotFather.
  • Send /newbot, and follow the prompts.
  • Copy the token.

2. Configure

{
"channels": {
"telegram": {
"enabled": true,
"token": "YOUR_BOT_TOKEN",
"allowFrom": ["YOUR_USER_ID"]
}
}
}

If the gateway cannot reach Telegram directly, add a proxy to the same section:

{
"channels": {
"telegram": {
"proxy": "http://127.0.0.1:7890"
}
}
}

HTTP, HTTPS, SOCKS5, and SOCKS5H proxy URLs are accepted. Treat a proxy URL containing a username or password as a secret.

You can find your User ID in Telegram settings. It is shown as @yourUserId. Copy this value without the @ symbol and paste it into the config file.

richMessages defaults to false. Set it to true only if your Telegram client supports Bot API 10.1 rich messages and you want richer markdown rendering. Keep it disabled for Telegram Web, which may show unsupported-message errors for rich messages.

Webhook mode (optional)

Telegram uses long polling by default. To receive updates through a webhook, expose a public HTTPS URL that forwards to nanoinfra's local listener and set mode to webhook:

{
"channels": {
"telegram": {
"enabled": true,
"token": "YOUR_BOT_TOKEN",
"mode": "webhook",
"webhookUrl": "https://example.com/telegram",
"webhookListenHost": "127.0.0.1",
"webhookListenPort": 8081,
"webhookPath": "/telegram",
"webhookSecretToken": "CHANGE_ME_RANDOM_SECRET",
"webhookMaxConnections": 4,
"allowFrom": ["YOUR_USER_ID"]
}
}
}

webhookSecretToken is required in webhook mode. Do not expose the local webhook listener directly to the public internet without a reverse proxy or tunnel in front of it. TLS/Host policy is handled by your proxy. nanoinfra only listens on webhookListenHost:webhookListenPort and validates Telegram's webhook secret token. webhookMaxConnections defaults to 4. nanoinfra still serializes Telegram updates per conversation before forwarding them to the agent.

webhookUrl is the public HTTPS URL registered with Telegram. webhookPath is the local path nanoinfra listens on. They often use the same path, but may differ when a reverse proxy or tunnel rewrites the request path.

Discord​

1. Create a bot

2. Enable intents

  • In the Bot settings, enable MESSAGE CONTENT INTENT.
  • (Optional) Enable SERVER MEMBERS INTENT if you plan to use allow lists based on member data.

3. Get your User ID

  • Discord Settings → Advanced → enable Developer Mode.
  • Right-click your avatar → Copy User ID.

4. Configure

{
"channels": {
"discord": {
"enabled": true,
"token": "YOUR_BOT_TOKEN",
"allowFrom": ["YOUR_USER_ID"],
"allowChannels": [],
"groupPolicy": "mention",
"streaming": true
}
}
}

groupPolicy defaults to "mention". DMs always respond when the sender is in allowFrom. If you set the group policy to open, create new threads as private threads, and then @ the bot into it. Otherwise the thread itself, and the channel you spawned it in, each spawn a bot session. allowChannels restricts the bot to specific Discord channel IDs. Empty (default) means respond in every channel the bot can see. Example: ["1234567890", "0987654321"]. The filter applies after allowFrom, so both must pass. Discord threads under an allowed parent channel are also allowed. For Forum channels, allowing the parent Forum channel allows all threads/posts in that forum. streaming defaults to true. Disable it only if you explicitly want non-streaming replies.

5. Invite the bot

  • OAuth2 → URL Generator.
  • Scopes: bot.
  • Bot Permissions: Send Messages, Read Message History.
  • Open the generated invite URL, and add the bot to your server.

Matrix​

Element is the reference client for Matrix.

1. Create or choose a Matrix account

  • Create or reuse a Matrix account on your homeserver (for example matrix.org).
  • Confirm you can log in with Element.

2. Get credentials

  • You need:
    • userId (example: @nanoinfra:matrix.org)
    • password

(Note: accessToken and deviceId are still supported for legacy reasons, but for reliable encryption, password login is recommended instead. If the password is provided, accessToken and deviceId will be ignored.)

3. Configure

{
"channels": {
"matrix": {
"enabled": true,
"homeserver": "https://matrix.org",
"userId": "@nanoinfra:matrix.org",
"password": "mypasswordhere",
"e2eeEnabled": true,
"sasVerification": true,
"allowFrom": ["@your_user:matrix.org"],
"groupPolicy": "open",
"groupAllowFrom": [],
"allowRoomMentions": false,
"maxMediaBytes": 20971520
}
}
}

Keep a persistent matrix-store — encrypted session state is lost if these change across restarts.

OptionDescription
allowFromUser IDs allowed to interact. Empty denies all.
groupPolicyDefaults to open.
groupAllowFromRoom allowlist (used when policy is allowlist).
allowRoomMentionsAccept @room mentions in mention mode.
e2eeEnabledE2EE support (default true). Set false for plaintext-only.
sasVerificationAuto-complete SAS device verification requests from allowed users (default false). Useful for Element X, which does not expose manual trust for third-party devices.
maxMediaBytesMax attachment size (default 20MB). Set 0 to block all media.

WhatsApp​

1. Link device with QR

nanoinfra channels login whatsapp
# Scan QR with WhatsApp → Settings → Linked Devices

2. Configure

{
"channels": {
"whatsapp": {
"enabled": true,
"allowFrom": ["1234567890"]
}
}
}

For groups, allowFrom can contain either a participant sender ID/LID or a group JID/bare group ID. A participant entry allows that sender wherever the bot can see them. A group entry allows replies in that group.

Optional session database path:

{
"channels": {
"whatsapp": {
"databasePath": "~/.nanoinfra/whatsapp-auth/neonize.db"
}
}
}

Migrating from the old bridge

  • Remove bridgeUrl and bridgeToken. WhatsApp no longer runs a local Node.js bridge.
  • Re-run nanoinfra channels login whatsapp. Old Baileys bridge auth data is not reused by neonize.
  • Update allowFrom entries to the WhatsApp sender ID without a leading +.

Optional: static LID mappings

Modern WhatsApp can deliver a sender's LID instead of their phone number. nanoinfra learns LID to phone mappings at runtime when both identifiers are present. You can also seed mappings up front, so the phone number resolves from the very first message:

{
"channels": {
"whatsapp": {
"enabled": true,
"allowFrom": ["1234567890"],
"lidMappings": { "123456789012345": "1234567890" }
}
}
}

Slack​

Uses Socket Mode — no public URL required.

1. Create a Slack app

  • Go to Slack API → Create New App → "From scratch".
  • Pick a name, and select your workspace.

2. Configure the app

  • Socket Mode: Toggle ON → Generate an App-Level Token with connections:write scope → copy it (xapp-...).
  • OAuth & Permissions: Add bot scopes: chat:write, reactions:write, app_mentions:read, files:read, files:write, channels:history, groups:history, im:history, mpim:history.
  • Event Subscriptions: Toggle ON → Subscribe to bot events: message.im, message.channels, app_mention → Save Changes.
  • App Home: Scroll to Show Tabs → Enable Messages Tab → Check "Allow users to send Slash commands and messages from the messages tab".
  • Install App: Click Install to Workspace → Authorize → copy the Bot Token (xoxb-...).

files:read is required to read files users send to nanoinfra. files:write is required for nanoinfra to send images, videos, and other file uploads. If you add either scope later, reinstall the Slack app to the workspace and restart nanoinfra so it uses the updated bot token.

3. Configure nanoinfra

{
"channels": {
"slack": {
"enabled": true,
"botToken": "xoxb-...",
"appToken": "xapp-...",
"allowFrom": ["YOUR_SLACK_USER_ID"],
"groupPolicy": "mention"
}
}
}

DM the bot directly or @mention it in a channel — it should respond!

[!TIP]

  • groupPolicy defaults to "mention".
  • groupAllowFrom: channel IDs the bot may respond in when groupPolicy is "allowlist".
  • groupRequireMention: when true and groupPolicy is "allowlist", the bot only replies to channels in groupAllowFrom and only when @mentioned, instead of to every message. It has no effect when groupPolicy is "mention" or "open". Use this to scope the bot to approved channels while keeping mention-only behavior.
  • DM policy defaults to open. Set "dm": {"enabled": false} to disable DMs.

Mattermost​

Mattermost needs no optional dependency: it ships in the base install.

Recommended WebUI setup

  1. Create a bot account in System Console → Integrations → Bot Accounts and copy its token.
  2. Run nanoinfra webui, then open Settings → Channels → Mattermost.
  3. Enter the server URL and the token. serverUrl and token are the only required fields.
  4. Save and enable Mattermost, then send the bot a direct message.

Manual setup

{
"channels": {
"mattermost": {
"enabled": true,
"serverUrl": "https://mattermost.example.com",
"token": "YOUR_MATTERMOST_TOKEN",
"teamId": "YOUR_TEAM_ID",
"groupPolicy": "mention",
"groupPolicyInThread": "open",
"replyInThread": true,
"dm": {
"policy": "allowlist"
}
}
}
}

teamId scopes the channel to one Mattermost team. groupPolicy defaults to "mention". groupPolicyInThread governs replies inside a thread, and it inherits groupPolicy when omitted. Set it to "open" when a follow-up in a thread should not need another @mention.

Mattermost issues pairing codes for direct messages only when dm.policy is "allowlist".

See the step-by-step Mattermost guide for the bot-account steps and troubleshooting.

Email​

Give nanoinfra its own email account. It polls IMAP for incoming mail and replies via SMTP — like a personal email assistant.

1. Get credentials (Gmail example)

  • Create a dedicated Gmail account for your bot (e.g. my-nanoinfra@gmail.com).
  • Enable 2-Step Verification → Create an App Password.
  • Use this app password for both IMAP and SMTP.

2. Configure

  • consentGranted must be true to allow mailbox access. This is a safety gate — set false to fully disable.
  • allowFrom: Add your email address.
  • smtpUseTls and smtpUseSsl default to true / false respectively, which is correct for Gmail (port 587 + STARTTLS). No need to set them explicitly.
  • Set "autoReplyEnabled": false if you only want to read/analyze emails without sending automatic replies.
  • postAction: Optional post-processing for processed emails: "delete" or "move" (default null). This runs only after an accepted email is successfully delivered to the AI pipeline.
  • postActionMoveMailbox: Destination mailbox used when postAction is "move" (for example "Processed" or "[Gmail]/Trash").
  • postActionIgnoreSkipped: If true (default), skipped emails are ignored for post-action and not moved/deleted.
  • postActionExpunge: When true, the channel allows a full-mailbox EXPUNGE fallback if UID-scoped expunge is unavailable or fails (default false). Enable only on very old IMAP servers that lack modern UIDPLUS support. Note that this fallback will expunge all messages marked as deleted in the mailbox, including ones not handled by the agent. Leaving this off is safe for all modern IMAP servers.
  • allowedAttachmentTypes: Save inbound attachments matching these MIME types — ["*"] for all, e.g. ["application/pdf", "image/*"] (default [] = disabled).
  • maxAttachmentSize: Max size per attachment in bytes (default 2000000 / 2MB).
  • maxAttachmentsPerEmail: Max attachments to save per email (default 5).
{
"channels": {
"email": {
"enabled": true,
"consentGranted": true,
"imapHost": "imap.gmail.com",
"imapPort": 993,
"imapUsername": "my-nanoinfra@gmail.com",
"imapPassword": "your-app-password",
"smtpHost": "smtp.gmail.com",
"smtpPort": 587,
"smtpUsername": "my-nanoinfra@gmail.com",
"smtpPassword": "your-app-password",
"fromAddress": "my-nanoinfra@gmail.com",
"allowFrom": ["your-real-email@gmail.com"],
"postAction": "move",
"postActionMoveMailbox": "[Gmail]/Trash",
"postActionIgnoreSkipped": true,
"postActionExpunge": false,
"allowedAttachmentTypes": ["application/pdf", "image/*"]
}
}
}

Microsoft Teams​

MVP — direct messages only.

Direct-message text in/out, tenant-aware OAuth, conversation reference persistence. Uses a public HTTPS webhook — no WebSocket. You need a tunnel or reverse proxy.

1. Create a Teams / Azure bot app registration

Create or reuse a Microsoft Teams / Azure bot app registration. Set the bot messaging endpoint to a public HTTPS URL ending in /api/messages.

2. Configure

{
"channels": {
"msteams": {
"enabled": true,
"appId": "YOUR_APP_ID",
"appPassword": "YOUR_APP_SECRET",
"tenantId": "YOUR_TENANT_ID",
"host": "0.0.0.0",
"port": 3978,
"path": "/api/messages",
"allowFrom": ["*"],
"replyInThread": true,
"mentionOnlyResponse": "Hi — what can I help with?",
"validateInboundAuth": true,
"refTtlDays": 30,
"pruneWebChatRefs": true,
"pruneNonPersonalRefs": true,
"refTouchIntervalS": 300
}
}
}
  • replyInThread: true replies to the triggering Teams activity when a stored activity_id is available.
  • mentionOnlyResponse controls what nanoinfra receives when a user sends only a bot mention (<at>nanoinfra</at>). Set to "" to ignore mention-only messages.
  • validateInboundAuth: true enables inbound Bot Framework bearer-token validation (signature, issuer, audience, lifetime, serviceUrl). This is the safe default for public deployments. Only set it to false for local development or tightly controlled testing.
  • refTtlDays (default 30) controls how old stored conversation refs can be before they are pruned.
  • pruneWebChatRefs (default true) drops refs with webchat.botframework.com service URLs.
  • pruneNonPersonalRefs (default true) drops refs whose conversation_type is not personal.
  • refTouchIntervalS (default 300) throttles how often successful sends refresh updated_at for active refs.

Signal​

Uses signal-cli daemon in HTTP mode — receive messages via SSE, send via JSON-RPC.

1. Install signal-cli

Install signal-cli and register a phone number:

signal-cli -u +1234567890 register
signal-cli -u +1234567890 verify <CODE>

Start the daemon:

signal-cli -a +1234567890 daemon --http localhost:8080

2. Configure

{
"channels": {
"signal": {
"enabled": true,
"phoneNumber": "+1234567890",
"daemonHost": "localhost",
"daemonPort": 8080,
"dm": {
"enabled": true,
"policy": "open"
},
"group": {
"enabled": true,
"policy": "open",
"requireMention": true
}
}
}
}
  • phoneNumber: Your registered Signal phone number.
  • daemonHost / daemonPort: Where signal-cli daemon is listening (default localhost:8080).
  • dm.policy: "open" (anyone can DM) or "allowlist" (only listed numbers/UUIDs). When "allowlist", unlisted DM senders receive a pairing code.
  • dm.allowFrom: List of allowed phone numbers or UUIDs (used when policy is "allowlist").
  • group.policy: "open" (all groups) or "allowlist" (only listed group IDs).
  • group.requireMention: When true (default), the bot only responds in groups when @mentioned.
  • group.allowFrom: List of allowed group IDs (used when group policy is "allowlist").
  • attachmentsDir: Override the directory where signal-cli stores inbound attachments. Defaults to ~/.local/share/signal-cli/attachments (the Linux default). Set this if signal-cli runs with a custom XDG_DATA_HOME or on macOS.
  • groupMessageBufferSize: Number of recent group messages kept for context (default 20, must be > 0).

[!TIP] The channel automatically reconnects to the signal-cli daemon with exponential backoff if the connection drops. Markdown in bot replies is automatically converted to Signal text styles (bold, italic, code, etc.).