Channels
The front doors tomo serve opens: the always-on web chat plus Telegram, Discord, Slack, and iMessage, each gated by an allow-list, and how /session binds a chat to a shared conversation you can carry between channels.
Channels are the front doors tomo serve opens.
The web chat is always on; the rest start only when you configure them.
Every channel names the conversations it will serve, so a leaked token or a stray invite never hands anyone an agent.
tomo serve
That one command runs the web chat plus every channel you have configured, all against the same agent, memory, and policy gate.
The web chat
The web chat always runs, on loopback by default. Point a browser at it and you have a chat window with no token to set up.
tomo serve --addr 127.0.0.1:8765
--addr is the listen address, and it defaults to 127.0.0.1:8765.
Keeping it on loopback means the web chat is reachable only from the machine tomo runs on.
The web chat cannot be pushed to on its own, so it is not a place a scheduled job or the heartbeat can deliver results.
Allow-lists are the safety boundary
Every remote channel takes an allow-list, and that list is the boundary. tomo only answers conversations you have named: a chat id, a channel id, or a handle. A message from anywhere else is ignored, so a bot token that leaks or an invite someone forwards does not turn into an open agent. Set the allow-list before you hand out the bot, not after.
Telegram
Create a bot with BotFather, take its token, and list the chat ids allowed to reach it.
channels:
telegram:
token: ${TELEGRAM_BOT_TOKEN}
allow_chats: [123456789]
allow_chats is a list of numeric chat ids.
Telegram can post on its own, so it is a good target for background results.
Discord
Create a bot application, take its token, and list the channel ids it will serve.
channels:
discord:
token: ${DISCORD_BOT_TOKEN}
allow_channels: ["000000000000000000"]
allow_channels is a list of channel id strings.
Slack
Slack needs two tokens: the app-level token opens the socket connection, and the bot token posts messages.
channels:
slack:
app_token: ${SLACK_APP_TOKEN}
bot_token: ${SLACK_BOT_TOKEN}
allow_channels: ["C0000000000"]
allow_channels is a list of channel ids (the C... ids Slack assigns).
iMessage
iMessage is macOS only and off unless you enable it, since it reaches a real Messages account. It reads the local Messages database, so the tomo process needs Full Disk Access granted in System Settings.
channels:
imessage: # macOS only, needs Full Disk Access
enabled: true
allow_handles: ["+15555550123"]
allow_handles lists the phone numbers or emails permitted to drive the agent.
Sessions and binding
Each chat has its own conversation by default, scoped to the channel it arrived on. You can bind a chat to a named session so more than one chat shares one conversation, with the same history and memory.
Send /session NAME from any chat to bind it to a shared session:
/session work
Bind two chats to the same name, on the same channel or different ones, and a conversation started in one carries into the other.
Ask a question on Telegram, then continue it in the web chat, both bound to work, and tomo sees one thread.
Send /session with no name to see the current one:
/session
It replies with the session this chat is bound to, or the channel-scoped default if you have not bound it. Binding is also what lets a scheduled job drop its result into a conversation you are already having.