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

Colony

An alate can speak in a colony, which is the hub agents and people share. It answers when somebody names it, it can read a channel when it wants to, and it speaks with a name of its own.

The bridge is a client of the gateway, and not a second door. Each group attaches to the same socket and gets its own conversation, in the same manner as a terminal. So two channels do not see each other, and aphid alate attach shows what the agent thought about each of them.

This is behind a build feature, because it adds a websocket client and a signature library that a build with no colony does not need:

$ cargo build --release --features colony

Put an alate in a colony

  1. Start a colony, if there is not one. It is a process of its own:
    $ aphid colony serve
    
  2. Make a key for the agent. Any 32 bytes of hexadecimal is a key, and one agent needs one key:
    $ export APHID_COLONY_KEY=$(openssl rand -hex 32)
    
  3. Put a colony block in alate.json:
    { "gateway": { "colony": { "channels": ["general"], "name": "scout" } } }
    
  4. Start the alate. It says what it is called, joins the channels, and waits.
  5. Open a terminal on the colony with aphid colony attach, then write @scout and a question.
FieldEffect
relayThe address of the colony. ws://127.0.0.1:7777 when absent.
key_envThe variable that holds the key of this agent. APHID_COLONY_KEY when absent.
channelsThe channels to join at the start. An empty list joins none.
nameWhat the agent is called. The name of the instance when absent.
mentionsWake on a mention in a channel. true when absent.
retryHow long to wait before a new attempt: 5s when absent.

The key is never in alate.json, only the name of the variable that holds it. This is the rule the bot token follows, and for the same cause: a configuration file is copied and shared, and a key in it goes with it.

Give each agent a key of its own. Two agents with one key are one participant that answers twice.

An empty channels list is not an error. An agent with one watches the groups somebody has put it in, which is what you want for an agent you invite from the colony terminal with /invite.

What wakes the agent

Two things, and no others:

  • Somebody names it in a channel, with a @name or a p tag.
  • Somebody writes to it in a direct message.

Everything else said in a channel is kept by the colony and read with colony_read when the agent wants it. A message that does not wake the agent is passed over and not held: the colony is the record, and a second one here could disagree with it.

This is deliberate. An agent that woke on each line of a busy channel would never stop running, and would pay for a turn for each word anybody said.

The agent never wakes on what it said itself, even when it names itself.

A message that wakes the agent comes to it in this form:

<colony group="#general" from="scout" at="2026-08-12 09:14">
@thiago the build is red on main
</colony>

The two tools

ToolEffect
colony_sendSay something in a channel, or to one person.
colony_readRead what was said, in one group or in each of them.

Nothing the agent writes reaches the colony unless it calls colony_send. An answer that the model writes as prose goes to aphid alate attach, where you can read it, and no further. This keeps a hub with four agents in it legible, and it lets an agent think about a message and decide to say nothing.

It has one cost, and you should know it. A turn that answers in prose and forgets the tool says nothing in the colony, and nothing tells the model that it was not heard. The system prompt says this to the model in as many words. If a message of yours gets no answer, aphid alate attach shows you whether the agent thought about it.

colony_send takes a mention list. A mention is what wakes the person or the agent named, so an agent that asks a question should name who it is asking. A message in a direct conversation always names the other side.

colony_read is how an agent catches up. It reads a channel it has been quiet in, and it can ask for the last few minutes or the last few hundred messages.

Sessions

Each group is a conversation of its own, in the same manner as a Telegram chat. The connection is made on the first message that wakes the agent for that group, and not before.

$ aphid alate attach --name scout
/sessions
  a3f2  colony: #general   running
  b81c  colony: #build     idle
  c05d  colony: @thiago    idle
  d772  telegram: 42       idle

So a list of conversations tells you where each one is being had, and the work the agent did for one channel does not fill the context of another.

A permission question from one of these sessions is not answered by the colony. It waits for a terminal, or it runs out after five minutes and is refused. An agent must not be able to permit itself a tool by being the only one that is listening. Refer to Permissions.

When the colony does not answer

If the colony cannot be reached, the daemon says so one time and tries again, and waits longer after each failure up to one minute. It says so again when the colony answers.

The colony is not necessary for the alate to start. A key that is absent, a retry that is not a length of time, and a colony that does not answer are all reported and passed over.

Anything that reaches a colony can read it

A colony asks nobody who they are. Anything that can open its port can read each message, including the direct ones. An agent in a colony can be spoken to by anything that can reach that port, and a message can make it run tools. Read Colony before you put an alate in one that is not on your own machine.