1use crate::manifest::IdentityMode;
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
9pub struct BackupPlan {
10 pub plan_id: String,
11 pub run_id: String,
12 pub fleet: String,
13 pub network: String,
14 pub root_canister_id: String,
15 pub selected_subtree_root: Option<String>,
16 pub selected_scope_kind: BackupScopeKind,
17 pub include_descendants: bool,
18 pub root_included: bool,
19 pub requires_root_controller: bool,
20 pub snapshot_read_authority: SnapshotReadAuthority,
21 pub quiescence_policy: QuiescencePolicy,
22 pub topology_hash_before_quiesce: String,
23 pub targets: Vec<BackupTarget>,
24 pub phases: Vec<BackupOperation>,
25}
26
27#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
32#[serde(rename_all = "kebab-case")]
33pub enum BackupScopeKind {
34 Member,
35 Subtree,
36 NonRootFleet,
37 MaintenanceRoot,
38}
39
40#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
45pub struct BackupTarget {
46 pub canister_id: String,
47 pub role: Option<String>,
48 pub parent_canister_id: Option<String>,
49 pub depth: u32,
50 pub control_authority: ControlAuthority,
51 pub snapshot_read_authority: SnapshotReadAuthority,
52 pub identity_mode: IdentityMode,
53 pub expected_module_hash: Option<String>,
54}
55
56#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
61#[serde(rename_all = "kebab-case")]
62pub enum AuthorityEvidence {
63 Proven,
64 Declared,
65 Unknown,
66}
67
68#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
73pub struct ControlAuthority {
74 pub source: ControlAuthoritySource,
75 pub evidence: AuthorityEvidence,
76}
77
78impl ControlAuthority {
79 #[must_use]
80 pub const fn unknown() -> Self {
81 Self {
82 source: ControlAuthoritySource::Unknown,
83 evidence: AuthorityEvidence::Unknown,
84 }
85 }
86
87 #[must_use]
88 pub const fn root_controller(evidence: AuthorityEvidence) -> Self {
89 Self {
90 source: ControlAuthoritySource::RootController,
91 evidence,
92 }
93 }
94
95 #[must_use]
96 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
97 Self {
98 source: ControlAuthoritySource::OperatorController,
99 evidence,
100 }
101 }
102
103 #[must_use]
104 pub fn alternate_controller(
105 controller: impl Into<String>,
106 reason: impl Into<String>,
107 evidence: AuthorityEvidence,
108 ) -> Self {
109 Self {
110 source: ControlAuthoritySource::AlternateController {
111 controller: controller.into(),
112 reason: reason.into(),
113 },
114 evidence,
115 }
116 }
117
118 #[must_use]
119 pub fn is_proven(&self) -> bool {
120 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
121 }
122
123 #[must_use]
124 pub fn is_proven_root_controller(&self) -> bool {
125 self.evidence == AuthorityEvidence::Proven
126 && self.source == ControlAuthoritySource::RootController
127 }
128}
129
130#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135#[serde(rename_all = "kebab-case", tag = "kind")]
136pub enum ControlAuthoritySource {
137 Unknown,
138 RootController,
139 OperatorController,
140 AlternateController { controller: String, reason: String },
141}
142
143#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
148pub struct SnapshotReadAuthority {
149 pub source: SnapshotReadAuthoritySource,
150 pub evidence: AuthorityEvidence,
151}
152
153impl SnapshotReadAuthority {
154 #[must_use]
155 pub const fn unknown() -> Self {
156 Self {
157 source: SnapshotReadAuthoritySource::Unknown,
158 evidence: AuthorityEvidence::Unknown,
159 }
160 }
161
162 #[must_use]
163 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
164 Self {
165 source: SnapshotReadAuthoritySource::OperatorController,
166 evidence,
167 }
168 }
169
170 #[must_use]
171 pub const fn snapshot_visibility(evidence: AuthorityEvidence) -> Self {
172 Self {
173 source: SnapshotReadAuthoritySource::SnapshotVisibility,
174 evidence,
175 }
176 }
177
178 #[must_use]
179 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
180 Self {
181 source: SnapshotReadAuthoritySource::RootConfiguredRead,
182 evidence,
183 }
184 }
185
186 #[must_use]
187 pub const fn root_mediated_transfer(evidence: AuthorityEvidence) -> Self {
188 Self {
189 source: SnapshotReadAuthoritySource::RootMediatedTransfer,
190 evidence,
191 }
192 }
193
194 #[must_use]
195 pub fn is_proven(&self) -> bool {
196 self.evidence == AuthorityEvidence::Proven
197 && self.source != SnapshotReadAuthoritySource::Unknown
198 }
199}
200
201#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
206#[serde(rename_all = "kebab-case")]
207pub enum SnapshotReadAuthoritySource {
208 Unknown,
209 OperatorController,
210 SnapshotVisibility,
211 RootConfiguredRead,
212 RootMediatedTransfer,
213}
214
215#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
220#[serde(rename_all = "kebab-case")]
221pub enum QuiescencePolicy {
222 CrashConsistent,
223 RootCoordinated,
224 AppQuiesced,
225}
226
227#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
232pub struct BackupOperation {
233 pub operation_id: String,
234 pub order: u32,
235 pub kind: BackupOperationKind,
236 pub target_canister_id: Option<String>,
237}
238
239#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
244#[serde(rename_all = "kebab-case")]
245pub enum BackupOperationKind {
246 ValidateTopology,
247 ValidateControlAuthority,
248 ValidateSnapshotReadAuthority,
249 ValidateQuiescencePolicy,
250 Stop,
251 CreateSnapshot,
252 Start,
253 DownloadSnapshot,
254 VerifyArtifact,
255 FinalizeManifest,
256}
257
258#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
263pub struct BackupReceipt {
264 pub plan_id: String,
265 pub operation_id: String,
266 pub status: BackupReceiptStatus,
267 pub target_canister_id: Option<String>,
268 pub snapshot_id: Option<String>,
269 pub message: Option<String>,
270}
271
272#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
277#[serde(rename_all = "kebab-case")]
278pub enum BackupReceiptStatus {
279 Completed,
280 Failed,
281 Skipped,
282}
283
284#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
289pub struct BackupExecutionPreflightReceipts {
290 pub plan_id: String,
291 pub preflight_id: String,
292 pub validated_at: String,
293 pub expires_at: String,
294 pub topology: TopologyPreflightReceipt,
295 pub control_authority: Vec<ControlAuthorityReceipt>,
296 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
297 pub quiescence: QuiescencePreflightReceipt,
298}
299
300#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
305pub struct ControlAuthorityReceipt {
306 pub plan_id: String,
307 pub preflight_id: String,
308 pub target_canister_id: String,
309 pub authority: ControlAuthority,
310 pub proof_source: AuthorityProofSource,
311 pub validated_at: String,
312 pub expires_at: String,
313 pub message: Option<String>,
314}
315
316#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
321pub struct SnapshotReadAuthorityReceipt {
322 pub plan_id: String,
323 pub preflight_id: String,
324 pub target_canister_id: String,
325 pub authority: SnapshotReadAuthority,
326 pub proof_source: AuthorityProofSource,
327 pub validated_at: String,
328 pub expires_at: String,
329 pub message: Option<String>,
330}
331
332#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
337#[serde(rename_all = "kebab-case")]
338pub enum AuthorityProofSource {
339 RootCoordination,
340 ManagementStatus,
341 SnapshotReadCheck,
342 Declaration,
343 Unknown,
344}
345
346#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
351pub struct ControlAuthorityPreflightRequest {
352 pub plan_id: String,
353 pub run_id: String,
354 pub fleet: String,
355 pub network: String,
356 pub root_canister_id: String,
357 pub requires_root_controller: bool,
358 pub targets: Vec<ControlAuthorityPreflightTarget>,
359}
360
361#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
366pub struct ControlAuthorityPreflightTarget {
367 pub canister_id: String,
368 pub role: Option<String>,
369 pub parent_canister_id: Option<String>,
370 pub declared_authority: ControlAuthority,
371}
372
373impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
374 fn from(target: &BackupTarget) -> Self {
375 Self {
376 canister_id: target.canister_id.clone(),
377 role: target.role.clone(),
378 parent_canister_id: target.parent_canister_id.clone(),
379 declared_authority: target.control_authority.clone(),
380 }
381 }
382}
383
384#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
389pub struct SnapshotReadAuthorityPreflightRequest {
390 pub plan_id: String,
391 pub run_id: String,
392 pub fleet: String,
393 pub network: String,
394 pub root_canister_id: String,
395 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
396}
397
398#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
403pub struct SnapshotReadAuthorityPreflightTarget {
404 pub canister_id: String,
405 pub role: Option<String>,
406 pub parent_canister_id: Option<String>,
407 pub declared_authority: SnapshotReadAuthority,
408}
409
410impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
411 fn from(target: &BackupTarget) -> Self {
412 Self {
413 canister_id: target.canister_id.clone(),
414 role: target.role.clone(),
415 parent_canister_id: target.parent_canister_id.clone(),
416 declared_authority: target.snapshot_read_authority.clone(),
417 }
418 }
419}
420
421#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
426pub struct TopologyPreflightRequest {
427 pub plan_id: String,
428 pub run_id: String,
429 pub fleet: String,
430 pub network: String,
431 pub root_canister_id: String,
432 pub selected_subtree_root: Option<String>,
433 pub selected_scope_kind: BackupScopeKind,
434 pub topology_hash_before_quiesce: String,
435 pub targets: Vec<TopologyPreflightTarget>,
436}
437
438#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
443pub struct TopologyPreflightTarget {
444 pub canister_id: String,
445 pub parent_canister_id: Option<String>,
446 pub depth: u32,
447}
448
449impl From<&BackupTarget> for TopologyPreflightTarget {
450 fn from(target: &BackupTarget) -> Self {
451 Self {
452 canister_id: target.canister_id.clone(),
453 parent_canister_id: target.parent_canister_id.clone(),
454 depth: target.depth,
455 }
456 }
457}
458
459#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
464pub struct TopologyPreflightReceipt {
465 pub plan_id: String,
466 pub preflight_id: String,
467 pub topology_hash_before_quiesce: String,
468 pub topology_hash_at_preflight: String,
469 pub targets: Vec<TopologyPreflightTarget>,
470 pub validated_at: String,
471 pub expires_at: String,
472 pub message: Option<String>,
473}
474
475#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
480pub struct QuiescencePreflightRequest {
481 pub plan_id: String,
482 pub run_id: String,
483 pub fleet: String,
484 pub network: String,
485 pub root_canister_id: String,
486 pub selected_subtree_root: Option<String>,
487 pub quiescence_policy: QuiescencePolicy,
488 pub targets: Vec<QuiescencePreflightTarget>,
489}
490
491#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
496pub struct QuiescencePreflightTarget {
497 pub canister_id: String,
498 pub role: Option<String>,
499 pub parent_canister_id: Option<String>,
500}
501
502impl From<&BackupTarget> for QuiescencePreflightTarget {
503 fn from(target: &BackupTarget) -> Self {
504 Self {
505 canister_id: target.canister_id.clone(),
506 role: target.role.clone(),
507 parent_canister_id: target.parent_canister_id.clone(),
508 }
509 }
510}
511
512#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
517pub struct QuiescencePreflightReceipt {
518 pub plan_id: String,
519 pub preflight_id: String,
520 pub quiescence_policy: QuiescencePolicy,
521 pub accepted: bool,
522 pub targets: Vec<QuiescencePreflightTarget>,
523 pub validated_at: String,
524 pub expires_at: String,
525 pub message: Option<String>,
526}