using System.Text.Json; using Fleet.Core; using Fleet.Rpc; namespace Fleet.Server; public sealed record EnrollmentContext(string? RemoteIp, JsonElement? ProductMeta); /// /// 服务器的身份密钥:操作员用 passkey 把它的公钥签进操作员集合。有它的时候,认证挑战会带上服务器的签名,节点据此确认对面是真的服务器, /// 并在这条链路上用应用层加密。没有注册就是旧的行为(没有服务器身份)。 /// public interface IServerIdentity { byte[] PublicKeySpki { get; } /// 对一段内容签名(Fleet.Identity.ServerHello.Payload 给出的)。 byte[] Sign(ReadOnlySpan payload); } /// 操作员集合的链(passkey 签的,创世在前)。没有初始化信任根时是空的。节点注册时用它确认「这个面板确实是我的操作员的」。 /// 节点握手时自述的二进制(旧节点没有)。 public sealed record BinaryEvidence(string? Sha256, string? Manifest, string? Signature); /// 是否让这个节点继续:不放行时 Reason 会原样告诉节点(这是它自己的信息,不是秘密)。 public sealed record BinaryDecision(bool Accept, string Reason = ""); /// /// 服务端对节点二进制的把关:节点第一次握手成功的那一刻绑定它的二进制哈希,之后哈希对不上、又不在 CI 签名的更新清单里,就不让它继续运行。 /// 没有注册实现时不做任何检查。 /// public interface IBinaryAttestor { ValueTask CheckAsync(NodeRecord node, BinaryEvidence evidence, CancellationToken ct); } public interface IOperatorTrustSource { ValueTask> ChainAsync(CancellationToken ct); } /// 产品挂钩:节点注册成功之后调用。 public interface IEnrollmentHook { ValueTask OnEnrolledAsync(NodeRecord node, EnrollmentContext context, CancellationToken ct); } public enum NodeEventKind { Online, Offline, Hello, Heartbeat, } public sealed record NodeEvent(NodeEventKind Kind, NodeRecord Node, long AtMs, HelloParams? Hello = null, JsonElement? Extra = null); public interface INodeEventSink { ValueTask OnEventAsync(NodeEvent nodeEvent, CancellationToken ct); } /// 期望状态提供者:为每个节点计算期望状态的完整快照(天然幂等)。 public interface IStateProvider { /// 状态文档类型,例如 task_set。 string Type { get; } ValueTask GetAsync(NodeRecord node, CancellationToken ct); /// 节点应用之后回调,rejected[] 带结构化原因。 ValueTask OnAppliedAsync(NodeRecord node, long version, StateSyncResult result, CancellationToken ct); } public sealed record StateDocument(long Version, JsonElement Doc); public enum InboxOutcome { Stored, Duplicate, Rejected, } /// 收件箱:持久化之后才返回 Stored,框架随后才回复 ACK。 public interface IInbox { byte Channel { get; } ValueTask HandleAsync(NodeRecord node, Guid msgId, ReadOnlyMemory payload, CancellationToken ct); } /// 通知 Fleet:某个节点的期望状态变了,需要重新计算并推送。 public interface IStateNotifier { void NotifyChanged(NodeId node); void NotifyAllChanged(); }