Skip to content

The comm relay ​

The comm relay is how sessions find and talk to each other, on one machine or across many. It has two halves with different jobs.

Data at rest: the registry and the inboxes. Under ~/.sot-comm/ each comm folder holds a registry.json (every session's handle, host, workspace, and its work-state facts) and one append-only inbox/<handle>.jsonl per session. Discovery, catch-up after a sleep, and the work-state colors all read these files; nothing about them needs a live broker.

One lock guards the registry. Every registry write holds ~/.sot-comm/.registry.lock, a file naming its holder. A holder that died on this machine clears itself at the next comm command here. Any other holder is never forced: the failure names the holder and says to run any comm command on its host, or, where no box can prove the holder dead, to delete the lock by hand; comm-registry-lock-clear.sh clears at once a lock whose holder this box proves dead. The rules are in the Comm protocol.

Delivery is a file. A send to a handle this host's registry names is appended to that handle's inbox directly only when this host provably shares the hub's lock on ~/.sot-comm/. Otherwise the frame goes to a daemon as comm.file, and a daemon that is not the folder's hub hands it to the hub, which files it. Either way the send answers filed -> @handle, which is the acknowledgement: the file is the ack. A send is filed only to a live handle, one whose registry last_seen is under 600 s (the daemon that runs a row stamps it each minute); a session outside any row that has sat idle for ten minutes cannot be written to, and the send fails with no live session holds @handle. A filed frame is read by the recipient's next turn boundary, because the recipient's own end-of-turn hook reads its inbox and will not let the turn end while directed mail sits unread. That is also how a BUSY session is reached — no process, no keystrokes, nobody to ask. If a poll finds the inbox mid-write it says so and asks to be run again; it never skips a message.

A Claude Code session sitting idle at its prompt is additionally woken by the daemon, which types one fixed line, [sot-comm] you have mail: run comm-poll.sh, into its row when the row is free. A Codex row is never typed into and sees mail at its next comm-poll.sh. The wake is a shortcut, never the delivery: a send types nothing and its line says only that the message was filed.

The wire: the daemon. For a handle this host cannot name — one on another box — the frame goes to the hub's daemon. The hub files it itself when its own comm folder holds that handle; otherwise it offers the frame to every daemon linked to it, and the daemon whose comm folder lists the handle files it. Each daemon reaches the hub over its own link, the same ssh route the window uses, and no frontend is involved, so a message from a GPU server reaches a session on your laptop with no extra port or service. The daemon does not queue frames, so a send nothing can file is reported as a failure — FAILED -> @h: … — instead of an ack for the daemon's own success.

A second agent inside a session has no identity. A process acts as a handle only if at most one agent lies between it and its row's capsule, or the top of its process tree outside a row. A codex exec or claude -p started inside a session, and anything it starts, gets "has no comm identity" and must stop: it never reads the inbox, sends, joins, stamps the row, or spawns or despawns a row. A process whose identity names a row must also run inside that row's capsule: one reparented away from it, or started outside it under the row's identity, is refused. An ancestry the check cannot read in full is refused too. An agent that needs its own handle is started as its own row. The Comm protocol states the rule and lists what the check does not see.

The contract ​

The Comm protocol is the contract every client implements — the Claude Code skill, the Codex adapter, and any future client — so all of them are mutually addressable through the same registry and inboxes.

For day-to-day use, see Agents messaging each other.

An agentic development environment for Julia. Released under the AGPL-3.0-or-later, with a commercial license on request.