8.2 KiB
9P Architecture
9P is Ollie’s public API. The server exposes sessions, agents, prompts, tools, state, and control operations as a filesystem. Clients use reads, writes, and blocking reads instead of an application-specific RPC API.
client ── 9P2000 over Unix socket or TCP ── olliesrv
│
└─ virtfs.Tree
└─ Ollie namespace
agent ── authenticated 9P over Unix socket ── toolsrv
├─ registry
├─ sandbox
└─ processes
The Ollie namespace is declared in cmd/olliesrv/internal/fs/spec.go, materialized by virtfs, and served by olliesrv. The separate toolsrv namespace is described in architecture-toolsrv.md.
Transports
olliesrv listens on the Unix socket selected by $NAMESPACE. It can also expose the same server on an optional TCP address. The TCP listener speaks the same 9P protocol and namespace; it does not add an HTTP or JSON API.
Use ollie-9p for direct operations:
ollie-9p ls session/
ollie-9p read session/myproj/agent/coding/chat
A 9P filesystem client can mount the namespace when the platform provides a 9P mount utility. The o shell client and KDE integrations are higher-level clients over the same namespace.
Root namespace
| Path | Mode | Purpose |
|---|---|---|
backends |
r | Available backend names. |
models |
r | Available models as backend<TAB>model. |
agents |
r | Available agent profiles. |
tools |
r | Available tool names and descriptions. |
help |
r | Generated filesystem reference. |
aliases |
r | Stable ID-to-path mappings. |
ctl |
rdwr | Server controls such as invalidate and kill. |
eventwait |
r | Blocking server-event stream. |
generate |
rdwr | One-shot LLM request. |
session/ |
dir | Session collection. |
One-shot generation uses a write followed by a read:
echo 'summarize this repo' | ollie-9p rdwr generate
echo '{"prompt":"explain recursion"}' | ollie-9p rdwr generate
Session namespace
| Path | Mode | Purpose |
|---|---|---|
session/new |
rdwr | Create a session. Write name=X [remote=Y] [yolo=true]; read the resulting name. |
session/idx |
r | Session index: session-id\tsession-name\tpaused\tconnected\tremote\tcwd. |
session/{sname}/env |
r | Session environment and runtime variables. |
session/{sname}/paused |
r | Pause state. |
session/{sname}/goal |
r/w | Session goal text. Writing triggers the workflow if not running. |
session/{sname}/goalstatus |
r/w | Goal status: running, complete, blocked, or error. |
session/{sname}/goalwait |
r | Blocks until goal status changes. |
session/{sname}/ctl |
rdwr | kill, save, invalidate, pause, and resume. |
session/{sname}/name |
r/w | Mutable session display name. |
session/{sname}/id |
r | Immutable session UUID. |
session/{sname}/agent/ |
dir | Agents in the session. |
Create a session and agent:
echo 'name=myproj' | ollie-9p write session/new
echo "name=coding cwd=$PWD" | ollie-9p write session/myproj/agent/new
Agent namespace
| Path | Mode | Purpose |
|---|---|---|
agent/new |
rdwr | Create an agent from name=X cwd=Y. With prompt=, runs as sub-agent. |
agent/idx |
r | Agent index: session-id\tagent-id\tagent-name\tparent-id\tdepth\tstate. |
agent/{aname}/prompt |
w | Queue a user turn. |
agent/{aname}/fifo |
r/w | Prompt queue. |
agent/{aname}/feed |
r/w | Change-detecting input stream. |
agent/{aname}/chat |
r | Filtered streaming chat output. |
agent/{aname}/chat.raw |
r | Full streaming output with markers. |
agent/{aname}/statewait |
r | Blocking state-change read. |
agent/{aname}/log |
r | Conversation snapshot. |
agent/{aname}/plan |
r/w | Persistent planning scratch space. |
agent/{aname}/cfg |
r/w | Agent configuration. |
agent/{aname}/ctl |
rdwr | Agent controls and queries. |
agent/{aname}/stats |
r | Token, cost, and context statistics. |
agent/{aname}/name |
r/w | Mutable agent display name. |
agent/{aname}/id |
r | Immutable agent UUID. |
agent/{aname}/proc/ |
dir | Agent-owned background processes. |
Submit and observe work:
echo 'fix the bug in main.go' \
| ollie-9p write session/myproj/agent/coding/prompt
ollie-9p read session/myproj/agent/coding/chat
ollie-9p read session/myproj/agent/coding/statewait
Sub-agents use the same namespace. The agent/new rdwr operation can create a child session and return its final response after the child exits.
Agent controls
| Command | Purpose |
|---|---|
stop |
Interrupt the running turn. |
compact |
Compact conversation history. |
clear |
Clear conversation history. |
kill |
Remove the agent. |
inject <text> |
Add text to the context. |
agent [profile] |
Show or switch the profile. |
model [name] |
Show or switch the model. |
models |
List models. |
backend |
Show the current backend. |
name [name] |
Show or set the display name. |
cwd [path] |
Show or change the working directory. |
tools |
List loaded tools. |
tool_load <name> |
Load a tool. |
tool_unload <name> |
Unload a tool. |
proc / proc top |
List background processes. |
proc term <pid> / proc kill <pid> |
Stop a background process. |
proc out <pid> |
Read process output. |
proc dismiss <pid> |
Dismiss a completed process. |
systemprompt |
Read the rendered system prompt. |
help |
List controls. |
Control files use request-response semantics: write one command, then read the response.
Plan 9 interaction patterns
- Control files: writes express operations such as stop, kill, rename, compact, and reload.
- Blocking reads:
statewait,eventwait,feed, and streaming chat replace event subscriptions with ordinary reads. - Stateless request/response:
generate,ctl, andrdwrfiles accept a request and return a result. - Change detection:
feeddoes not wake readers for identical consecutive data. - Shared namespace: multiple clients can inspect and modify the same sessions concurrently.
- Stable aliases: mutable names are listed normally; immutable IDs can resolve the same nodes through invisible aliases.
Shell composition is intentional:
for p in session/*/agent/*/prompt; do echo 'run tests' > "$p"; done
for s in session/*/agent/*/statewait; do cat "$s" > /dev/null; done
grep -r TODO session/*/agent/*/plan
cat session/*/agent/*/stats
Authentication and permissions
olliesrv serves the Ollie namespace and applies 9P identity and file-mode checks. The namespace is intended for the local user and trusted clients; the Unix socket and optional TCP exposure provide transport boundaries, not a separate authorization API.
The Ollie client authenticates to toolsrv over a separate 9P connection. The toolsrv authentication and policy details belong in architecture-toolsrv.md.
Lifecycle
At startup, olliesrv resolves the namespace, constructs the virtfs tree, starts the Unix listener, restores sessions, and optionally starts TCP. Shutdown cancels the root context, closes listeners, terminates tracked processes, removes the Unix socket, and flushes logs.
The filesystem tree contains live handlers. Reads and writes therefore reach current session and agent state rather than a static export. Dynamic session and agent directories are rebuilt through virtfs bindings when the namespace resolves them.
Source map
| Responsibility | Source |
|---|---|
| Ollie namespace declaration | cmd/olliesrv/internal/fs/spec.go |
| Namespace tree construction | cmd/olliesrv/internal/fs/newroot.go |
| Ollie 9P protocol server | cmd/olliesrv/server.go |
| Server startup and listeners | cmd/olliesrv/main.go |
| Direct command-line client | cmd/ollie-9p/main.go |
| Shared virtfs implementation | virtfs/ |
| Tool-service 9P server | cmd/toolsrv/p9.go |
| Tool-service client | cmd/olliesrv/internal/toolclient/toolsrv.go |