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}