The map
Four programs, three transports, and one program a person actually runs. Every edge below is a process boundary, and every one of them can be absent.
Every edge
| edge | who starts it | carries | absent? |
|---|---|---|---|
| magi → casper | magi, once per tool call | the call as argv and stdin; the result on stdout | three builtin tools remain: read, write, edit |
| magi → melchior | magi, once per session | what the session is doing, up; what the model said and who is calling, down | no model and no siblings — the session runs and says so |
| magi → balthasar | magi, once per session | every entry out; recalled memory and prior sessions back | the journal on disk becomes the record instead |
| session → session | either, through melchior | messages, questions and answers between agents | a session with no siblings, which is the ordinary case |
| front end → session | the front end | what was typed, up; everything to draw, down | the turn keeps running — close the window and come back |
Why separate processes
Three things fall out of the split that do not fall out of modules in one binary.
- A crash is contained. A tool that segfaults takes down one process that was going to exit anyway. The turn sees a failed result, not a dead harness.
- The kernel does the identifying.
SO_PEERCREDnames the program on the other end of a socket, so there is no token to issue and none to leak. - The boundary is checkable. A gate on the merge path refuses any file in a sibling that names a harness — the separation is enforced, not merely intended.
A second implementation is what keeps a protocol honest. magi ships a second tool peer written in Lua, deliberately unlike the first — a different language, a different lifecycle, and one thing it cannot do at all. A protocol with one implementation is a function call with extra steps.