ant
An ant is the agent you talk to: one Claude Code process in its own KVM guest. Each group has one ant, and the folder of the group on disk is the ant. The diary, the skills and PERSONA.md are files in that folder.
The folder is the agent
Every group lives at /srv/data/arizuko_<instance>/groups/<folder>/ on the host. When a message arrives, runed starts a guest and mounts that folder at /home/node/ inside the guest. The agent sees what is in the folder:
groups/solo/inbox/
PERSONA.md persona — name, tone, voice
CLAUDE.md the operator's runbook for this group
work.md what the agent is doing right now
diary/ dated work log, survives restarts
episodes/ compacted day, week and month summaries
facts/ durable knowledge
users/ one file per person the agent talks to
.claude/ CLAUDE.md, skills/, session state
If you copy the folder, you copy the ant. If you delete the folder, the ant is gone. No separate database row holds the persona.
Memory is layers of files in the folder
Memory is also files in the folder, in layers. The work file and the sender's file go into every prompt. The diary and the episodes have levels: a file per day, per week and per month, named by its period, in one flat folder per lens. When a task starts, the newest file of each level goes into the prompt, marked as folder-wide, because the diary and the episodes cover the whole folder and not one thread. The facts stay on disk until the agent searches for them:
layer file in the group home reaches the turn
──────────── ──────────────────────────────── ─────────────────────────────
now ~/work.md pushed whole, every turn
diary ~/diary/YYYYMMDD.md, newest of each level pushed
day, week, YYYY-WNN.md, YYYY-MM.md on a new task, <memory
month scope="folder">; the rest:
episodes ~/episodes/YYYYMMDD.md, /recall-memories
day, week, YYYY-WNN.md, YYYY-MM.md
month
people ~/users/<id>.md pushed whole for the sender,
<user-memory>
facts ~/facts/*.md pulled: /recall-memories,
then /find
index ~/.claude/projects/-home-node/ read by Claude Code itself
memory/MEMORY.md
/diary ──────────────────────────────────────────────► ~/diary/YYYYMMDD.md
session transcripts ─► /compact-memories episodes day ─► ~/episodes/YYYYMMDD.md
day files ─► /compact-memories <lens> week ─► YYYY-WNN.md ─► month ─► YYYY-MM.md
one flat directory per lens; the five tasks run isolated
The diary is the folder seen from inside: what the agent did and decided. The episodes are the folder seen from outside: the exchanges, read off the session transcripts. One skill, compact-memories, writes every level above the first with one method: keep what a person said or corrected, drop what the agent concluded, end with the one thing to do next. Five scheduled tasks run it. Every group gets these tasks at creation, and each task runs isolated, so a compaction turn does not summarise its own work.
Three processes build what the ant reads
Three processes build what the agent sees, and each process builds the part that only it holds:
routd buildAgentPrompt, routd/prompt.go:26 ───► RunRequest.MessageBatch
<system-note> <system> <autocalls> <persona> <topic>
<surface> <pane-context> <link-context> <messages>
│
│ POST /v1/runs
▼
runed preparePayload, runed/render.go:177 ────► the guest's stdin prompt
Topic session: <user id>
then routd's string unchanged
│
│ vsock, guest-init, stdin
▼
ant buildSystemPrompt, ant/src/index.ts:49 ─► the system prompt
SYSTEM.md, the package manual for codex and pi, PERSONA.md
ant hooks/prompt_context.py, UserPromptSubmit ► additionalContext
<knowledge layer="work"> <user-memory>
<memory scope="folder"> on a new task
The <user id> tag names the sender and holds no content. The hook reads the file of the sender in users/ and adds it as <user-memory>. Bot rows never start a turn. When a tool returns history on purpose, each bot row carries bot="true". So the agent does not read its own past words as the words of another person.
The selected packages' manuals are combined at ~/.claude/CLAUDE.md, including the required core manual. The agent reads the file on each turn, so a section stays in it only for one of four reasons:
- The agent needs the section before it can know to ask for it.
- A break in the section fails without an error.
- The section is a pointer that no user phrase triggers.
- A skill reads the section from that file.
Everything else lives in skills. A test fails when the file points at a skill or a doc that does not exist.
If a group has a SYSTEM.md, that file replaces the base system prompt completely, and nothing merges into it. PERSONA.md follows it.
Each turn gets a new guest
The guest lives for one turn. runed starts it when a message arrives, the agent reads the prompt and replies, and the guest powers off. Nothing that matters lives inside the guest, only in the mounted folder. The next turn gets a new guest and a new kernel with the same folder.
A turn has one deadline, RUNED_RUN_TIMEOUT, and the default is 20 minutes. The timeout of the agent is 30 seconds shorter, and never below 60 seconds, so the agent can report before runed stops the guest. For a longer job, split it into turns, write checkpoints to ~/facts/ or ~/tmp/, or use a background task. Do not raise the timeout.
A grant gives capability, not the folder depth
The depth of a folder says nothing about what its ant can do. solo/inbox and corp/eng/sre/oncall start with the same rights: the role:member role. Every folder gets this role at creation, and it carries the messaging verbs and nothing more. Every other right is a row that a principal gave to the folder for a subtree. Examples are the rights to register groups, edit routes, open egress or schedule tasks. If an ant has no row for a tool, the ant does not see the tool at all.
The agent calls tools over one socket
The agent calls MCP tools, such as send or find_messages, over a unix socket. routd hosts one socket per group at ipc/<folder>/gated.sock on the host. For each turn, runed runs a small bridge that accepts the vsock connection of the guest and connects it to that socket. Inside the guest, the mcpServers config of Claude Code points at a local path. Every tool call is a JSON-RPC message over that socket. routd runs the same grant check on it as on a call to the matching REST endpoint.
Selected packages supply the next turn
Startup installs the release's core package. arizuko package sync updates selected package sources. The turn renderer mounts their skills, native hooks, tools and combined manual read-only; custom group skills stay writable. Package updates need no agent migration turn.
The folder stays and the guest goes
- Stays
- Everything under
groups/<folder>/: the diary, facts, skills and the JSONL session transcripts in.claude/. - Goes
- The guest, all files in
/tmpinside it, and all process state in RAM. The own filesystem of the guest is a tmpfs overlay that power-off discards.
For this reason, an edit to the folder is enough. Change PERSONA.md on the host or through WebDAV, and the next guest uses the new persona without a restart or a reload.
Four pages continue from here
- a turn, end to end: where the prompt goes after the build.
- route tokens: how a browser or a webhook reaches an ant.
- grants: how an
aclrow becomes a permission for one tool. - jid: the addresses the agent uses to send messages out.