RPC 套接字传输
守护进程通过本地 IPC 流暴露一个 JSON-RPC 2.0 接口,在 Unix 上使用 Unix 域套接字,在 Windows 上使用命名管道。这是 zerocode 等本地客户端的主要传输方式。HTTP/WS 网关则保留用于 webhook、Web 仪表板和远程 REST 使用方。
端点解析
每个数据目录都有自己的端点,因此同一台机器上的多个守护进程实例不会发生冲突。数据目录派生自配置目录(--config-dir / ZEROCLAW_CONFIG_DIR,或 ZEROCLAW_DATA_DIR)。
| 操作系统 | 默认端点 |
|---|---|
| Linux | <data_dir>/daemon.sock(Unix 域套接字) |
| macOS | <data_dir>/daemon.sock(Unix 域套接字) |
| Windows | \\.\pipe\zeroclaw-<hash>,其中 <hash> 派生自 data_dir |
在任意平台上均可通过 ZEROCLAW_SOCKET 环境变量进行覆盖:
sh
export ZEROCLAW_SOCKET=/tmp/my-zeroclaw.sock
zeroclaw daemon
PowerShell
$env:ZEROCLAW_SOCKET = '\\.\pipe\my-zeroclaw'
zeroclaw daemon
线路协议
NDJSON(以换行符分隔的 JSON)。每一行都是一条完整的 JSON-RPC 2.0 消息。没有 HTTP 帧,也没有长度前缀。各平台的帧格式完全一致;命名管道传输的字节流与 Unix 套接字相同。
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":1},"id":1}\n
{"jsonrpc":"2.0","result":{"protocolVersion":1,"serverVersion":"0.8.5"},"id":1}\n
握手
第一个 RPC 调用必须是 initialize。在 initialize 成功之前,守护进程会拒绝所有其他方法。协议版本不匹配会产生一个错误码为 -32011 的结构化错误。
{
`jsonrpc`: "2.0",
方法: 初始化,
"参数": {
protocolVersion: 1
},
"id": 1
}
该端点不需要配对令牌。访问控制由操作系统处理:
- Unix:套接字为
0o600,父目录为0o700。 - Windows:命名管道 ACL 默认授予创建用户和
SYSTEM。
方法
| 方法 | 方向 | 描述 |
|---|---|---|
initialize | 客户端 -> 守护进程 | 验证身份并协商协议版本 |
session/new | 客户端 -> 守护进程 | 创建代理会话(需要 agentAlias,可选 cwd、sessionId;可选的 keep_siblings 会禁止空闲的同模式兄弟会话驱逐,适用于自行管理兄弟会话生命周期的多会话客户端) |
session/close | 客户端 -> 守护进程 | 关闭并清理会话 |
session/prompt | 客户端 -> 守护进程 | 运行一个回合(通过 session/update 通知进行流式传输) |
session/cancel | 客户端 -> 守护进程 | 取消正在进行的回合 |
status | 客户端 -> 守护进程 | 服务器版本、协议版本、活动会话列表 |
session/update | 守护进程 -> 客户端 | 回合期间的流式通知(文本块、工具调用、审批) |
elicitation/create | 守护进程 -> 客户端 | 为 ask-user 和 poll 流程请求交互式输入 |
双向请求
任一方都可以通过已建立的套接字发送请求。接收方必须使用相同的 id 进行响应,并且只能包含 result 或 error 二者之一。每个对等方都使用各自的 zc-out-<number> 序列发送出站请求;此前缀不是全局唯一的命名空间。关联仍然具有方向性:每个对等方仅根据自己的待处理请求映射匹配响应,因此相同的文本 ID 可以在相反方向上独立处于进行中状态。
{`jsonrpc`:"2.0",方法:"elicitation/create","参数":{"消息":"是否继续?"},"id":zc-out-0}
{`jsonrpc`:"2.0","结果":{action:"接受","内容":{答案:"yes"}},"id":zc-out-0}
显式的 "result": null 表示响应成功,并且仍会解析挂起的调用方。error 对象会将其解析为失败。如果对端没有响应,发起请求的 ask-user 或 poll 操作仍保持现有的超时行为。
每个帧都必须是带有 "jsonrpc": "2.0" 的 JSON 对象。请求必须包含字符串类型的 method;如果存在,params 必须是对象或数组。响应必须包含字符串、数字或 null 类型的 id,并且必须恰好包含一个响应成员。无效 JSON 会产生 -32700(解析错误)。格式错误的请求形信封会产生 -32600(无效请求);如果能够恢复出有效的请求 ID,则使用该 ID。格式错误的响应形信封会在不记录帧内容的情况下记录日志,并在不回复的情况下丢弃,因为回显其 ID 可能会在相反方向完成一个无关请求。无法判定方向的信封使用 id: null。同样会记录日志并忽略未知的有效响应 ID,而不是回复它们,从而防止响应循环。
开启流式传输
session/prompt 在回合完成时返回最终结果。在执行过程中,守护进程会发送 session/update 通知以传递增量事件:
{`jsonrpc`:"2.0",方法:"session/update","参数":{"会话ID":"...",类型:agent_message_chunk,"文本":"Hello"}}
{`jsonrpc`:"2.0",方法:"session/update","参数":{"会话ID":"...",类型:"tool_call",toolCallId:tc_1,"name":bash,rawInput:{...}}}
{`jsonrpc`:"2.0",方法:"session/update","参数":{"会话ID":"...",类型:"tool_result",toolCallId:tc_1,"name":bash,rawOutput:"..."}}
事件类型:agent_message_chunk、agent_thought_chunk、tool_call、tool_result、approval_request。
临时模式
zeroclaw daemon --ephemeral 会跟踪已连接的客户端,并在最后一个客户端断开连接时自行终止(在 1 秒的宽限期之后)。在宽限期内重新连接将取消关闭操作。在至少有一个客户端连接之前,该守护进程不会退出。
未使用 --ephemeral 启动的守护进程会忽略客户端数量,并持续运行直到被显式停止。
安全
- Unix 套接字目录:
0o700(仅所有者) - Unix socket 文件:
0o600(仅所有者) - Windows 命名管道:默认 ACL 向创建用户和
SYSTEM授予权限 - Linux 上的
SO_PEERCRED提供连接进程的 PID 和 UID 用于审计日志;Windows 则将pipe:local记录为对端标签
快速测试
在一个终端中启动守护进程:
sh
zeroclaw daemon
在 Unix 上的第二个终端中,使用 socat 连接:
sh
socat READLINE UNIX-CONNECT:~/.zeroclaw/data/daemon.sock
请逐行粘贴:
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":1},"id":1}
{"jsonrpc":"2.0","method":"status","params":{},"id":2}
在 Windows 上,使用任意命名管道客户端(PowerShell [System.IO.Pipes.NamedPipeClientStream]、通过 WSL 使用 nc,或直接运行 zerocode)。
内部原理
调度层位于 crates/zeroclaw-runtime/src/rpc/:
| 文件 | 角色 |
|---|---|
transport.rs | RpcTransport trait |
turn.rs | execute_turn() 共享回合执行器 |
session.rs | RpcSession、SessionStore |
dispatch.rs | RpcDispatcher 方法路由 |
local.rs | LocalTransport + 监听器(Unix 套接字 / Windows 命名管道) |
wss.rs | WSS(WebSocket Secure)传输 + TLS 接收器 |
attachments.rs | 文件上传处理、去重、标记生成 |
RpcTransport trait 的设计使得额外的传输方式(vsock、自定义 IPC)能够无缝接入,而无需改动分发或会话逻辑。local.rs 模块使用 tokio::io::split 将 Unix 和 Windows 原语封装在单个 LocalTransport struct 之后,因此读写循环可在两个平台间共享。