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

Gateway

The gateway is a Unix socket in the home of the alate. The daemon listens on it. Each terminal that attaches is a client, and so is the Telegram bot and the colony bridge.

The gateway is the only door. Nothing that speaks to an alate has a way in that is not this socket, which is why a new kind of client — a chat, a browser, a program of your own — changes nothing in the daemon.

The protocol

One JSON object for each line, in both directions. You can read it with nc, and you can write another client for it.

Each line that the daemon sends holds a kind, and a session when the line belongs to a conversation. A line with no session is the daemon speaking for itself: the greeting, a heartbeat, a session list, a permission question.

A client sends {"kind":"attach"} first. The daemon then opens a session for it and answers with hello. A program that only wants to know whether an alate is awake connects and closes without sending anything, and no conversation is made for it.

A client can also say what it is: {"kind":"attach","channel":"telegram: 42"}. The name is what /sessions shows for that conversation. It is cut to 32 characters, and line ends are removed, because it is printed in a list. The field can be absent, and a client that does not send it is listed as attached.

What a client sends

KindFieldsEffect
attachchannel (optional)Say that this is a client, and open a session for it.
prompttextSay this to the agent, as if it were typed.
cancelStop the run in flight.
answerid, decisionAnswer a confirm. allow, allow_always or deny.
watchidLook at a different session, and replay it.
sessionsAsk what sessions there are.
newOpen another session on this connection.

A request needs no session on it. A connection has one session that it watches, and each request is about that one. watch is what changes it.

What the daemon sends

KindFieldsMeaning
helloinstance, model, context_window, thinkingThe first frame. What this alate is.
session_openedinfoA session started. Sent to everybody.
session_closedidA session ended, and sends nothing more.
sessionslive, storedThe answer to sessions, to the connection that asked.
history_startidA replay starts. What is drawn for this session is old.
history_endidThe replay is complete. What comes now is live.
turn_startedA turn started.
texttextText from the model.
thinkingtextReasoning from the model.
tool_stream_startblock, nameA tool call opened, and its arguments still arrive.
tool_stream_deltablock, bytesMore of those arguments arrived.
tool_callid, name, argumentsA tool call, complete and committed.
tool_progressid, chunkPartial output of a tool.
tool_resultid, name, text, is_error, detailsA tool completed.
turn_endedusage, stop, errorA turn is complete.
run_endedstop, turns, errorThe run stopped.
noticetextSomething a plugin wants seen.
prompttextA prompt went to the agent. Echoed to everybody in that session.
heartbeatat, noteThe alate woke on its own.
confirmid, tool, summary, riskA tool waits for permission. The first answer decides.

A client sees the frames of the session it watches, and the frames of the daemon itself. Two terminals on two sessions thus do not draw each other’s replies.

Watching a different session

To change what it watches, a client sends {"kind":"watch","id":"..."}. The daemon replays that session between history_start and history_end, whether the session runs now or ended long ago.

There is no store of recent frames. What a client missed is in the transcript, which is what watch reads — so what it gets back cannot disagree with what happened.

Five kinds are not replayed: confirm, hello, sessions, history_start and history_end. A question that was answered an hour ago must not open a window over the new client, and the other four are addressed to one connection and not to a conversation.

The log

Each line is also written to alate.log in the home. Read the hours when nobody watched with jq:

$ jq -r 'select(.kind == "heartbeat") | .at + "  " + .note' alate.log
$ jq -r 'select(.session == "20260811T090000-0000") | .text // empty' alate.log

This file is the frames, and not the log of the program. For the log of the program, refer to Logs.

The socket

The socket permits only its owner to read and write it. Anything that can connect can make the agent run commands, so the permissions of the file are the whole of the access control.

The gateway needs a Unix socket, so aphid alate does not work on Windows.

A socket file that no daemon is behind is removed and made again. Two daemons cannot serve one alate: the second one stops and says so.

gateway.socket in alate.json moves the file. It is gateway.sock in the home when absent.

The clients

ClientWhat it is
CLIaphid alate attach. A terminal on the alate.
TelegramA bot. Each chat is a conversation.
ColonyNot written yet.