Skip to content
tomo

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.