Files
pangolin/server/proto/pangolin/agent/v1/agent.proto
T
wangjia 6ec7cd8146 merge: maestro/tsk_V9IrIk2g5Q78 [proto 契约 + buf 工具链] (tsk_FrRarw9YO8Nf)
合并 proto 契约 + buf 工具链任务到 main(已含 apierr + idgen + CONVENTIONS.md):

- server/proto/pangolin/agent/v1/agent.proto: AgentService 6 个 RPC(Enroll/Register/
  Heartbeat/Subscribe/Ack/ReportUsage)及全量消息类型,隐私红线注释保留
- server/buf.yaml: v2 格式,STANDARD lint + FILE breaking 规则
- server/buf.gen.yaml: remote protoc-gen-go v1.36.4 + protoc-gen-go-grpc v1.5.1 插件
- server/internal/pb/: 生成的 agent.pb.go + agent_grpc.pb.go 提交入库
- server/Makefile: 新增 proto / proto-lint / proto-breaking 目标
- server/tools.go: 新增 buf v1.70.0、protoc-gen-go、protoc-gen-go-grpc 工具依赖
- server/go.mod / go.sum: 合并 buf 工具链依赖;github.com/google/uuid 保留为 direct
  依赖(idgen 直接 import);go 版本升至 1.25.10

冲突解决:两侧均修改 go.mod,以 proto 分支(超集)为基础,将 google/uuid 移至
直接依赖 require 块,保留 apierr/idgen 的已有文件不变。

go build ./... ✓  |  go test ./internal/apierr/... ./internal/idgen/... ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:11:11 +08:00

