ollie/doc/architecture-toolsrv.md

11 KiB

Toolsrv Architecture

toolsrv is Ollie's separate tool-execution service. It exposes a 9P filesystem over a Unix socket. olliesrv connects through ToolsrvConn; remote deployments use the same client protocol over an SSH-forwarded Unix socket.

The agent-side client and runtime integration are covered by architecture-core.md and architecture-prompting.md.

The service owns tool discovery, metadata resolution, per-agent loaded-tool state, process execution, output streaming, sandbox enforcement, bypass requests, and process lifecycle.

Position in the runtime

flowchart LR
    A[Agent runtime] --> C[ToolsrvConn]
    C -->|authenticated 9P| S[toolsrv Server]
    S --> R[per-agent Registry]
    S --> P[Process State]
    P --> M[Metadata resolver]
    P --> E[Tool executor]
    E --> X[Native Landlock sandbox helper]
    E --> B[Bypass broker]
    E --> T[Installed tool command]

toolsrv is not an in-process library used by the agent loop. The client and server communicate through the 9P namespace. The server is independently deployable and can execute tools on a remote host.

Startup and shutdown

The command is started as:

toolsrv serve --cwd <path> --listen <socket> [--yolo] [--no-auth] [--idle-timeout duration]

Startup sequence:

  1. Require the serve subcommand and a --listen path.
  2. Expand ~ in --cwd.
  3. Create the log sink and call util.EnsureEnv().
  4. Start the native Landlock sandbox helper for each restricted tool execution.
  5. Create the per-agent registry and the server state.
  6. Apply --yolo to the server and process state.
  7. Build the 9P tree from server.Spec.
  8. Install signal cancellation, process cleanup, optional idle monitoring, and process garbage collection.
  9. Remove a stale socket and listen on the Unix socket.
  10. Accept connections and serve each connection concurrently.

Shutdown cancels the run context, closes the listener, kills all tracked processes, removes the socket, flushes logs, and exits. A SIGINT or SIGTERM initiates the same cancellation path. The idle monitor exits before the first connection after a 60-second startup grace period, or after the configured idle timeout once all connections close.

Server state

server.Server owns authentication, the tool registry, the yolo flag, and a server.State process manager.

type Server struct {
    secret   string
    token    string
    registry *registry.Registry
    yolo     bool
    Fs       *State
}

NewServer creates a state object with no fixed working directory. Working directory, environment, session ID, and agent ID are supplied through each process request and its associated agent connection.

BuildTree passes the server into virtfs.BuildTree(Spec(server)). Namespace handlers capture the server and process state directly; there is no second RPC dispatcher.

Authentication

The client authenticates through the 9P Tauth exchange. The first authenticated client supplies the shared secret; the server stores it and returns a random session token. Later clients must provide the same secret and receive the existing token. A different secret is rejected.

--no-auth disables this check for debugging. It is not the normal deployment mode. The token is required in proc/new payloads and binds tool requests to the authenticated toolsrv instance.

The Ollie client generates a secret for the first connection, saves it for reconnect, authenticates as agent, and attaches the authenticated fid. The client also sends an agent ID with tool-list and process requests so registry state stays scoped per agent.

9P namespace

/
├── ctl
├── tools
├── all
├── info
├── bypass/
│   ├── pending
│   └── resolve
└── proc/
    ├── list
    ├── new
    ├── new.bg
    └── {pid}/
        ├── out
        ├── wait
        ├── stat
        └── ctl

Control and discovery

Path Operation Behavior
ctl write load <agentID> <tool> or unload <agentID> <tool>
tools read/write write an agent ID, then read its loaded tool list as JSON
all read list all host-available tools and descriptions
info read return platform and arch

tools reads the registry for the supplied agent ID. all calls metadata discovery directly and is not the per-agent loaded set.

Process namespace

Path Operation Behavior
proc/list read/write list processes, optionally filtered by agent ID
proc/new read/write execute a tool and block for a structured result
proc/new.bg read/write start a background tool and return its PID
proc/{pid}/out read read accumulated process output
proc/{pid}/wait blocking read wait for exit and return the exit code
proc/{pid}/stat read return process state, runtime, and tool metadata
proc/{pid}/ctl write signal or dismiss a process

Closing a request fid cancels the request context. This is the cancellation path used when an agent interrupts a blocking tool call.

Bypass namespace

Path Operation Behavior
bypass/pending blocking read return the next pending approval request as JSON
bypass/resolve write JSON approve or reject a request by ID

The bypass broker is separate from normal sandbox execution. A tool asking for bypass creates a pending request containing its ID, command, working directory, and environment. An authorized observer resolves it through the namespace.

Tool metadata and discovery

Tool metadata format, executable versus metadata-only tools, command resolution, and host variants are documented in architecture-tools.md. The toolsrv registry consumes that metadata and evaluates variants on the host where the service runs.

