merge: maestro/tsk__R8M4jEw43JR [tsk__R8M4jEw43JR]
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
// Package pangolin.agent.v1 is the frozen control-plane <-> node-agent contract.
|
||||
//
|
||||
// CONNECTION DIRECTION: the node agent ALWAYS dials the control plane (never the
|
||||
// reverse). Every RPC except Enroll requires a verified mTLS client certificate
|
||||
// whose Subject CN equals the node UUID (see server/internal/mtls). Enroll is the
|
||||
// single bootstrap exception, authenticated by a one-time bootstrap token instead.
|
||||
//
|
||||
// DATA-PLANE RED LINE (doc/06 §3, doc/04 §2): messages on this contract carry ONLY
|
||||
// the opaque data-plane credential id (dp_uuid) and aggregate counters. They MUST
|
||||
// NOT carry user_id, email, device id, destination address, SNI or any DNS query.
|
||||
//
|
||||
// NOTE ON CODE GENERATION: this repository has no protoc/buf toolchain wired yet
|
||||
// (Makefile `generate` is a stub). Until task #5's proto pipeline lands, the Go
|
||||
// types and gRPC stubs in server/internal/pb/agentv1 are hand-written to mirror
|
||||
// this file 1:1 and are wire-encoded with a JSON gRPC codec. When protoc is
|
||||
// introduced this file is the source of truth; field numbers are reserved here so
|
||||
// the generated code stays compatible.
|
||||
syntax = "proto3";
|
||||
|
||||
package pangolin.agent.v1;
|
||||
|
||||
option go_package = "github.com/wangjia/pangolin/server/internal/pb/agentv1;agentv1";
|
||||
|
||||
// AgentService is implemented by the control plane and consumed by node agents.
|
||||
service AgentService {
|
||||
// Enroll exchanges a one-time bootstrap token + CSR for a node client
|
||||
// certificate (CN = node_uuid, 90d). Exempt from mTLS — this is the only RPC an
|
||||
// un-enrolled agent may call. Idempotent only within the token TTL.
|
||||
rpc Enroll(EnrollRequest) returns (EnrollResponse);
|
||||
|
||||
// Register is called after (re)connecting over mTLS. It returns the authoritative
|
||||
// full ConfigSnapshot so the agent can converge local state.
|
||||
rpc Register(RegisterRequest) returns (ConfigSnapshot);
|
||||
|
||||
// Heartbeat reports liveness + load every ~30s and the agent's local
|
||||
// config_version. The control plane flags need_full_resync when it detects drift.
|
||||
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
|
||||
|
||||
// Subscribe is a server-streaming feed of Commands. The agent passes the last
|
||||
// command_id it durably processed so the control plane can resume without gaps
|
||||
// (at-least-once; commands are idempotent and Ack'd individually).
|
||||
rpc Subscribe(SubscribeRequest) returns (stream Command);
|
||||
|
||||
// Ack confirms a command was applied. Safe to retry.
|
||||
rpc Ack(AckRequest) returns (AckResponse);
|
||||
|
||||
// ReportUsage uploads per-dp_uuid aggregate byte/minute counters. No identities.
|
||||
rpc ReportUsage(UsageReport) returns (UsageAck);
|
||||
}
|
||||
|
||||
// ─── enroll ──────────────────────────────────────────────────────────────────
|
||||
|
||||
message EnrollRequest {
|
||||
string bootstrap_token = 1; // one-time, injected via cloud-init
|
||||
bytes csr_pem = 2; // PKCS#10 CSR, agent-generated key never leaves node
|
||||
string agent_version = 3;
|
||||
}
|
||||
|
||||
message EnrollResponse {
|
||||
string node_uuid = 1; // authoritative; becomes the cert CN
|
||||
bytes cert_pem = 2; // signed leaf, 90d, EKU=clientAuth
|
||||
bytes ca_pem = 3; // CA to pin for server validation
|
||||
int64 not_after_unix = 4;
|
||||
}
|
||||
|
||||
// ─── register / config ─────────────────────────────────────────────────────────
|
||||
|
||||
message RegisterRequest {
|
||||
string node_uuid = 1;
|
||||
string agent_version = 2;
|
||||
int64 local_config_version = 3; // 0 on a fresh node
|
||||
}
|
||||
|
||||
// Protocol selects which inbound(s) a credential is provisioned on.
|
||||
enum Protocol {
|
||||
PROTOCOL_UNSPECIFIED = 0;
|
||||
PROTOCOL_REALITY = 1; // VLESS+REALITY inbound, user = {uuid, flow}
|
||||
PROTOCOL_HY2 = 2; // Hysteria2 inbound, user = {password}
|
||||
PROTOCOL_BOTH = 3; // both inbounds share the same dp_uuid
|
||||
}
|
||||
|
||||
// Credential is the ONLY per-subscriber object a node ever sees.
|
||||
message Credential {
|
||||
string dp_uuid = 1; // opaque data-plane id; REALITY user uuid
|
||||
Protocol protocol = 2;
|
||||
string flow = 3; // e.g. "xtls-rprx-vision"
|
||||
int64 expires_at_unix = 4; // agent-side TTL; 0 = no expiry
|
||||
}
|
||||
|
||||
// ConfigSnapshot is the full authoritative node state.
|
||||
message ConfigSnapshot {
|
||||
int64 config_version = 1;
|
||||
repeated Credential credentials = 2;
|
||||
|
||||
// Inbound parameters (node-wide, not per-user).
|
||||
RealityInbound reality = 3;
|
||||
Hy2Inbound hy2 = 4;
|
||||
|
||||
// Baseline command id already reflected in this snapshot; the agent resumes
|
||||
// Subscribe from here.
|
||||
int64 last_command_id = 5;
|
||||
}
|
||||
|
||||
message RealityInbound {
|
||||
int32 listen_port = 1;
|
||||
string private_key = 2; // REALITY server private key (base64)
|
||||
string short_id = 3;
|
||||
string server_name = 4; // masquerade SNI handshake target
|
||||
string handshake_server = 5;
|
||||
int32 handshake_port = 6;
|
||||
}
|
||||
|
||||
message Hy2Inbound {
|
||||
int32 listen_port = 1;
|
||||
string masquerade = 2;
|
||||
string cert_path = 3;
|
||||
string key_path = 4;
|
||||
}
|
||||
|
||||
// ─── heartbeat ───────────────────────────────────────────────────────────────
|
||||
|
||||
message HeartbeatRequest {
|
||||
string node_uuid = 1;
|
||||
int64 config_version = 2;
|
||||
int32 online_peers = 3;
|
||||
int64 bandwidth_up_bps = 4;
|
||||
int64 bandwidth_down_bps = 5;
|
||||
double cpu_percent = 6;
|
||||
int64 timestamp_unix = 7;
|
||||
}
|
||||
|
||||
message HeartbeatResponse {
|
||||
bool need_full_resync = 1; // agent must Register + overwrite local state
|
||||
int64 server_time_unix = 2;
|
||||
}
|
||||
|
||||
// ─── command stream ──────────────────────────────────────────────────────────
|
||||
|
||||
enum CommandType {
|
||||
COMMAND_TYPE_UNSPECIFIED = 0;
|
||||
COMMAND_TYPE_UPSERT = 1; // add/update a credential
|
||||
COMMAND_TYPE_REVOKE = 2; // remove a credential
|
||||
COMMAND_TYPE_ROTATE_CREDENTIAL = 3; // new replaces old, old kept until grace
|
||||
COMMAND_TYPE_APPLY_CONFIG = 4; // replace inbound params / full snapshot
|
||||
COMMAND_TYPE_LIFECYCLE = 5; // drain / resume / shutdown
|
||||
}
|
||||
|
||||
enum LifecycleAction {
|
||||
LIFECYCLE_ACTION_UNSPECIFIED = 0;
|
||||
LIFECYCLE_ACTION_DRAIN = 1;
|
||||
LIFECYCLE_ACTION_RESUME = 2;
|
||||
LIFECYCLE_ACTION_SHUTDOWN = 3;
|
||||
}
|
||||
|
||||
message Command {
|
||||
int64 command_id = 1; // monotonically increasing per node
|
||||
CommandType type = 2;
|
||||
|
||||
UpsertPayload upsert = 3;
|
||||
RevokePayload revoke = 4;
|
||||
RotatePayload rotate = 5;
|
||||
ConfigSnapshot apply_config = 6;
|
||||
LifecyclePayload lifecycle = 7;
|
||||
}
|
||||
|
||||
message UpsertPayload {
|
||||
Credential credential = 1;
|
||||
}
|
||||
|
||||
message RevokePayload {
|
||||
string dp_uuid = 1;
|
||||
}
|
||||
|
||||
message RotatePayload {
|
||||
string old_dp_uuid = 1;
|
||||
Credential new_credential = 2;
|
||||
int64 grace_until_unix = 3; // old credential kept active until this time
|
||||
}
|
||||
|
||||
message LifecyclePayload {
|
||||
LifecycleAction action = 1;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
string node_uuid = 1;
|
||||
int64 last_command_id = 2; // resume point; 0 = from the beginning
|
||||
}
|
||||
|
||||
message AckRequest {
|
||||
string node_uuid = 1;
|
||||
int64 command_id = 2;
|
||||
}
|
||||
|
||||
message AckResponse {}
|
||||
|
||||
// ─── usage ───────────────────────────────────────────────────────────────────
|
||||
|
||||
message UsageEntry {
|
||||
string dp_uuid = 1; // never a user id
|
||||
int64 bytes_up = 2;
|
||||
int64 bytes_down = 3;
|
||||
int64 session_minutes = 4;
|
||||
}
|
||||
|
||||
message UsageReport {
|
||||
string node_uuid = 1;
|
||||
int64 window_start_unix = 2;
|
||||
int64 window_end_unix = 3;
|
||||
repeated UsageEntry entries = 4;
|
||||
}
|
||||
|
||||
message UsageAck {}
|
||||
Reference in New Issue
Block a user