Skip to content

Latest commit

 

History

History
201 lines (131 loc) · 14.7 KB

File metadata and controls

201 lines (131 loc) · 14.7 KB

Cyber 协议与传输架构

架构概览 · 前置:事件与数据 · 开发:外部接入

本文定义 Cyber 的协议职责。ConnectRPC 承担应用管理与查询;实时 Application 与 Node 数据均使用 AOP Envelope。服务端暴露两个明确 endpoint,但握手后的连接运行机制与 namespace dispatch 保持统一。

1. 唯一真相

跨进程、跨语言和跨前后端的数据类型只在 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,不定义或持久化平行事实类型。

2. 两个平面

平面 传输 职责
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。

3. Namespace 所有权

AOP

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 拥有。

Cyber

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

Cairn 复用 aop.Envelope、AOP namespace 和同一条应用 WebSocket。只有 Cairn 自己拥有的应用语义才进入 Cairn namespace;不得在 Cyber 中创建 Cairn DTO、registry 或转发协议。

4. Envelope 语义

aop.Envelope 是唯一 framing 单元:

  • id:本次 operation 的唯一标识,也是 request/reply correlation key;
  • reply_to:响应或输出所对应的 request id;
  • 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 发送。

5. 连接和并发

每个浏览器应用实例和每个 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 持有。

6. Framing

  • WebSocket:一条 binary message 对应一个 protobuf binary Envelope;
  • stdio:一行 protobuf JSON 对应一个 Envelope。

两种 framing 进入相同的 Runtime protobuf loop。stdio 不是第二套协议,不存在 ServerFrame/AgentFrame 或 JSON DTO。

7. Agent 身份

AgentHello.node_id 是 Web 作用域内唯一的节点 ID。Pool、Session.node_id、PTY 路由和前端选择状态直接使用同一个值。

server-url 只决定节点连接到哪个 Web,不进入节点身份。IOA 使用独立的 ioa-url 和 IOA Node ID,不参与 Web 的 Chat/PTY 路由。

8. 类型与管理服务

  • 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.SystemService
  • cyber.rpc.config.ConfigService
  • cyber.rpc.agent.AgentService
  • cyber.rpc.chat.SessionService
  • cyber.rpc.scan.ScanService
  • cyber.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。

9. 持久化边界

  • Session 和 Scan 以 protobuf 为存储真相;
  • AOP 历史只存 aop.Event ProtoJSON;
  • Artifact archive 只存完整 aop.tool.Artifact event protobuf 与自增 cursor;Go 不存 CSTX node、operation-node projection 或处理状态;
  • 浏览器通过 @cyber/cstx WASM ABI 生成 canonical nodes,并在 IndexedDB 保存 nodes、operation 关联和 archive cursor;
  • CLI -o/--output 通过 telemetry Extension 将 agent、scan、观测和 scanner-native artifact 写入一个新建的 aop.Event ProtoJSONL;
  • -r、/resume 和 -F 只读取事件流,不修改恢复源,也不隐式开启输出;系统不保留 checkpoint/snapshot 文件、Record/Timeline 双写或 replay/fallback 管线。

历史读取是纯查询,不派发 Agent frame、不收敛 operation,也不复制 terminal event。

10. 抽象预算

允许的抽象为 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。

11. 服务端 Go 分层与 client 世界

第 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。

12. 实现位置与验收

  • 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 去重,流式增量不增加重放缓存。