Skip to main content

zlayer_types/api/
internal.rs

1//! Internal API DTOs for scheduler-to-agent communication.
2//!
3//! These types describe the request/response payloads for the internal
4//! endpoints used by the distributed scheduler to trigger operations on
5//! agents. They use a shared secret for authentication rather than JWT
6//! tokens.
7
8use serde::{Deserialize, Serialize};
9use utoipa::ToSchema;
10
11/// Request to scale a service
12#[derive(Debug, Deserialize, ToSchema)]
13pub struct InternalScaleRequest {
14    /// Service name to scale
15    pub service: String,
16    /// Target replica count
17    pub replicas: u32,
18}
19
20/// Response from internal scale operation
21#[derive(Debug, Serialize, ToSchema)]
22pub struct InternalScaleResponse {
23    /// Whether the operation succeeded
24    pub success: bool,
25    /// Service name that was scaled
26    pub service: String,
27    /// New replica count
28    pub replicas: u32,
29    /// Optional message
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub message: Option<String>,
32    /// When set, this agent refused the scale because it cannot run the
33    /// workload's OS (H-7 `RouteToPeer` policy). The value is the OCI-canonical
34    /// OS string the workload requires (`linux` / `windows` / `darwin`). The
35    /// scheduler catches this and re-dispatches to a cluster peer whose
36    /// `NodeState.os` matches.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub reroute_to_os: Option<String>,
39}
40
41/// Request to add a `WireGuard` peer to the local overlay transport.
42///
43/// Sent by the leader to existing nodes when a new node joins the cluster,
44/// so that all nodes learn about the new peer without waiting for periodic
45/// reconciliation.
46#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
47pub struct InternalAddPeerRequest {
48    /// New peer's `WireGuard` public key (base64)
49    pub wg_public_key: String,
50    /// New peer's overlay IP (e.g. "10.200.0.3")
51    pub overlay_ip: String,
52    /// New peer's `WireGuard` endpoint (e.g. "203.0.113.5:51820")
53    pub endpoint: String,
54}
55
56/// Response from internal add-peer operation
57#[derive(Debug, Serialize, ToSchema)]
58pub struct InternalAddPeerResponse {
59    /// Whether the operation succeeded
60    pub success: bool,
61    /// Optional message
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub message: Option<String>,
64}
65
66/// Op type for the secrets Raft state machine.
67///
68/// Replicated through openraft alongside the existing scheduler ops.
69/// `zlayer-consensus` carries the bytes; `zlayer-secrets`'s `raft_sm.rs`
70/// applies them. The variants intentionally mirror the structure of
71/// [`crate::storage::NodeIdentity`], [`crate::storage::WrappedDek`], and
72/// [`crate::storage::ReplicatedSecret`] so the wire shape is identical to
73/// the stored shape.
74#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
75#[serde(tag = "op", rename_all = "snake_case")]
76pub enum SecretsRaftOp {
77    /// Register a new node. Triggers an automatic re-wrap of the current
78    /// DEK so the new node can decrypt secrets going forward.
79    RegisterNode {
80        /// Identity payload (uuid, X25519 pubkey, WG pubkey, `joined_at`).
81        identity: crate::storage::NodeIdentity,
82    },
83
84    /// Soft-revoke a node. Followers stop including it in DEK wraps; the
85    /// next `RotateDek` excludes it permanently.
86    RevokeNode {
87        /// Cluster-wide node UUID being revoked.
88        node_id: String,
89    },
90
91    /// Rotate the cluster DEK. The leader proposes a new generation with
92    /// fresh per-node wraps; followers re-encrypt every `ReplicatedSecret`
93    /// from the previous generation to the new one.
94    RotateDek {
95        /// New wrapped-DEK envelope (generation + per-node wraps).
96        new_wraps: crate::storage::WrappedDek,
97    },
98
99    /// Insert or update a secret. The ciphertext is encrypted under the
100    /// `dek_generation` recorded inside the payload.
101    PutSecret {
102        /// The full replicated secret record.
103        secret: crate::storage::ReplicatedSecret,
104    },
105
106    /// Remove a secret entirely. Hard delete — re-encryption skips it.
107    DeleteSecret {
108        /// `"{scope}:{name}"` storage key, same shape as elsewhere.
109        storage_key: String,
110    },
111}