feat: pinned agents and probes only accept peers carrying the operator's proof; the panel ships the signed operator chain with the rosters [CI SKIP]

This commit is contained in:
justme committed 2026-10-07 23:24:33 +08:00
1 parent fc2bbdcc6d
commit 9bb6841dce
11 files changed
+129 -12

No files matched your search

@@ -40,7 +40,7 @@ public static class TrustEndpoints
return TypedResults.Json(Mappers.ToDto(state), ApiJsonContext.Default.TrustStateDto);
}
private static async Task<IResult> CommitAsync(HttpContext http, SignedStatementDto signed, TrustService trust, BackendOptions options)
private static async Task<IResult> CommitAsync(HttpContext http, SignedStatementDto signed, TrustService trust, AgentService agents, BackendOptions options)
{
PasskeyContext context = AuthEndpoints.PasskeyContextOf(http, options);
// 第一次(创世)是整个信任根的起点:除了 passkey 签名,还要凭证窗口,避免只拿到一个密码会话的人抢先立根
@@ -55,6 +55,7 @@ public static class TrustEndpoints
return Problems.From(http, error);
}
await agents.AfterAgentSetChangedAsync(http.RequestAborted); // 新的操作员集合要随名册下发给节点
http.SetDetail($"operator set v{state.Version}");
return TypedResults.Json(Mappers.ToDto(state), ApiJsonContext.Default.TrustStateDto, statusCode: StatusCodes.Status201Created);
}
@@ -24,9 +24,11 @@ public sealed partial class AgentService
private readonly NodeInfoService _nodes;
private readonly LinkService _links;
private readonly TrustService _trust;
public AgentService(IAgentStore agents, AgentRuntime runtime, IAgentGateway gateway, TaskPlanner planner, ISettingsStore settings, NodeInfoService nodes, LinkService links)
public AgentService(IAgentStore agents, AgentRuntime runtime, IAgentGateway gateway, TaskPlanner planner, ISettingsStore settings, NodeInfoService nodes, LinkService links, TrustService trust)
{
_trust = trust;
_links = links;
_nodes = nodes;
_agents = agents;
@@ -169,9 +171,13 @@ public sealed partial class AgentService
TcpPort = agent.ProbeTcpPort,
UdpPorts = [.. agent.ProbeUdpPorts],
PublicKey = Convert.ToBase64String(agent.TlsPublicKey!),
Proof = agent.Proof,
});
}
IReadOnlyList<Fleet.Core.SignedStatementDto> chain = await _trust.ChainAsync(ct);
result.TrustChain = chain.Count == 0 ? null : [.. chain];
return result;
}
@@ -21,10 +21,12 @@ public sealed class TaskPlanner : IDisposable
private readonly IProbeStore _probes;
private readonly ISettingsStore _settings;
private readonly LinkService _links;
private readonly TrustService _trust;
private readonly SemaphoreSlim _gate = new(1, 1);
public TaskPlanner(ITaskStore tasks, IAgentStore agents, IProbeStore probes, ISettingsStore settings, LinkService links)
public TaskPlanner(ITaskStore tasks, IAgentStore agents, IProbeStore probes, ISettingsStore settings, LinkService links, TrustService trust)
{
_trust = trust;
_links = links;
_tasks = tasks;
_agents = agents;
@@ -62,9 +64,12 @@ public sealed class TaskPlanner : IDisposable
foreach (ProbeRecord probe in probes.Where(p => p.RevokedAtMs is null && p.PeerPublicKey is { Length: > 0 } && links.IsLinked(p.Id, agentId.Value)))
{
document.Peers.Add(new PeerSpec { ProbeId = new ProbeId(probe.Id), PublicKey = Convert.ToBase64String(probe.PeerPublicKey!) });
document.Peers.Add(new PeerSpec { ProbeId = new ProbeId(probe.Id), PublicKey = Convert.ToBase64String(probe.PeerPublicKey!), Proof = probe.Proof });
}
IReadOnlyList<Fleet.Core.SignedStatementDto> chain = await _trust.ChainAsync(ct);
document.TrustChain = chain.Count == 0 ? null : [.. chain];
string hash = Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(document, MoShiroJsonContext.Default.TaskSet)));
await _gate.WaitAsync(ct);