通信协议
了解 SIREN 的客户端-服务端通信协议
开发参考
本页面面向维护 SIREN 协议、排查客户端连接和插件通信的开发者,描述的是当前实现中的内部协议,不承诺跨版本兼容。
通信通道
SIREN 客户端与服务端的主通道是 TLS TCP 连接。服务端默认监听 443,TLS 最低版本为 1.2;同一监听端口也承载 WebUI、WebSocket 和 MCP 的 HTTPS 流量。新版客户端通过 ALPN 标识 SIREN 协议,旧版客户端继续通过首个 MsgClientHello 帧识别;HTTP 流量交给 Web 服务处理,不使用本页描述的帧协议。
主通道建立后,客户端必须先发送 MsgClientHello。服务端通过该帧完成客户端注册、恢复备注与项目归属,并缓存当前插件列表及 Recon Profile、artifact format 能力。后续插件发生安装、卸载、更新或启停变化时,客户端通过 MsgPluginRegister 刷新完整插件列表;结构化 reload 后通过 MsgClientCapabilities 刷新 Recon 能力。
交互式 shell 会在主通道之外建立一个共用服务端主端口的独立 TLS Shell 连接;端口转发的数据则继续通过主通道上的 MsgPortForwardData 分片传输。
标准帧
type Header struct {
MsgType uint8
DataLen uint64
}
type Message struct {
Header Header
Data []byte
}标准帧格式如下:
1 byte MsgType | 8 bytes DataLen | DataLen bytes payloadDataLen 是 big-endian uint64。单帧 payload 最大为 1 GiB,超过限制会被视为不可恢复的协议错误并断开连接。DataLen = 0 表示没有 payload。
Payload 约定
不同消息类型复用同一帧头,但 payload 约定不同:
| 类型 | Payload 约定 |
|---|---|
MsgClientHello、MsgClientCapabilities、MsgPluginRegister、MsgPluginInvoke、MsgPluginManage、MsgFSRequest、MsgFSResponse | JSON |
MsgRun | 前 4 字节为 big-endian requestID,后续为命令或结果 |
MsgInfo | CLI 场景可发送文本参数;WebUI/MCP 场景使用带 requestID 的 JSON |
MsgRecon | v2 请求包含协商后的 Snapshot format、客户端采集预算,并支持按 request ID 取消;成功结果包含 Profile、format、`complete |
MsgUpload | 第一帧为文件名,紧随其后的第二帧必须是 MsgFile 文件内容 |
MsgPortForward、MsgPortForwardData | 使用固定长度字段承载连接 ID、端口、目标主机和数据分片 |
MsgShell | JSON {token, cols, rows},客户端通过主端口和 siren-shell/1 ALPN 建立 Shell 连接 |
MsgClean | 请求无 payload;结果为 JSON,包含 can_terminate、removed、scheduled 和可选 error |
MsgReloadConfig | 可选 JSON;新版服务端请求 reload 后刷新能力,旧版空 payload 仍兼容 |
消息类型
| 消息 | 方向 | 用途 |
|---|---|---|
MsgClientHello | Client -> Server | 客户端上线握手,包含稳定 agentId、OS、插件列表、可选 Recon Profile 能力,以及主机名、内网 IP 和阿里云 ECS Instance ID 等主机信息 |
MsgClientCapabilities | Client -> Server | reload 后刷新 Recon 协议版本、默认 Profile、名称列表和 artifact formats |
MsgPluginRegister | Client -> Server | 客户端插件列表变化后刷新完整列表 |
MsgRecon | Server -> Client / Client -> Server | 服务端用 request ID 和 Profile 发起 Recon;客户端返回结构化成功或错误。旧客户端保留文件名响应 |
MsgUpload | Server -> Client / Client -> Server | 服务端请求客户端上传指定文件,客户端返回文件名 |
MsgFile | Client -> Server | recon 或 upload 的文件内容帧 |
MsgRun | Server -> Client / Client -> Server | 执行远程命令并返回输出 |
MsgInfo | Server -> Client / Client -> Server | 查询进程、端口或进程名相关信息 |
MsgShell | Server -> Client | 通知客户端通过主端口建立 Shell 连接 |
MsgPortForward | Server -> Client | 通知客户端为一次转发连接拨号目标服务 |
MsgPortForwardData | 双向 | 传输端口转发的数据分片 |
MsgClean | 双向 | 下发清理请求;客户端回报清理结果后退出 |
MsgReloadConfig | Server -> Client | 重新加载客户端配置 |
MsgPluginManage | Server -> Client | 安装、卸载、更新或切换插件 |
MsgPluginInvoke | Server -> Client | 调用 command 类型插件 |
MsgPluginResponse | Client -> Server | 返回插件执行结果 |
MsgFSRequest | Server -> Client | WebUI 文件浏览、读取、下载或上传请求 |
MsgFSResponse | Client -> Server | WebUI 文件操作响应 |
MsgPortForwardStop 当前仅保留为消息类型定义,现行 stopfwd 流程通过服务端取消转发上下文来关闭监听,不向客户端发送停止帧。
Recon 兼容
| 组合 | 行为 |
|---|---|
| 新客户端 + 新服务端 | 客户端在旧 format 字段中保留 v1 兼容声明,并通过 preferredFormats 公布 [snapshot-jsonl-v2, snapshot-jsonl-v1];服务端优先请求 v2,客户端只传 JSONL,服务端校验后生成 Markdown |
| 新客户端 + 仅理解 v1 format 的 v2 服务端 | 旧服务端忽略 preferredFormats,继续请求 snapshot-jsonl-v1 |
| 只支持 Snapshot v1 的客户端 + 新服务端 | 服务端根据 capability 回退请求 snapshot-jsonl-v1 |
| v1 Profile 客户端 + 新服务端 | 继续按 request ID 传 Markdown |
| Legacy 客户端 + 新服务端 | 只允许省略 Profile 的 Legacy default,沿用空请求和 Markdown 文件帧 |
| 新客户端 + 旧服务端 | 收到空请求后执行默认 Profile,只传 Markdown,不发送 JSONL |
Raw 日志
客户端完成握手后会把日志输出重定向到主连接。服务端读取客户端数据时,如果首字节不在标准消息类型范围内,会把它当作 raw 日志行读取到 \n,打印到终端并记录到 WebUI Recon 日志流,然后继续读取下一帧。
raw 日志单行最大为 64 KiB。超过限制或出现无法重新对齐帧边界的错误时,服务端会关闭当前连接。
Recon 请求与结果 JSON 控制帧最大为 64 KiB;v2 Snapshot 的后续 MsgFile 在读取正文前按 128 MiB 上限检查并流式保存,v1/legacy Markdown 也会在分配正文前应用相同上限。控制帧与 artifact 使用不同边界,异常长度不会触发大块内存分配。
错误边界
- 客户端握手帧必须是
MsgClientHello,且握手 payload 最大为64 KiB。 - 成功的结构化
MsgRecon结果与MsgUpload文件名后面必须紧跟MsgFile。如果第二帧类型不匹配,连接会被关闭,避免错误字节流被继续解释为协议帧;结构化 Recon 错误不发送文件帧。 - 超过单帧大小限制、raw 日志行过长或读取到无法识别的服务端消息时,接收方会把连接视为不可恢复。
- 除
MsgRun、MsgInfo、MsgFSResponse、MsgPluginResponse、MsgPluginRegister等明确有响应消息的流程外,管理类消息通常不单独返回 ACK。
关键流程
客户端上线
recon / upload
recon 会先解析客户端本地 Profile,再按固定顺序执行内置采集和显式引用的 recon 插件。服务端为每个客户端维护单飞 operation;请求 ID 防止迟到响应串入后续操作。服务端等待超时时会立即释放 operation,并向 v2 客户端发送同 request ID 的取消请求;有 request ID 的迟到结果可保存,但不能满足新请求。pre-Profile legacy 文件帧没有 request ID,因此超时会关闭旧会话,由客户端重连后再接受新操作。新服务端连接旧客户端时只允许 legacy default;新客户端收到旧服务端的空请求时执行自己的默认 Profile 并返回旧式文件帧。upload 使用相同的文件边界,把客户端文件保存到服务端 uploads/client-<id>/ 下。
run / info / filesystem
MsgRun 与 WebUI/MCP 使用的结构化 MsgInfo、MsgFSRequest 都带有 requestID,服务端据此把异步响应交还给对应调用方。REPL 发起的 run 使用 requestID = 0,结果直接打印到服务端终端。
shell
Shell 使用主监听端口(默认 TCP 443)上的独立 TLS 连接,不占用主控制通道。一次性 token 通过已认证的控制连接下发,Shell 连接先发送 token,再回报终端启动结果。输入与尺寸变更使用带长度的帧,输出为原始终端字节。主控制连接断开时,关联 Shell 一并关闭。本版不兼容旧 port:cols:rows 格式,服务端与客户端须一起升级。
port forward
一次本地连接对应一个 connectionID。双向数据都被切成 MsgPortForwardData 帧,payload 内带 connectionID,服务端和客户端各自按 ID 分发到对应 TCP 连接。
plugins / lifecycle
recon 类型插件不走 MsgPluginInvoke,而是在 MsgRecon 触发的 Profile 中通过 plugin:<name> 显式引用后执行。