1use crate::manifest::IdentityMode;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19#[serde(deny_unknown_fields)]
20pub struct BackupPlan {
21 pub plan_version: u16,
22 pub plan_id: String,
23 pub run_id: String,
24 pub fleet: String,
25 pub environment: String,
26 pub root_canister_id: String,
27 pub selected_subtree_root: Option<String>,
28 pub selected_scope_kind: BackupScopeKind,
29 pub include_descendants: bool,
30 pub root_included: bool,
31 pub requires_root_controller: bool,
32 pub snapshot_read_authority: SnapshotReadAuthority,
33 pub quiescence_policy: QuiescencePolicy,
34 pub topology_hash_before_quiesce: String,
35 pub targets: Vec<BackupTarget>,
36 pub phases: Vec<BackupOperation>,
37}
38
39#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
47#[serde(rename_all = "kebab-case")]
48pub enum BackupScopeKind {
49 Member,
50 Subtree,
51 NonRootDeployment,
52 MaintenanceRoot,
53}
54
55#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
63#[serde(deny_unknown_fields)]
64pub struct BackupTarget {
65 pub canister_id: String,
66 pub role: Option<String>,
67 pub parent_canister_id: Option<String>,
68 pub depth: u32,
69 pub control_authority: ControlAuthority,
70 pub snapshot_read_authority: SnapshotReadAuthority,
71 pub identity_mode: IdentityMode,
72 pub expected_module_hash: Option<String>,
73}
74
75#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
83#[serde(rename_all = "kebab-case")]
84pub enum AuthorityEvidence {
85 Proven,
86 Declared,
87 Unknown,
88}
89
90#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
98#[serde(deny_unknown_fields)]
99pub struct ControlAuthority {
100 pub source: ControlAuthoritySource,
101 pub evidence: AuthorityEvidence,
102}
103
104impl ControlAuthority {
105 #[must_use]
106 pub const fn unknown() -> Self {
107 Self {
108 source: ControlAuthoritySource::Unknown,
109 evidence: AuthorityEvidence::Unknown,
110 }
111 }
112
113 #[must_use]
114 pub const fn root_controller(evidence: AuthorityEvidence) -> Self {
115 Self {
116 source: ControlAuthoritySource::RootController,
117 evidence,
118 }
119 }
120
121 #[must_use]
122 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
123 Self {
124 source: ControlAuthoritySource::OperatorController,
125 evidence,
126 }
127 }
128
129 #[must_use]
130 pub fn is_proven(&self) -> bool {
131 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
132 }
133
134 #[must_use]
135 pub fn is_proven_root_controller(&self) -> bool {
136 self.evidence == AuthorityEvidence::Proven
137 && self.source == ControlAuthoritySource::RootController
138 }
139}
140
141#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
149#[serde(rename_all = "kebab-case", tag = "kind")]
150pub enum ControlAuthoritySource {
151 Unknown,
152 RootController,
153 OperatorController,
154}
155
156#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
164#[serde(deny_unknown_fields)]
165pub struct SnapshotReadAuthority {
166 pub source: SnapshotReadAuthoritySource,
167 pub evidence: AuthorityEvidence,
168}
169
170impl SnapshotReadAuthority {
171 #[must_use]
172 pub const fn unknown() -> Self {
173 Self {
174 source: SnapshotReadAuthoritySource::Unknown,
175 evidence: AuthorityEvidence::Unknown,
176 }
177 }
178
179 #[must_use]
180 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
181 Self {
182 source: SnapshotReadAuthoritySource::OperatorController,
183 evidence,
184 }
185 }
186
187 #[must_use]
188 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
189 Self {
190 source: SnapshotReadAuthoritySource::RootConfiguredRead,
191 evidence,
192 }
193 }
194
195 #[must_use]
196 pub fn is_proven(&self) -> bool {
197 self.evidence == AuthorityEvidence::Proven
198 && self.source != SnapshotReadAuthoritySource::Unknown
199 }
200}
201
202#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
210#[serde(rename_all = "kebab-case")]
211pub enum SnapshotReadAuthoritySource {
212 Unknown,
213 OperatorController,
214 RootConfiguredRead,
215}
216
217#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
225#[serde(rename_all = "kebab-case")]
226pub enum QuiescencePolicy {
227 CrashConsistent,
228 RootCoordinated,
229}
230
231#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
239#[serde(deny_unknown_fields)]
240pub struct BackupOperation {
241 pub operation_id: String,
242 pub order: u32,
243 pub kind: BackupOperationKind,
244 pub target_canister_id: Option<String>,
245}
246
247#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
255#[serde(rename_all = "kebab-case")]
256pub enum BackupOperationKind {
257 ValidateTopology,
258 ValidateControlAuthority,
259 ValidateSnapshotReadAuthority,
260 ValidateQuiescencePolicy,
261 Stop,
262 CreateSnapshot,
263 Start,
264 DownloadSnapshot,
265 VerifyArtifact,
266 FinalizeManifest,
267}
268
269#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
277#[serde(deny_unknown_fields)]
278pub struct BackupExecutionPreflightReceipts {
279 pub plan_id: String,
280 pub preflight_id: String,
281 pub validated_at: String,
282 pub expires_at: String,
283 pub topology: TopologyPreflightReceipt,
284 pub control_authority: Vec<ControlAuthorityReceipt>,
285 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
286 pub quiescence: QuiescencePreflightReceipt,
287}
288
289#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
297#[serde(deny_unknown_fields)]
298pub struct ControlAuthorityReceipt {
299 pub plan_id: String,
300 pub preflight_id: String,
301 pub target_canister_id: String,
302 pub authority: ControlAuthority,
303 pub proof_source: AuthorityProofSource,
304 pub validated_at: String,
305 pub expires_at: String,
306 pub message: Option<String>,
307}
308
309#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
317#[serde(deny_unknown_fields)]
318pub struct SnapshotReadAuthorityReceipt {
319 pub plan_id: String,
320 pub preflight_id: String,
321 pub target_canister_id: String,
322 pub authority: SnapshotReadAuthority,
323 pub proof_source: AuthorityProofSource,
324 pub validated_at: String,
325 pub expires_at: String,
326 pub message: Option<String>,
327}
328
329#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
337#[serde(rename_all = "kebab-case")]
338pub enum AuthorityProofSource {
339 ManagementStatus,
340}
341
342#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
350#[serde(deny_unknown_fields)]
351pub struct ControlAuthorityPreflightRequest {
352 pub plan_id: String,
353 pub run_id: String,
354 pub fleet: String,
355 pub environment: 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)]
369#[serde(deny_unknown_fields)]
370pub struct ControlAuthorityPreflightTarget {
371 pub canister_id: String,
372 pub role: Option<String>,
373 pub parent_canister_id: Option<String>,
374 pub declared_authority: ControlAuthority,
375}
376
377impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
378 fn from(target: &BackupTarget) -> Self {
379 Self {
380 canister_id: target.canister_id.clone(),
381 role: target.role.clone(),
382 parent_canister_id: target.parent_canister_id.clone(),
383 declared_authority: target.control_authority.clone(),
384 }
385 }
386}
387
388#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
396#[serde(deny_unknown_fields)]
397pub struct SnapshotReadAuthorityPreflightRequest {
398 pub plan_id: String,
399 pub run_id: String,
400 pub fleet: String,
401 pub environment: String,
402 pub root_canister_id: String,
403 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
404}
405
406#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
414#[serde(deny_unknown_fields)]
415pub struct SnapshotReadAuthorityPreflightTarget {
416 pub canister_id: String,
417 pub role: Option<String>,
418 pub parent_canister_id: Option<String>,
419 pub declared_authority: SnapshotReadAuthority,
420}
421
422impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
423 fn from(target: &BackupTarget) -> Self {
424 Self {
425 canister_id: target.canister_id.clone(),
426 role: target.role.clone(),
427 parent_canister_id: target.parent_canister_id.clone(),
428 declared_authority: target.snapshot_read_authority.clone(),
429 }
430 }
431}
432
433#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
441#[serde(deny_unknown_fields)]
442pub struct TopologyPreflightRequest {
443 pub plan_id: String,
444 pub run_id: String,
445 pub fleet: String,
446 pub environment: String,
447 pub root_canister_id: String,
448 pub selected_subtree_root: Option<String>,
449 pub selected_scope_kind: BackupScopeKind,
450 pub topology_hash_before_quiesce: String,
451 pub targets: Vec<TopologyPreflightTarget>,
452}
453
454#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
462#[serde(deny_unknown_fields)]
463pub struct TopologyPreflightTarget {
464 pub canister_id: String,
465 pub parent_canister_id: Option<String>,
466 pub depth: u32,
467}
468
469impl From<&BackupTarget> for TopologyPreflightTarget {
470 fn from(target: &BackupTarget) -> Self {
471 Self {
472 canister_id: target.canister_id.clone(),
473 parent_canister_id: target.parent_canister_id.clone(),
474 depth: target.depth,
475 }
476 }
477}
478
479#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
487#[serde(deny_unknown_fields)]
488pub struct TopologyPreflightReceipt {
489 pub plan_id: String,
490 pub preflight_id: String,
491 pub topology_hash_before_quiesce: String,
492 pub topology_hash_at_preflight: String,
493 pub targets: Vec<TopologyPreflightTarget>,
494 pub validated_at: String,
495 pub expires_at: String,
496 pub message: Option<String>,
497}
498
499#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
507#[serde(deny_unknown_fields)]
508pub struct QuiescencePreflightRequest {
509 pub plan_id: String,
510 pub run_id: String,
511 pub fleet: String,
512 pub environment: String,
513 pub root_canister_id: String,
514 pub selected_subtree_root: Option<String>,
515 pub quiescence_policy: QuiescencePolicy,
516 pub targets: Vec<QuiescencePreflightTarget>,
517}
518
519#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
527#[serde(deny_unknown_fields)]
528pub struct QuiescencePreflightTarget {
529 pub canister_id: String,
530 pub role: Option<String>,
531 pub parent_canister_id: Option<String>,
532}
533
534impl From<&BackupTarget> for QuiescencePreflightTarget {
535 fn from(target: &BackupTarget) -> Self {
536 Self {
537 canister_id: target.canister_id.clone(),
538 role: target.role.clone(),
539 parent_canister_id: target.parent_canister_id.clone(),
540 }
541 }
542}
543
544#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
552#[serde(deny_unknown_fields)]
553pub struct QuiescencePreflightReceipt {
554 pub plan_id: String,
555 pub preflight_id: String,
556 pub quiescence_policy: QuiescencePolicy,
557 pub accepted: bool,
558 pub targets: Vec<QuiescencePreflightTarget>,
559 pub validated_at: String,
560 pub expires_at: String,
561 pub message: Option<String>,
562}