Per-agent registry

registry.Registry stores loaded tools and revision counters by agent ID:

type Registry struct {
    agents    map[string]map[string]protocol.ToolInfo
    revisions map[string]uint64
}

The registry is deliberately separate from discovery. Discovery describes every tool available on the host. Loading makes a tool callable for one agent. Load resolves current metadata and increments that agent's revision. Unload removes the tool and increments the revision. Loaded returns tools sorted by name. Lookup resolves one loaded tool.

The agent client polls or reads the tool listing after loading changes. The registry does not inspect executable contents.

Request payloads and results

proc/new receives newline-separated key=value fields. The client sends at least:

token=<auth token>
tool=<tool name>
agent=<agent ID>
<argument>=<escaped value>

Values escape newlines and backslashes. The server parses the payload with protocol.ParsePayload, verifies the token, identifies the tool, and converts the remaining fields to JSON arguments before execution.

The synchronous result is a protocol.ToolResult:

{
  "content": [{"type": "text", "text": "tool output"}],
  "isError": false
}

Content can also include image data with a media type. Errors are returned as structured results with isError: true where possible.

Process lifecycle

State tracks all process objects. Each Proc records its ID, command, tool, session ID, agent ID, start and end times, exit code, output, cancellation function, and current state.

Synchronous requests create a process, execute it, stream optional output, and expose the final result through the request fid. Background requests use proc/new.bg; the process remains in the state table and its output and exit status are available under proc/{pid}.

Process controls include:

  • signal <N> to deliver a signal to the child.
  • dismiss to remove a completed process from the namespace.
  • KillAll during server shutdown.
  • Periodic garbage collection for old completed processes.

When a background process exits and contains both a session ID and agent ID, toolsrv writes a system completion message to the agent's prompt file through the Ollie 9P namespace.

Tool execution

exec.ExecuteTool performs the following steps:

  1. Resolve the executable from metadata and the tools directory.
  2. Parse and remove dispatch fields from the JSON arguments: bypass, timeout, and sandbox.
  3. Resolve the working directory and environment.
  4. Route the command to direct bypass execution or sandboxed execution.
  5. Capture output, optionally stream it, and enforce output limits.
  6. Return a structured ToolResult.

Normal tools use os/exec.CommandContext, so request cancellation terminates the child process. The execution layer supports shell commands from metadata, scripts with interpreters, direct executables, and background process tracking.

The model-facing agent limit is 32 KiB. Toolsrv also applies a larger process-output safety limit so runaway output cannot exhaust memory before the agent limit is applied.

Sandbox

The sandbox configuration is loaded from the installed sandbox.yaml. It contains general settings, filesystem paths, network policy, environment rules, and advanced Landlock options.

general:
  best_effort: true
filesystem:
  ro: []
  rox: []
  rw: []
network:
  enabled: true
env: []
advanced:
  ldd: false
  add_exec: false

Paths support {CWD}, {HOME}, {TMPDIR}, XDG variables, and Ollie configuration/data variables. The loaded policy is passed to the sandbox wrapper. --yolo skips enforcement for explicit development use.

Sandbox validation rejects dangerous command patterns before execution. The execution service also rate-limits repeated validation failures. The bypass path is not a sandbox configuration override; it is a separately approved execution route.

Cancellation and shutdown

Cancellation has a direct transport path:

sequenceDiagram
    participant A as Agent
    participant C as ToolsrvConn
    participant S as toolsrv
    participant P as Child process

    A->>C: CallTool(ctx)
    C->>S: write proc/new and block on read
    A-->>C: ctx cancelled
    C->>C: close request fid
    C->>S: Tclunk
    S->>S: cancel request context
    S->>P: CommandContext terminates child
    P-->>S: exit status
    S-->>C: cancelled/error result

A server shutdown cancels the run context, closes the listener, kills tracked processes, and removes the Unix socket. Closing a 9P request fid is idempotent with normal process completion.

Remote deployment

Remote toolsrv startup, SSH forwarding, configuration transfer, and recovery are documented in architecture-remote.md.

Implementation map

Area Implementation
CLI and lifecycle cmd/toolsrv/main.go
9P namespace and auth cmd/toolsrv/internal/server/server.go
Process state and handlers cmd/toolsrv/internal/server/proc.go
Tool execution cmd/toolsrv/internal/exec/exec.go
Registry cmd/toolsrv/internal/registry/registry.go
Sandbox configuration cmd/toolsrv/internal/sandbox/config.go
Metadata parsing and variants toolsrv/metadata/meta.go
Metadata discovery toolsrv/metadata/discover.go
Wire types and payload parsing toolsrv/protocol/types.go, payload.go
Agent-side 9P client cmd/olliesrv/internal/toolclient/toolsrv.go