Skip to main content
One Comis install can serve your whole team. Many people message it, each gets their own private conversation, and you decide whether they share one assistant or each get their own - fully isolated. This page explains the model and the exact configuration, Telegram first, then every other channel.
You don’t need to understand the technical details to use this feature. The configuration examples below are copy-paste ready.

The one rule that matters

Comis isolates by agent, not by person. Each agent has its own memory, its own secrets, its own budget, and its own tool policy. So the question “how do I keep my users separate?” comes down to one decision:
  • People who should share knowledge (one team, one family) → point them at one shared agent.
  • People who must stay private from each other (different clients, departments) → give each their own agent, and route to it.
Everything below is how you wire those two cases.

Do users see each other’s messages?

Short answer: no - not their messages. The only thing a shared agent pools is what it has learned over time. Here is each layer:
LayerAre two users isolated?Why
Telegram transport (DMs)✅ YesA private 1:1 chat with the bot is invisible to everyone else - Telegram enforces this. One bot serves unlimited private DMs.
Telegram transport (groups)❌ No (by design)Everyone in a group sees the group’s messages. That is what a group is - use DMs for privacy.
Comis conversation/session✅ Yes (default)The default per-channel-peer session gives each person their own thread. User A’s transcript never enters User B’s session.
Agent long-term memory⚠️ Only if they use different agentsAn agent’s learned facts are scoped to the agent. Two people on the same agent draw on the same memory; give them separate agents for full separation.
A shared agent shares its memory. Conversations stay private, but the facts an agent distills over time are stored per-agent - not per-person. If two people talk to the same agent, a fact learned from one can surface for the other. For real separation between people, give each their own agent (Setup B below). For a team that should pool knowledge, one shared agent is the feature, not a leak.

Setup A - one shared agent (a team that shares knowledge)

The simplest case. Everyone DMs the same bot and talks to the same agent. Their conversations are already private (Telegram DMs + per-peer sessions); they share the agent’s learned memory, which is usually what a single team wants.
# ~/.comis/config.yaml
channels:
  telegram:
    enabled: true
    botToken: "${TELEGRAM_BOT_TOKEN}"
    # Optional: restrict who can use the bot at all (see "Access control" below)
    allowFrom:
      - "678314278"      # Alice
      - "5900067809"     # Bob

agents:
  default:
    model: claude-sonnet-4-6
Nothing else is needed - routing.defaultAgentId is default, and the default session scope already keeps each person’s chat separate.

Setup B - one agent per person or team (full isolation)

When people must stay private from each other, give each their own agent and use top-level routing to send each person (or group) to their agent. Because each agent is its own isolation boundary, this separates memory, secrets, and budget - not just the conversation.
# ~/.comis/config.yaml
channels:
  telegram:
    enabled: true
    botToken: "${TELEGRAM_BOT_TOKEN}"

agents:
  default:
    model: claude-sonnet-4-6
  alice:
    model: claude-sonnet-4-6
  bob:
    model: claude-sonnet-4-6

routing:
  defaultAgentId: default
  bindings:                 # first match wins; more specific wins ties
    - channelType: telegram
      peerId: "678314278"   # Alice's Telegram user ID -> her own agent
      agentId: alice
    - channelType: telegram
      peerId: "5900067809"  # Bob -> his own agent
      agentId: bob
Now Alice and Bob each get an agent with its own memory - nothing Alice tells the bot can surface in Bob’s chats, and vice versa. The same pattern routes a whole group to a team agent (use channelId for a specific chat/group, or guildId for a Discord server).
Routing bindings are ranked by specificity: peerId (8) > channelId (4) > guildId (2) > channelType (1), and the first match wins for equal weight. Full details and cross-channel examples in Multi-agent routing.

The building blocks

Four independent levers cover every multi-user setup. Mix them as needed.

1. Access control - who can use the bot at all

channels.<type>.allowFrom is an allowlist of sender IDs. Empty (the default) means anyone can talk to the bot; list IDs to lock it down.
channels:
  telegram:
    allowFrom:
      - "678314278"     # only these Telegram user IDs may reach any agent
      - "5900067809"
allowFrom is sender-keyed, not chat-keyed. Putting a group’s chat ID here does nothing - list each individual user’s ID. Leave it empty (or omit it) to allow everyone.

2. Conversation isolation - how sessions are split

Each agent’s session.dmScope.mode controls how DM conversations are keyed. The default already isolates per person; change it only if you want a single shared thread.
agents:
  default:
    session:
      dmScope:
        mode: per-channel-peer   # default
        threadIsolation: true    # separate Telegram forum topics get their own session
modeEffect
per-channel-peer (default)One session per channel + person. Each user’s DM is its own thread.
per-peerOne session per person, shared across channels (their Telegram and Discord DMs merge).
mainAll DMs share one session - everyone talks into the same thread. Avoid for multi-user.
per-account-channel-peerAdds the bot-account identifier to the key (for multi-bot session isolation).

3. Agent routing - who talks to which agent

Top-level routing maps people, chats, groups, or whole channels to a specific agent (Setup B above). This is the lever that gives memory/secret/budget isolation, because each agent is its own tenant. See Multi-agent routing.

