App server
roder app-server starts Roder's local control plane. The app server is
the boundary between the stable Rust runtime and every product surface that wants
to drive it: the reference TUI, desktop apps, IDE integrations, CI harnesses,
local scripts, and remote pairing clients.
The important design rule is that clients talk to the app server, not directly to
roder-core. The server owns runtime construction, method dispatch,
policy enforcement, event-to-notification projection, user config persistence,
background tasks, and remote pairing. A client can therefore start turns, observe
streaming deltas, answer approvals, browse tools, manage memories, or install
plugins without linking the core crate.
roder-protocol stabilizes. Prefer the typed Rust protocol structs when
writing in Rust, and keep JSON clients tolerant of added fields.
Start the server
From a source checkout, run the server through the CLI package. An installed
distribution can use the generated binary name instead of cargo run.
cargo run -p roder-cli -- app-server By default the server speaks newline-delimited JSON-RPC 2.0 over stdio. Each inbound line must be one JSON-RPC request. Each response is one JSON object on stdout. Runtime warnings and remote pairing details are printed on stderr.
echo '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | cargo run -p roder-cli -- app-server Configuration passed through to the runtime
App-server startup accepts the same runtime-oriented options as the interactive
CLI. For example, use --mode to set the policy mode before clients
connect, or use provider/model settings from the normal Roder config file.
cargo run -p roder-cli -- app-server --mode ask cargo run -p roder-cli -- app-server --mode bypass
The server builds a normal Runtime from local configuration and installed
extensions. Provider API keys still come from environment variables or provider
configuration; do not pass secrets in ad-hoc client payloads unless the method is
explicitly a configuration method such as providers/configure.
Remote WebSocket mode
For local-network or paired desktop/mobile clients, start the app server with
--remote. This enables a WebSocket JSON-RPC transport in addition to the
same underlying app-server methods. The default remote listen address is
ws://0.0.0.0:0, which binds an ephemeral port and prints connection details.
cargo run -p roder-cli -- app-server --remote cargo run -p roder-cli -- app-server --remote --listen ws://127.0.0.1:9786 RODER_REMOTE_TOKEN=dev-token cargo run -p roder-cli -- app-server --remote --auth-token env:RODER_REMOTE_TOKEN
Remote mode uses bearer-token authentication. The server accepts the token either
as an Authorization: Bearer ... header or through a WebSocket subprotocol of
the form bearer.<token>. Clients should also offer the
roder.remote.v1 subprotocol; if present, the server echoes it in the
WebSocket handshake.
const socket = new WebSocket(
"ws://127.0.0.1:9786",
["roder.remote.v1", "bearer.dev-token"]
);
When --remote starts, the terminal output includes URLs, a token preview,
and a roder://connect?payload=... deep link. With QR output enabled,
the same deep link is rendered as a terminal QR code for pairing. Disable QR
rendering if a launcher or test harness needs simpler stderr output.
cargo run -p roder-cli -- app-server --remote --print-qr=false ws://0.0.0.0 on untrusted LANs because
app-server methods can read files, run policy-permitted commands, start turns,
and manipulate local Roder state.
JSON-RPC envelope
Requests use the standard JSON-RPC shape. id is optional in the type, but
clients should include it for ordinary request/response calls.
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}
Successful responses contain result. Failed responses contain
error with JSON-RPC-style codes. Common errors are:
-32700— parse error, usually malformed JSON on the transport.-32601— method not found.-32602— invalid parameters or missing params for a method that requires them.-32000— internal/runtime error.-32004— policy denied the requested command or tool action.
Initialize and discover capabilities
Start every client session with initialize. The result reports the active
provider, model, and current working directory. Remote WebSocket clients also get
a remote object with authentication and workspace metadata.
{"jsonrpc":"2.0","id":1,"method":"initialize"} After initialization, discover installed extension and provider state instead of hard-coding assumptions about a distribution.
{"jsonrpc":"2.0","id":2,"method":"extensions/list"}
{"jsonrpc":"2.0","id":3,"method":"providers/list"}
{"jsonrpc":"2.0","id":4,"method":"model/list"}
{"jsonrpc":"2.0","id":5,"method":"tools/list"}
{"jsonrpc":"2.0","id":6,"method":"agents/list"} Thread and turn lifecycle
The desktop-oriented protocol uses thread/* and turn/* methods.
A thread is the long-lived conversation/session shown by clients. A turn is one
model interaction within that thread.
Create or list threads
{
"jsonrpc": "2.0",
"id": 10,
"method": "thread/start",
"params": {
"cwd": "/absolute/path/to/workspace",
"modelProvider": "mock",
"model": "mock",
"ephemeral": false
}
} {"jsonrpc":"2.0","id":11,"method":"thread/list","params":{"limit":20}}
{"jsonrpc":"2.0","id":12,"method":"thread/read","params":{"threadId":"THREAD_ID","includeTurns":true}} Start, steer, or interrupt a turn
Use turn/start to submit a prompt. The protocol also accepts structured
input items for text and attachments; simple clients can provide only
prompt.
{
"jsonrpc": "2.0",
"id": 20,
"method": "turn/start",
"params": {
"threadId": "THREAD_ID",
"prompt": "summarize this repository and list the main crates",
"taskLedgerRequired": false
}
}
Eval and automation clients can set taskLedgerRequired on
turn/start. The runtime then treats the task ledger as part of
finalization and asks the model to checkpoint scoreable outputs before the deadline
reserve closes.
{
"jsonrpc": "2.0",
"id": 21,
"method": "turn/steer",
"params": {
"threadId": "THREAD_ID",
"expectedTurnId": "TURN_ID",
"prompt": "focus on app-server code paths"
}
} {
"jsonrpc": "2.0",
"id": 22,
"method": "turn/interrupt",
"params": { "threadId": "THREAD_ID", "turnId": "TURN_ID" }
} Streaming notifications
The app server projects runtime events into client-facing JSON-RPC notifications.
Stdio clients receive notifications as additional JSON lines on stdout while the
server runs. Remote clients receive responses over the WebSocket transport; local
Rust clients can subscribe through LocalAppClient::subscribe_notifications()
and LocalAppClient::subscribe_events().
Core thread and turn notifications include:
thread/startedandthread/status/changedturn/startedandturn/completeditem/startedanditem/completeditem/agentMessage/deltafor streamed model text and reasoning deltascommand/exec/output_deltafor command output when streaming is requested
{
"jsonrpc": "2.0",
"method": "item/agentMessage/delta",
"params": {
"threadId": "THREAD_ID",
"turnId": "TURN_ID",
"itemId": "TURN_ID-agent-final_answer",
"delta": "Partial text from the model"
}
} Feature-specific notifications are emitted for teams, subagent traces, plan reviews, hunks, workflow imports, media artifacts, and memory updates. Treat notifications as an append-only stream: render what you understand and ignore unknown methods until your client adds support.
Session mode, approvals, and user input
Clients can read and change session policy mode through app-server methods. Policy still lives in the runtime: changing mode affects whether tool calls are allowed, denied, auto-approved, or require approval.
{"jsonrpc":"2.0","id":30,"method":"session/get"}
{"jsonrpc":"2.0","id":31,"method":"session/set_mode","params":{"mode":"ask","reason":"desktop toggle"}} When a turn requires approval or user input, the runtime emits events and the client answers through the server.
{"jsonrpc":"2.0","id":32,"method":"session/resolve_approval","params":{"approvalId":"APPROVAL_ID","approved":true}}
{"jsonrpc":"2.0","id":33,"method":"session/resolve_user_input","params":{"requestId":"REQUEST_ID","answers":{"choice":"yes"}}}
{"jsonrpc":"2.0","id":34,"method":"session/exit_plan","params":{"requestId":"PLAN_REQUEST_ID","approved":true}} Provider and settings methods
Provider methods let a client show available backends, configure a provider, and select the default model for future work. Settings methods expose web-search mode and default policy mode.
{"jsonrpc":"2.0","id":40,"method":"providers/list"}
{"jsonrpc":"2.0","id":41,"method":"providers/select","params":{"provider":"openai","model":"gpt-5.1","reasoning":null}}
{"jsonrpc":"2.0","id":42,"method":"providers/configure","params":{"provider":"openrouter","api_key":"sk-..."}}
{"jsonrpc":"2.0","id":43,"method":"settings/get"}
{"jsonrpc":"2.0","id":44,"method":"settings/set_web_search","params":{"mode":"auto"}}
{"jsonrpc":"2.0","id":45,"method":"settings/set_default_mode","params":{"mode":"ask"}}
If user config persistence is enabled by the CLI, provider selections and settings
are written back to the user's Roder configuration. providers/configure
persists API keys for API-key providers such as OpenRouter when config persistence is
enabled. Clients should make this clear in UI because these choices outlive the
current connection.
Filesystem and command methods
The app server exposes filesystem helpers for UI clients. Paths must be absolute. File contents are returned as base64 so binary files can be transported safely.
{"jsonrpc":"2.0","id":50,"method":"fs/readDirectory","params":{"path":"/absolute/path/to/workspace"}}
{"jsonrpc":"2.0","id":51,"method":"fs/readFile","params":{"path":"/absolute/path/to/file.rs"}} command/exec runs a local process after policy evaluation. It supports a
command vector, optional absolute cwd, environment overrides, timeout, and
output caps. By default commands time out after 30 seconds. In eval-profile turns
with a deadline, command timeouts are clamped to leave a finalization reserve.
{
"jsonrpc": "2.0",
"id": 52,
"method": "command/exec",
"params": {
"command": ["git", "status", "--short"],
"cwd": "/absolute/path/to/workspace",
"timeoutMs": 30000,
"outputBytesCap": 20000
}
}
To receive command output as notifications, set streamStdoutStderr and a
processId. The response then contains empty stdout/stderr and the server
emits command/exec/output_delta notifications with base64 chunks.
Tool-facing exec_command and shell results also expose effective
timeout and timeout status so clients and eval analyzers can distinguish process
timeout, policy denial, provider failure, and ordinary non-zero exit.
Tools, commands, agents, and background tasks
The app server can list available slash commands, expand a command without running it, run a command as a turn, call a registered tool directly, list agents, and manage background task executors.
{"jsonrpc":"2.0","id":60,"method":"commands/list"}
{"jsonrpc":"2.0","id":61,"method":"commands/expand","params":{"name":"review","arguments":"src/main.rs","workspace":"/absolute/path"}}
{"jsonrpc":"2.0","id":62,"method":"commands/run","params":{"threadId":"THREAD_ID","name":"review","arguments":"src/main.rs"}}
{"jsonrpc":"2.0","id":63,"method":"tools/call","params":{"threadId":"THREAD_ID","toolName":"example_tool","arguments":{}}}
{"jsonrpc":"2.0","id":64,"method":"tasks/list"}
Background task methods are tasks/submit, tasks/list,
tasks/get, tasks/cancel, and tasks/subscribe. A task is
submitted to an installed executor by executorId, with optional
threadId, turnId, and workspace context.
Teams and subagents
Team methods expose multi-agent orchestration over the same control plane. Use
team/start to create a team, team/member/start to add members,
team/member/message to send a prompt to a member, and interrupt/focus
methods to manage active work. Split-pane methods currently return an unsupported
error in the app server.
{"jsonrpc":"2.0","id":70,"method":"team/start","params":{"leadThreadId":"THREAD_ID","members":[{"name":"reviewer"}]}}
{"jsonrpc":"2.0","id":71,"method":"team/list","params":{"limit":10}}
{"jsonrpc":"2.0","id":72,"method":"turn/subagentTraces/list","params":{"threadId":"THREAD_ID","turnId":"TURN_ID"}} Review, workflows, media, and memory
Higher-level product features are also controlled through JSON-RPC methods:
- Plan review:
plan/review/read,plan/review/comment,plan/review/rewrite,plan/review/approve, andplan/review/reject. - Hunks:
hunk/list,hunk/read, andhunk/rollback. - Git change review:
git/changes/list,git/changes/read, and observed shell/exec changes throughworkspace/changes/list. - Workflow imports:
workflow/scan,workflow/preview,workflow/enable,workflow/ignore,workflow/refresh, andworkflow/remove. - Dynamic workflows:
workflows/plan,workflows/approve,workflows/list,workflows/get,workflows/pause,workflows/resume,workflows/stop,workflows/restartAgent, and workflow script methods. - Media:
media/list,media/read,media/thumbnail,media/delete, andmedia/attachToTurn. - Memory:
memory/list,memory/read,memory/save,memory/update,memory/delete,memory/query,memory/provider/list,memory/provider/set, andmemory/recall/preview.
{"jsonrpc":"2.0","id":80,"method":"memory/query","params":{"scope":"project","text":"release process","limit":5,"includeGlobal":true}}
{"jsonrpc":"2.0","id":81,"method":"media/list","params":{"threadId":"THREAD_ID"}}
{"jsonrpc":"2.0","id":82,"method":"hunk/list","params":{"threadId":"THREAD_ID","turnId":"TURN_ID"}}
{"jsonrpc":"2.0","id":83,"method":"git/changes/list","params":{"workspace":"/absolute/path/to/workspace"}}
{"jsonrpc":"2.0","id":84,"method":"workflows/list","params":{"limit":10}}
Git change review marks untracked binary files with binary: true and
additions: 0. Reading one through git/changes/read returns a
binary-file patch summary instead of trying to decode bytes as text.
Marketplaces and plugins
Marketplace methods let clients manage external plugin catalogs and installed plugin variants without shelling out. The protocol distinguishes catalog sources, de-duplicated search results, concrete plugin variants, and installed records.
{"jsonrpc":"2.0","id":90,"method":"marketplaces/list"}
{"jsonrpc":"2.0","id":91,"method":"marketplaces/search","params":{"query":"rust"}}
{"jsonrpc":"2.0","id":92,"method":"plugins/list_installed"}
Installation methods include marketplaces/install_default,
marketplaces/add, marketplaces/remove,
marketplaces/refresh, marketplaces/plugin,
plugins/preview_install, plugins/install,
plugins/install_all_variants, plugins/disable, and
plugins/uninstall.
Method reference by area
- Handshake:
initialize. - Discovery:
extensions/list,providers/list,model/list,tools/list,agents/list. - Providers/auth/settings:
providers/configure,providers/select,auth/codex/login,auth/codex/status,auth/codex/logout,auth/supergrok/login,auth/supergrok/status,auth/supergrok/logout,settings/get,settings/set_web_search,settings/set_default_mode. - Threads/turns/session:
thread/start,thread/list,thread/read,turn/start,turn/steer,turn/interrupt,session/get,session/set_mode,session/exit_plan,session/resolve_approval,session/resolve_user_input. - Filesystem/process/tools:
fs/readFile,fs/readDirectory,command/exec,commands/list,commands/expand,commands/run,tools/call. - Runners/tasks:
runners/list,runners/select,runners/session,runners/snapshot,runners/delete,runners/ports,tasks/submit,tasks/list,tasks/get,tasks/cancel,tasks/subscribe. - Teams/subagents:
team/start,team/list,team/read,team/member/start,team/member/message,team/member/interrupt,team/member/focus,team/cleanup,team/pane/focus,team/pane/cleanup,turn/subagentTraces/list,turn/subagentTrace/read. - Review/workflow/state:
plan/review/read,plan/review/comment,plan/review/rewrite,plan/review/approve,plan/review/reject,git/changes/list,git/changes/read,workspace/changes/list,hunk/list,hunk/read,hunk/rollback,workflow/*import methods, andworkflows/*dynamic workflow methods. - Marketplace/media/memory:
marketplaces/list,marketplaces/install_default,marketplaces/add,marketplaces/remove,marketplaces/refresh,marketplaces/search,marketplaces/plugin,plugins/preview_install,plugins/install,plugins/install_all_variants,plugins/list_installed,plugins/disable,plugins/uninstall,media/list,media/read,media/thumbnail,media/delete,media/attachToTurn,memory/list,memory/read,memory/save,memory/update,memory/delete,memory/query,memory/provider/list,memory/provider/set,memory/recall/preview.
Implementing a client
- Start
roder app-serveras a child process or connect to remote WebSocket mode. - Send
initialize, then discover providers, models, tools, commands, and extensions. - Create or select a thread with
thread/start,thread/list, andthread/read. - Submit work with
turn/startorcommands/run. - Render notifications for turn/item deltas, approvals, plan reviews, workspace changes, workflows, memory updates, and task changes.
- Resolve approvals and user input through
session/resolve_approvalandsession/resolve_user_input. - Keep method handling forward-compatible: ignore unknown notification methods and tolerate extra result fields.