microsandbox_protocol/control/types.rs
1//! Shared control records. Their field spellings also preserve legacy JSON.
2
3use serde::{Deserialize, Serialize};
4
5//--------------------------------------------------------------------------------------------------
6// Types
7//--------------------------------------------------------------------------------------------------
8
9/// Empty map payload for state and capability queries.
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
11pub struct Empty {}
12
13/// Facilities available for this runtime and VM configuration.
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
15pub struct Capabilities {
16 /// Runtime supports online root-disk growth over its extended control API.
17 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
18 pub root_disk_grow: bool,
19 /// Live CPU target changes are available.
20 pub cpu_resize: bool,
21 /// Live memory target changes are available.
22 pub memory_resize: bool,
23 /// Host secret changes are available.
24 pub secrets_update: bool,
25}
26
27/// Accepted and observed memory quantities, all in MiB.
28#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
29pub struct MemoryState {
30 /// Boot allocation.
31 pub boot_mib: u64,
32 /// Accepted target, which need not have converged yet.
33 pub target_mib: u64,
34 /// Current guest observation.
35 pub current_mib: u64,
36 /// Boot-time capacity ceiling.
37 pub max_mib: u64,
38}
39
40/// CPU capacity, accepted target, observation, and enforcement.
41#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
42pub struct CpuState {
43 /// CPUs possible for this VM boot.
44 pub possible: u32,
45 /// Accepted online target.
46 pub requested_online: u32,
47 /// Guest-reported online CPUs.
48 pub actual_online: u32,
49 /// Host-enforced online CPUs.
50 pub enforced: u32,
51}
52
53/// Native memory-target payload, without SDK convergence policy.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
55pub struct MemoryTarget {
56 /// Requested total memory in MiB.
57 pub total_mib: u64,
58}
59
60/// Native CPU-target payload.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
62pub struct CpuTarget {
63 /// Requested online CPUs.
64 pub online: u32,
65}
66
67/// Secret material that is redacted in diagnostics and cleared on drop.
68#[derive(Clone, Serialize, Deserialize, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
69#[serde(transparent)]
70pub struct SecretValue(pub String);
71
72/// One ordered host secret modification, preserving the JSON operation tags.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(tag = "change", rename_all = "snake_case")]
75pub enum SecretChange {
76 /// Replace an existing secret's value.
77 Rotate {
78 /// Secret identity.
79 name: String,
80 /// New secret material.
81 value: SecretValue,
82 },
83 /// Remove a secret; absence is a successful no-op.
84 Remove {
85 /// Secret identity.
86 name: String,
87 },
88 /// Replace an existing secret's allowed hosts.
89 SetAllowedHosts {
90 /// Secret identity.
91 name: String,
92 /// Replacement host patterns, in caller order.
93 hosts: Vec<String>,
94 },
95}
96
97/// Sequential, non-transactional secret modifications.
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct SecretsUpdate {
100 /// Apply in order and stop at the first operation failure.
101 pub changes: Vec<SecretChange>,
102}
103
104/// State mutation certainty reported by an operation error.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106#[serde(rename_all = "snake_case")]
107pub enum ErrorEffect {
108 /// This operation (or failed batch entry) did not change state.
109 None,
110 /// A change cannot be ruled out.
111 Unknown,
112}
113
114/// A recoverable peer error. Codes stay strings for future interoperability.
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
116pub struct ControlError {
117 /// Stable machine-readable code; preserve unknown future codes.
118 pub code: String,
119 /// Safe diagnostic text, never a request body or secret value.
120 pub message: String,
121 /// Certainty for this operation, not a retry instruction.
122 pub effect: ErrorEffect,
123}
124
125/// Completion of a sequential secret batch.
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127#[serde(tag = "outcome", rename_all = "snake_case")]
128pub enum SecretsResult {
129 /// Every entry completed, including successful no-ops.
130 Complete {
131 /// Number of completed entries.
132 applied_count: u32,
133 },
134 /// Earlier entries completed; remaining entries were not attempted.
135 Failed {
136 /// Number of completed entries.
137 applied_count: u32,
138 /// Zero-based failed entry, equal to `applied_count`.
139 failed_index: u32,
140 /// Certainty here applies to the failed entry only.
141 error: ControlError,
142 },
143}
144
145/// Legacy JSON request, also used as a checked dispatch representation.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(tag = "op", rename_all = "snake_case")]
148pub enum ControlRequest {
149 /// Query available host operations.
150 Capabilities,
151 /// Set the memory target.
152 MemoryTarget {
153 /// Requested total memory in MiB.
154 total_mib: u64,
155 },
156 /// Observe memory state.
157 MemoryState,
158 /// Set the CPU target.
159 CpuTarget {
160 /// Requested online CPUs.
161 online: u32,
162 },
163 /// Observe CPU state.
164 CpuState,
165 /// Apply ordered secret modifications.
166 SecretsUpdate {
167 /// Caller-ordered changes.
168 changes: Vec<SecretChange>,
169 },
170}
171
172/// Existing JSON response with an additive discovery advertisement.
173///
174/// Omission of `control_protocols` identifies an ordinary legacy response.
175/// Raw JSON consumers must retain the original bytes to preserve unknown fields.
176#[derive(Debug, Clone, Default, Serialize, Deserialize)]
177pub struct JsonControlResponse {
178 /// Whether the operation succeeded.
179 pub ok: bool,
180 /// Legacy diagnostic; batch progress cannot be inferred from it.
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub error: Option<String>,
183 /// Present for memory operations.
184 #[serde(default, skip_serializing_if = "Option::is_none")]
185 pub memory: Option<MemoryState>,
186 /// Present for CPU operations.
187 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub cpu: Option<CpuState>,
189 /// Present for capability discovery.
190 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub capabilities: Option<Capabilities>,
192 /// Explicit operation formats, emitted only during discovery.
193 #[serde(default, skip_serializing_if = "Option::is_none")]
194 pub control_protocols: Option<Vec<String>>,
195}
196
197//--------------------------------------------------------------------------------------------------
198// Methods
199//--------------------------------------------------------------------------------------------------
200
201impl ControlError {
202 /// Construct an error known to precede mutation.
203 pub fn rejected(code: impl Into<String>, message: impl Into<String>) -> Self {
204 Self {
205 code: code.into(),
206 message: message.into(),
207 effect: ErrorEffect::None,
208 }
209 }
210}
211
212//--------------------------------------------------------------------------------------------------
213// Trait Implementations
214//--------------------------------------------------------------------------------------------------
215
216impl std::fmt::Debug for SecretValue {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.write_str("[redacted]")
219 }
220}