4. Trust-based replies - different treatment per person (advanced)

Give known operators a different model or system prompt than strangers, within a single agent, via agents.<id>.elevatedReply. You assign each sender a trust label (a name you choose), then map labels to model routes and prompt overrides.
agents:
  default:
    elevatedReply:
      enabled: true
      defaultTrustLevel: guest          # everyone unlisted
      senderTrustMap:
        "678314278": admin              # Alice is "admin"
        "5900067809": member            # Bob is "member"
      trustModelRoutes:
        admin: claude-opus-4-7          # admins get the stronger model
      trustPromptOverrides:
        guest: "You are a read-only assistant. Be brief and never take destructive actions."
Trust labels are free-form strings you define - there is no fixed admin/member/guest list. elevatedReply changes the model and system prompt per trust level; it does not replace allowFrom (access) or routing (agent isolation). Use it on top of them when different people need different capabilities from the same agent.

Telegram specifics

  • For privacy between users, use DMs. Each person starts a private chat with the bot; Telegram keeps those chats mutually invisible. No setup required.
  • Groups are shared by design. Everyone in a Telegram group sees the group’s messages. Use a group when you want shared context, not for per-user privacy.
  • Finding IDs for allowFrom / routing: a user’s numeric Telegram ID and a group’s chat ID both appear in the logs on every inbound message (senderId, chatId). See Finding a group’s chat ID and the allowFrom steps on the Telegram page.

Other channels

The same four levers work on every channel. Only two things change per platform: the routing-key field you bind on, and the sender-ID format you put in allowFrom. A private 1:1 (DM / direct chat) is transport-isolated on every platform; shared spaces (groups, servers, channels) are shared by design.
ChannelPrivate 1:1Shared spaceRoute a shared space withallowFrom sender ID
TelegramDMgroup / supergroupchannelId (chat ID, negative)numeric user ID
DiscordDMserver → channelguildId (server) and/or channelIdnumeric user ID
SlackDMworkspace → channelchannelId (C…)user ID (U…)
WhatsApp1:1 chatgroupchannelId (group JID …@g.us)E.164 phone (+1…)
Signal1:1 chatgroupchannelId (group:<id>)phone or UUID
iMessage1:1 chatgroupchannelId (chat GUID)phone or email
LINE1:1group / roomchannelId (group/room ID)LINE user ID (U…)
IRCprivate messagechannel (#name)channelId (#channel)nick ⚠️
Emailthread- (1:many by recipients)route by channelType: emailemail address
peerId (the sender) works on every platform. guildId is Discord-only - it’s the server ID; every other platform routes a shared space with channelId.
# Discord: a whole support server -> support agent; one VIP anywhere -> vip agent.
# WhatsApp: route one group to a team agent (channelId = the group JID).
routing:
  defaultAgentId: default
  bindings:
    - channelType: discord
      guildId: "9876543210"            # server ID -> support agent
      agentId: support
    - channelType: discord
      peerId: "112233445566"           # one user, any channel -> vip agent
      agentId: vip
    - channelType: whatsapp
      channelId: "123456789-1234567890@g.us"   # one WhatsApp group -> team agent
      agentId: team
See each channel’s page under Channels for how to find its IDs.

Platform caveats for multi-user

A few platforms need extra care when several people share one install:
  • IRC - nicks are spoofable. IRC nicknames aren’t authenticated by default, so allowFrom on IRC is not a security boundary unless your network enforces registration (NickServ) and you trust it. Don’t gate a sensitive agent on an IRC nick alone.
  • Email - the From address isn’t verified. channels.email.allowMode is allowlist by default (only listed addresses reach the agent; set open to accept all). But sender addresses can be spoofed and Comis does not check SPF/DKIM/DMARC, so treat the email allowlist as convenience, not strong authentication, for high-stakes agents.
  • WhatsApp, Signal, iMessage, Email are personal accounts, not bots. The adapter is bound to one phone number (WhatsApp/Signal), one Apple ID on macOS (iMessage), or one mailbox (Email) - so “one bot per channel” is really “one account per channel.” Everyone talks to that single number/address; you still isolate people with per-person agents via routing, they just all see the same identity.
  • Slack & Discord are team-native. A Slack workspace or Discord server is already a multi-user space; allowFrom gates at the bot/workspace level (a user can or cannot reach the bot at all), and you split people across agents with routing as usual.

One identity per channel

Each Comis daemon runs one identity per channel type - one Telegram/Discord/ Slack/LINE bot, one WhatsApp or Signal phone number, one iMessage Apple ID, one mailbox. That is not a limit on users (one identity serves unlimited private DMs and shared spaces); it only means you can’t run two different identities of the same type (two Telegram @usernames, two phone numbers) in a single daemon. If you genuinely need a second identity, run one daemon per identity - see Routing to a separate bot identity on the Telegram page.

Multi-agent routing

The full routing schema, specificity rules, and cross-channel examples.

Sessions

How conversations are keyed and isolated per person, channel, and topic.

Memory

What an agent learns and stores - and why memory is scoped per agent.

Telegram setup

Step-by-step bot creation, groups, channels, and troubleshooting.