本文定义 Cyber 的协议职责。ConnectRPC 承担应用管理与查询;实时 Application 与 Node 数据均使用 AOP Envelope。服务端暴露两个明确 endpoint,但握手后的连接运行机制与 namespace dispatch 保持统一。
跨进程、跨语言和跨前后端的数据类型只在 protobuf 中定义。业务代码可以拥有领域对象或 UI view model,但不得再定义与 protobuf 同构的 wire DTO,也不得在 AOP、Connect、REST 或 JSON-RPC 之间做同一语义的多次转换。
CSTX 独占安全事实模型:IP、Port、URL/Web、App、Framework、Vulnerability 等节点由浏览器中的 @cyber/cstx WASM ABI 定义和生成。Go 只归档原始 aop.tool.Artifact event,不定义或持久化平行事实类型。
| 平面 | 传输 | 职责 |
|---|---|---|
| AOP 应用平面 | Application WS /api/aop/application/ws、Node WS /api/aop/node/ws、Application AOPService.Connect |
Agent 会话、Turn、事件、工具、原始 Artifact、命令、file、PTY、取消和实时 scan 事件 |
| Cyber 管理平面 | ConnectRPC unary | 查询、配置、Agent 列表与本地进程生命周期、Session 历史、Scan CRUD、原始 Artifact 归档同步、系统状态 |
目标态不存在 JSON-RPC、AOP ChatService、独立 Agent socket、独立 terminal socket 或额外的 WebSocket wire。AOP 只定义一个 Connect(stream Envelope) 双向流;Connect/gRPC 与浏览器 WebSocket 适配到同一个 EnvelopeStream 服务核心。当前 Agent 默认仍使用 WebSocket,新增 gRPC 服务端不改变旧 Agent 或浏览器连接。管理 RPC 与 AOP 流由同一个 Connect handler 注册,但职责仍按 service 分离。
该边界按“语义”而不是按“调用者”划分:Runner 只通过 WebSocket 接入 Web;浏览器的管理/历史查询走 ConnectRPC,但浏览器的实时 Session/Turn、命令、文件与 PTY 也走 WebSocket。Web 服务拥有 Agent Pool、调度、持久化和管理 RPC,节点只拥有自身 Runtime、工具与执行状态。
Agent 对外只使用 --server-url 作为 Cyber Web/AOP 基址。IOA 使用独立的 --ioa-url;Web 默认托管同源 IOA,因此 Web Agent 未指定 --ioa-url 时自动使用 <server-url>/ioa。
cyber-ui/packages/aop/proto/aop 定义跨应用的语义:
aop.ProtocolMessage:Agent 注册与 Session/Turn 生命周期;aop.Event:message、tool、usage、status、error 和生命周期事件;aop.file、aop.exec、aop.pty、aop.tool:通用扩展协议。
Cyber 不注册或宣告 aop.exec;远程命令统一通过 aop.tool 的 tool.call 调用 bash 工具。上游 AOP 仍保留 exec 的通用协议定义。Web、Agent Node 和 Tool Node 共用 pkg/aopconn.Connection 的收发队列;NamespaceMux 只负责分发,工具调用的取消与清理由现有 pkg/node/tool 处理。
这些扩展不是 Cyber DTO。PTY 和 file 对任何 AOP Agent 都成立,因此由 AOP 拥有。
proto/types 与 proto/rpc 只定义 Cyber 应用机制:
cyber.command:Cyber 命令目录、请求、结果与 receipt;cyber.scan:Scan 状态、快照和实时事件;cyber.reload:Cyber 配置热重载;cyber.agent/config/chat/artifact/system:管理数据类型;对应服务位于cyber.rpc.*。
Cyber 专有元数据通过 google.protobuf.Any 携带 namespace-owned message;protobuf full name / Any.type_url 是唯一类型身份,不得再增加 namespace 字符串或把 protobuf 编码成 JSON bytes。
Cairn 复用 aop.Envelope、AOP namespace 和同一条应用 WebSocket。只有 Cairn 自己拥有的应用语义才进入 Cairn namespace;不得在 Cyber 中创建 Cairn DTO、registry 或转发协议。
aop.Envelope 是唯一 framing 单元:
id:本次 operation 的唯一标识,也是 request/reply correlation key;reply_to:响应或输出所对应的 requestid;payload:google.protobuf.Any,type URL 决定 protobuf namespace;delivery_cursor:持久化订阅的位置,只用于恢复,不等同于Event.seq。
请求 message 内不再重复 request_id。同步响应、流式输出和取消都围绕同一个 Envelope ID:
request.id = op-1
reply.reply_to = op-1
stream item.reply_to = op-1, delivery_cursor = 42
CancelOperation.target_id = op-1
Event.seq 是 Session 内的事件语义顺序;delivery_cursor 是存储/投递位置。两者不能互换。
WebSocket 本身提供连续字节传输,但不提供业务 correlation、可恢复 cursor 或精确取消,因此 Envelope 仍然必要;WatchEventsResponse 之类再包装则没有必要,事件直接作为 reply stream item 发送。
每个浏览器应用实例和每个 Runner 各自使用一条 AOP 应用流。浏览器使用 Application WebSocket;原生 Application 客户端可以使用 AOPService.Connect 的 Connect/gRPC 双向流;Runner 使用 Node WebSocket。连接只有一个 reader;所有输出通过一个 FIFO writer。协议不引入优先级队列。
浏览器最终唯一连接所有者是 @cyber/aop 的 AOPClient。Terminal、Chat、Command、File 和 Scan watcher 将只提交 protobuf message,不创建 socket。该浏览器 cutover 当前延期,现有 Chat/WatchEvents ConnectRPC 与 Terminal WebSocket 暂时保留。
Application 与 Node 使用不同 endpoint,不再根据首帧猜测角色。Node 首帧必须是 AgentHello;Application 收到 AgentHello 返回 WRONG_ENDPOINT。endpoint 初始化完成后,两者都进入 pkg/web.Connection → NamespaceMux。顶层 namespace 消息(名称以 ProtocolMessage 结尾)通过实例级 NamespaceMux 注册;namespace 内部 oneof 继续使用显式 type switch。
Go 传输边界只有:
type EnvelopeStream interface {
Recv() (*Envelope, error)
Send(*Envelope) error
}Context 由调用者显式传入,Stream 不拥有 Session、Turn 或 operation 状态。
pkg/web.Connection 是机制层抽象,不是 AOP wire contract。它只拥有单条 EnvelopeStream 的 reader、FIFO writer、context 与错误收敛;不拥有 Agent、Session、Turn、pending operation、subscription 或 Hub fanout。Application 与 Node 业务分别由 pkg/web/api 和 AgentPool 持有。
- WebSocket:一条 binary message 对应一个 protobuf binary
Envelope; - stdio:一行 protobuf JSON 对应一个
Envelope。
两种 framing 进入相同的 Runtime protobuf loop。stdio 不是第二套协议,不存在 ServerFrame/AgentFrame 或 JSON DTO。
AgentHello.node_id 是 Web 作用域内唯一的节点 ID。Pool、Session.node_id、PTY 路由和前端选择状态直接使用同一个值。
server-url 只决定节点连接到哪个 Web,不进入节点身份。IOA 使用独立的 ioa-url 和 IOA Node ID,不参与 Web 的 Chat/PTY 路由。
aop/:AOP core 与官方aop.*生成类型;core/types/:Agent、Runner、TUI、Web 共用的 Cyber protobuf message 与 typed extension helper,单一 Go 包且不依赖 Connect;pkg/rpc/:Cyber ConnectRPC service descriptor、client 和 handler,.pb.go与.connect.go位于同一 Go 包;pkg/web/api/:协议无关的管理 API;直接接收/返回 protobuf message,不依赖 Connect、HTTP 或 WebSocket;pkg/web/connect.go:唯一生成 RPC 暴露适配器,注册管理服务与AOPService,并映射认证和传输错误;pkg/web/其余代码:AOP WebSocket、AgentPool、Runner 委派、Hub 与持久化基础设施;cmd/gen/:唯一 protobuf/TypeScript 生成入口。
非 full 构建不得依赖 pkg/rpc 或 connectrpc.com/connect。Runner transport 已随节点端剥离收敛到 pkg/node,不依赖 pkg/web。
Web 管理面暴露以下 unary 服务:
cyber.rpc.system.SystemServicecyber.rpc.config.ConfigServicecyber.rpc.agent.AgentServicecyber.rpc.chat.SessionServicecyber.rpc.scan.ScanServicecyber.rpc.artifact.ArtifactService
AOP 应用面只额外暴露一个双向流服务:
cyber.rpc.aop.AOPService/Connect
生成流程只生成 protobuf 与 Connect-Go 代码,不生成 grpc-go service/client。Go 插件由 go.mod 的 tool 指令固定,统一入口为 go run ./cmd/gen(或 make proto-gen);CI 会重新生成并要求零 diff。Connect-Go 的同一 handler 原生支持 Connect、gRPC 与 gRPC-Web;浏览器因双向流限制使用薄 WebSocket 适配,不存在第二套业务实现。REST /api/* 仅保留认证、Application WS 与 Node WS;旧 /api/aop/ws 和未知管理 REST 返回 404。/health 和原生 /ioa/ 不属于 Cyber RPC。
- Session 和 Scan 以 protobuf 为存储真相;
- AOP 历史只存
aop.EventProtoJSON; - Artifact archive 只存完整
aop.tool.Artifactevent protobuf 与自增 cursor;Go 不存 CSTX node、operation-node projection 或处理状态; - 浏览器通过
@cyber/cstxWASM ABI 生成 canonical nodes,并在 IndexedDB 保存 nodes、operation 关联和 archive cursor; - CLI
-o/--output通过telemetryExtension 将 agent、scan、观测和 scanner-native artifact 写入一个新建的aop.EventProtoJSONL; -r、/resume和-F只读取事件流,不修改恢复源,也不隐式开启输出;系统不保留 checkpoint/snapshot 文件、Record/Timeline 双写或 replay/fallback 管线。
历史读取是纯查询,不派发 Agent frame、不收敛 operation,也不复制 terminal event。
允许的抽象为 AOP EnvelopeStream、实例级 NamespaceMux、web 机制层 Connection 和浏览器 AOPClient。其余逻辑使用具体 owner;顶层 namespace 由 Mux 注册,namespace 内部 oneof 使用显式 switch:
- Session/Turn 状态属于 Runtime/Service;
- Agent pending task 属于具体
remoteAgent; - Application subscription 与 PTY route 属于该 Application connection 的业务 dispatcher;
pkg/web/api拥有 Web 原生管理语义;Agent/Session 执行通过能力接口委派给既有 AOP/AgentPool/Runner,不复制执行逻辑。- Connect handler 只做 request wrapper、服务注册与错误映射。
namespace 贡献只有一个类型 aop.Binding(Prototype + Open):Open 在每个连接建立时给出该连接的 handler,共享一个 handler 的贡献用 aop.Shared 构造。不再有第二个 binding 类型,core/namespaces.Registry 因此只有一个 Point 和一个 namespace 序。
新增抽象必须证明至少有两个真实 owner、不能由 protobuf message + 普通函数表达,并在本文补充职责和生命周期。允许的 namespace 注册抽象只做 full-name → handler 路由;不得扩展成全局 schema registry、通用 pending manager、link、wire 或兼容 adapter。
第 2 节的线协议边界在 Go 代码上投影为三个服务端层和一个 client 世界。分层的判断标准是语义归属,不是文件大小或调用频次。
- rpc(定义投影层,
proto/rpc、pkg/rpc):protobuf service contract、生成的 Go message/client/handler 接口,不实现业务语义。 - api(业务层,
pkg/web/api):实现控制面(Sessions/Scans/Config/Artifacts/Agents/Status)与 Application envelope 业务路由(OpenSession/RunTurn/Watch/Command/File/PTY)。本层不得 import net/http、WebSocket、Connect 或 SQLite;机制通过 Store/Runtime/CommandExecutor/FileUploader/PTYRouter 和最小 ApplicationConnection 接口注入。 - web(机制与传输层,
pkg/web):拥有 WS upgrade、EnvelopeStream adapter、Connection、认证、持久化、AgentPool、Hub 与装配。两个 endpoint 只做各自首帧初始化;Application 移交 api,Node 移交 AgentPool,之后复用 Connection。 - core(领域层,
core/、agent/、aop/):web 之前已存在的领域能力,不感知管理端。 - client 世界:SPA、CLI、node 平级,都是 api 的消费者。node(
pkg/node,原pkg/web/agent)是 cyber 的节点端 client:只依赖 aop 协议与 runner,不得依赖pkg/web。
session 只有一个概念、三种视图:协议视图 aop.Session(core)、定义视图 api.Sessions、机制视图 Service runtime + store。其他同名概念(如 auth cookie session)必须改名,不得共享 "session" 命名。
当前收敛:节点端库剥离为 pkg/node;transport adapter 位于 pkg/aopws(WebSocket ↔ aop.EnvelopeStream);Application 业务路由位于 pkg/web/service/application.go(ServeApplication);Node 连接由 pkg/web/service/agents_stream.go 的 AgentPool.ServeNode 拥有;两个 endpoint 由 pkg/web/service/endpoints.go 装配,并通过 pkg/web.Connection 复用单 reader/FIFO writer/error convergence;Connect 服务直接投影到 Application Endpoint。
- AOP schema:
web/frontend/cyber-ui/packages/aop/proto/aop - Cyber message schema:
proto/types - Cyber RPC schema:
proto/rpc - Cyber Go message:
core/types - Cyber Go RPC:
pkg/rpc - 生成入口:
cmd/gen - AOP endpoint 装配:
pkg/web/service/endpoints.go - 统一连接机制:
pkg/web/connection.go - EnvelopeStream transport 适配:
pkg/aopws/stream.go - Application 业务语义(envelope 路由):
pkg/web/service/application.go - Agent 节点连接(AgentPool 拥有):
pkg/web/service/agents_stream.go - Agent Runtime session protocol:
agent/session/protocol.go - stdio framing:
pkg/host/stdio.go;入口组合:cmd/aiscan/stdio.go - Browser client:
web/frontend/cyber-ui/packages/aop/src/client.ts - Connect boundary:
pkg/web/connect.go
完成态验收:全仓只能由 AOPClient 创建浏览器 WebSocket;不存在 ChatService、WatchEventsResponse、WatchScanEventsResponse、AgentTransport frame、terminal 专用 socket、手写 wire DTO 或 grpc-go service 生成物。
配置先构建并加载完整候选 Profile。加载失败保留旧 Profile;成功后停止旧任务准入、取消连接与执行、等待清理并关闭旧 Profile,再切换到候选。不迁移旧 Session、REPL 或任务。内容相同的配置不重建运行时。管理配置请求不计入被取消的业务工作,避免等待自身退出。
Web 接收事件时复制输入、保留事件 ID,并按自己的时间线分配序号。分配序号、持久化与广播串行执行;持久化失败不提交终止标记。持久事件复用数据库按事件 ID 去重,流式增量不增加重放缓存。