220 lines
9.4 KiB
Protocol Buffer
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// agent.proto — Control-plane ↔ Agent 通信契约
//
// 安全红线(禁止违反):
// 1. 所有消息中只使用 dp_uuid 标识数据通道,严禁出现 user_id / email 等用户身份字段。
// 2. ReportUsage 不含目的地址、DNS 查询等任何可还原用户行为的字段(无日志口径)。
// 3. 本文件冻结后,任何 breaking change 须先通过 buf breaking 审查。
syntax = "proto3";
package pangolin.agent.v1;
import "google/protobuf/timestamp.proto";
option go_package = "github.com/wangjia/pangolin/server/internal/pb/pangolin/agent/v1;agentv1";
// ─── Service ──────────────────────────────────────────────────────────────────
// AgentService 是控制面暴露给 Agent 的唯一 gRPC 接口。
// 连接方向:Agent 主动拨号控制面。
// 除 Enroll 外,所有 RPC 均需通过 mTLS 客户端证书鉴权。
service AgentService {
// Enroll 供新节点申请证书。唯一不要求 mTLS 客户端证书的入口。
// Agent 提交 bootstrap_token(云初始化注入)与 CSR,控制面签发短期证书。
rpc Enroll(EnrollRequest) returns (EnrollResponse);
// Register 是 mTLS 建立后的首次调用,返回该节点的全量配置快照。
// ConfigSnapshot 直接作为响应类型(非 RegisterResponse),是有意设计——快照兼用于
// ApplyConfig 下发,避免重复定义。buf:lint:ignore 声明如下。
// buf:lint:ignore RPC_RESPONSE_STANDARD_NAME
rpc Register(RegisterRequest) returns (ConfigSnapshot);
// Heartbeat 由 Agent 每 30 秒发送一次,上报实时负载并与控制面对账配置版本。
// 若 need_full_resync == trueAgent 应主动重新调用 Register。
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
// Subscribe 建立控制面 → Agent 的服务端流,用于下发 Command。
// Agent 重连时携带 last_command_id,控制面补发缺失命令(at-least-once 语义)。
// Command 直接作为流元素类型(非 SubscribeResponse),是有意设计——下游消费者
// 直接操作 Command,无需额外包装。buf:lint:ignore 声明如下。
// buf:lint:ignore RPC_RESPONSE_STANDARD_NAME
rpc Subscribe(SubscribeRequest) returns (stream Command);
// Ack 用于 Agent 向控制面回执命令的执行结果。
rpc Ack(AckRequest) returns (AckResponse);
// ReportUsage 上报各数据通道的流量用量,供计费/限速使用。
// 红线:items 中只含 dp_uuid 与字节/分钟计数,不含目的地址或 DNS 字段。
rpc ReportUsage(ReportUsageRequest) returns (ReportUsageResponse);
}
// ─── Enroll ───────────────────────────────────────────────────────────────────
message EnrollRequest {
// bootstrap_token 由云初始化(cloud-init)注入,单次有效,控制面验证后即失效。
string bootstrap_token = 1;
// csr_pem 是 Agent 生成的 X.509 证书签名请求(PEM 编码)。
string csr_pem = 2;
}
message EnrollResponse {
// cert_pem 是控制面签发的节点证书(PEM 编码)。
string cert_pem = 1;
// ca_pem 是控制面 CA 证书链(PEM 编码),供 Agent 验证后续 TLS。
string ca_pem = 2;
// not_after 是证书过期时间,Agent 应在到期前主动轮换。
google.protobuf.Timestamp not_after = 3;
}
// ─── Register ─────────────────────────────────────────────────────────────────
message RegisterRequest {
// agent_version 是 Agent 的语义化版本字符串,例如 "1.2.3"。
string agent_version = 1;
// capabilities 描述 Agent 支持的功能集,例如 ["hysteria2", "reality"]。
repeated string capabilities = 2;
}
// ─── Heartbeat ────────────────────────────────────────────────────────────────
message HeartbeatRequest {
// load 是节点实时负载快照。
NodeLoad load = 1;
// config_version 是 Agent 当前持有的配置版本号,用于对账。
uint64 config_version = 2;
}
message HeartbeatResponse {
// need_full_resync 为 true 时,Agent 应重新调用 Register 获取全量配置。
bool need_full_resync = 1;
}
// NodeLoad 节点实时负载,由 Heartbeat 携带。
message NodeLoad {
// online_count 是当前在线连接数(代理隧道数)。
uint32 online_count = 1;
// bandwidth_up 是上行带宽(bytes/s)。
uint64 bandwidth_up = 2;
// bandwidth_down 是下行带宽(bytes/s)。
uint64 bandwidth_down = 3;
// cpu_percent 是 CPU 使用率(0100)。
float cpu_percent = 4;
}
// ─── Subscribe ────────────────────────────────────────────────────────────────
message SubscribeRequest {
// last_command_id 是 Agent 已成功处理的最后一个命令 ID。
// 首次订阅传 0,控制面将从头补发未 Ack 的命令。
uint64 last_command_id = 1;
}
// ─── Command ──────────────────────────────────────────────────────────────────
// Command 是控制面下发给 Agent 的指令,at-least-once 投递语义。
// command_id 单调递增,Agent 根据 last_command_id 实现幂等去重。
message Command {
// command_id 全局单调递增,用于续传与去重。
uint64 command_id = 1;
oneof payload {
UpsertCredential upsert_credential = 2;
RevokeCredential revoke_credential = 3;
RotateCredential rotate_credential = 4;
ApplyConfig apply_config = 5;
Lifecycle lifecycle = 6;
}
}
// UpsertCredential 新增或更新数据通道凭证。
// 红线:只含 dp_uuid,不含 user_id / email。
message UpsertCredential {
// dp_uuid 是数据通道的唯一标识符(Data-Plane UUID)。
string dp_uuid = 1;
// expires_at 是凭证到期时间,到期后 Agent 应拒绝该 dp_uuid 的连接请求。
google.protobuf.Timestamp expires_at = 2;
}
// RevokeCredential 立即吊销数据通道凭证,Agent 应断开对应连接。
// 红线:只含 dp_uuid,不含 user_id / email。
message RevokeCredential {
string dp_uuid = 1;
}
// RotateCredential 原子替换凭证,grace_seconds 内新旧 dp_uuid 均有效。
// 红线:只含 dp_uuid,不含 user_id / email。
message RotateCredential {
string old_dp_uuid = 1;
string new_dp_uuid = 2;
// grace_seconds 是新旧凭证共存的宽限期(秒),到期后 old_dp_uuid 自动失效。
uint32 grace_seconds = 3;
}
// ApplyConfig 将一份完整的 ConfigSnapshot 应用到 Agent。
message ApplyConfig {
ConfigSnapshot config = 1;
}
// Lifecycle 控制节点生命周期。
message Lifecycle {
enum Action {
ACTION_UNSPECIFIED = 0;
// ACTION_DRAIN 停止接受新连接,等待存量连接自然结束。
ACTION_DRAIN = 1;
// ACTION_SHUTDOWN 立即停止所有服务并退出进程。
ACTION_SHUTDOWN = 2;
}
Action action = 1;
}
// ─── ConfigSnapshot ───────────────────────────────────────────────────────────
// ConfigSnapshot 是节点运行所需的全量配置,由 Register 返回或 ApplyConfig 下发。
message ConfigSnapshot {
// config_version 单调递增,Agent 使用此版本号与控制面对账。
uint64 config_version = 1;
// credentials 是当前有效的数据通道凭证列表。
// 红线:只含 dp_uuid,不含 user_id / email。
repeated CredentialEntry credentials = 2;
// reality_sni 是 REALITY 协议使用的 SNI 域名(例如 "www.example.com")。
string reality_sni = 3;
// hy2_port_start / hy2_port_end 是 Hysteria2 监听的 UDP 端口范围。
uint32 hy2_port_start = 4;
uint32 hy2_port_end = 5;
}
// CredentialEntry 单条数据通道凭证。
// 红线:只含 dp_uuid 与有效期,不含 user_id / email。
message CredentialEntry {
string dp_uuid = 1;
google.protobuf.Timestamp expires_at = 2;
}
// ─── Ack ──────────────────────────────────────────────────────────────────────
message AckRequest {
uint64 command_id = 1;
bool ok = 2;
// error_message 仅在 ok == false 时填写,描述失败原因。
string error_message = 3;
}
message AckResponse {}
// ─── ReportUsage ──────────────────────────────────────────────────────────────
message ReportUsageRequest {
repeated UsageItem items = 1;
}
// UsageItem 单个数据通道的用量记录。
// 红线:只含 dp_uuid 与流量计数,不含目的地址、DNS 查询等可还原用户行为的字段。
message UsageItem {
string dp_uuid = 1;
uint64 bytes_up = 2;
uint64 bytes_down = 3;
uint32 minutes = 4;
}
message ReportUsageResponse {}