SIREN

通信协议

了解 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 分片传输。

标准帧

internal/protocol/protocol.go
type Header struct {
	MsgType uint8
	DataLen uint64
}

type Message struct {
	Header Header
	Data   []byte
}

标准帧格式如下:

1 byte MsgType | 8 bytes DataLen | DataLen bytes payload

DataLen 是 big-endian uint64。单帧 payload 最大为 1 GiB,超过限制会被视为不可恢复的协议错误并断开连接。DataLen = 0 表示没有 payload。

Payload 约定

不同消息类型复用同一帧头,但 payload 约定不同:

类型Payload 约定
MsgClientHelloMsgClientCapabilitiesMsgPluginRegisterMsgPluginInvokeMsgPluginManageMsgFSRequestMsgFSResponseJSON
MsgRun前 4 字节为 big-endian requestID,后续为命令或结果
MsgInfoCLI 场景可发送文本参数;WebUI/MCP 场景使用带 requestID 的 JSON
MsgReconv2 请求包含协商后的 Snapshot format、客户端采集预算,并支持按 request ID 取消;成功结果包含 Profile、format、`complete
MsgUpload第一帧为文件名,紧随其后的第二帧必须是 MsgFile 文件内容
MsgPortForwardMsgPortForwardData使用固定长度字段承载连接 ID、端口、目标主机和数据分片
MsgShellJSON {token, cols, rows},客户端通过主端口和 siren-shell/1 ALPN 建立 Shell 连接
MsgClean请求无 payload;结果为 JSON,包含 can_terminateremovedscheduled 和可选 error
MsgReloadConfig可选 JSON;新版服务端请求 reload 后刷新能力,旧版空 payload 仍兼容

消息类型

消息方向用途
MsgClientHelloClient -> Server客户端上线握手,包含稳定 agentId、OS、插件列表、可选 Recon Profile 能力,以及主机名、内网 IP 和阿里云 ECS Instance ID 等主机信息
MsgClientCapabilitiesClient -> Serverreload 后刷新 Recon 协议版本、默认 Profile、名称列表和 artifact formats
MsgPluginRegisterClient -> Server客户端插件列表变化后刷新完整列表
MsgReconServer -> Client / Client -> Server服务端用 request ID 和 Profile 发起 Recon;客户端返回结构化成功或错误。旧客户端保留文件名响应
MsgUploadServer -> Client / Client -> Server服务端请求客户端上传指定文件,客户端返回文件名
MsgFileClient -> Serverreconupload 的文件内容帧
MsgRunServer -> Client / Client -> Server执行远程命令并返回输出
MsgInfoServer -> Client / Client -> Server查询进程、端口或进程名相关信息
MsgShellServer -> Client通知客户端通过主端口建立 Shell 连接
MsgPortForwardServer -> Client通知客户端为一次转发连接拨号目标服务
MsgPortForwardData双向传输端口转发的数据分片
MsgClean双向下发清理请求;客户端回报清理结果后退出
MsgReloadConfigServer -> Client重新加载客户端配置
MsgPluginManageServer -> Client安装、卸载、更新或切换插件
MsgPluginInvokeServer -> Client调用 command 类型插件
MsgPluginResponseClient -> Server返回插件执行结果
MsgFSRequestServer -> ClientWebUI 文件浏览、读取、下载或上传请求
MsgFSResponseClient -> ServerWebUI 文件操作响应

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 日志行过长或读取到无法识别的服务端消息时,接收方会把连接视为不可恢复。
  • MsgRunMsgInfoMsgFSResponseMsgPluginResponseMsgPluginRegister 等明确有响应消息的流程外,管理类消息通常不单独返回 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 使用的结构化 MsgInfoMsgFSRequest 都带有 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> 显式引用后执行。

On this page