nixfleet_agent/runtime/wire.rs
1//! Wire types for the agent's HTTP traffic against CP.
2//!
3//! These mirror the request / response shapes defined in
4//! `nixfleet_control_plane::server::routes::{dispatch,heartbeat,events}`.
5//! Hand-mirrored here because they live inside the CP crate (not in
6//! `nixfleet-proto`); a future cleanup could move them into
7//! `nixfleet-proto::agent_wire` so the two sides share one type.
8
9use chrono::{DateTime, Utc};
10use nixfleet_proto::RolloutId;
11use serde::{Deserialize, Serialize};
12
13/// Body of `GET /v1/agent/dispatch?wait=60` when CP has a queued
14/// dispatch for this agent. Empty body / 204 means no work pending.
15///
16/// `rollout_id` is the canonical `"{channel}@{channel_ref}"` composite
17/// (RFC-0008 §6.3); serde-transparent so the wire JSON shape is
18/// indistinguishable from a plain `String`.
19#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
20pub struct DispatchResponse {
21 pub hostname: String,
22 pub rollout_id: RolloutId,
23 pub target_closure: String,
24 pub soak_due_at: DateTime<Utc>,
25 pub enqueued_at: DateTime<Utc>,
26}
27
28/// Body of `POST /v1/agent/heartbeat`.
29#[derive(Debug, Clone, Deserialize, Serialize)]
30pub struct HeartbeatRequest {
31 pub hostname: String,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub rollout_id: Option<RolloutId>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub current_closure: Option<String>,
36 pub at: DateTime<Utc>,
37}
38
39#[derive(Debug, Clone, Deserialize, Serialize)]
40pub struct HeartbeatResponse {
41 #[serde(default)]
42 pub received_at: Option<DateTime<Utc>>,
43 /// LIFT #3: per active rollout on this host, a HostRolloutSnapshot
44 /// the agent should apply to its in-memory reducer (RFC-0005 §9.5
45 /// scenario 3, agent-side half). Populated by CP only when the
46 /// heartbeat carried `rollout_id = None` AND CP holds non-terminal
47 /// records for the host. Empty in steady-state.
48 #[serde(default, skip_serializing_if = "Vec::is_empty")]
49 pub bootstrap_rollouts: Vec<nixfleet_proto::agent_wire::HostRolloutSnapshot>,
50}
51
52/// Reset signal sent by the applier to the probe worker. Cleared on
53/// `Effect::LocalResetProbeCache`. Per-rollout scope.
54#[derive(Debug, Clone)]
55pub struct ProbeResetCommand {
56 pub rollout_id: RolloutId,
57}
58
59/// Intent signal sent by the applier to the activation worker. Triggers
60/// real systemd-run dispatch. (Stub in 7c — activation worker just emits
61/// the corresponding Started/Completed/Failed events directly through
62/// the input MPSC.)
63#[derive(Debug, Clone)]
64pub struct ActivationIntent {
65 pub rollout_id: RolloutId,
66 pub target_closure: String,
67 pub rollback: bool,
68}