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_id: String,
22 pub run_id: String,
23 pub fleet: String,
24 pub network: String,
25 pub root_canister_id: String,
26 pub selected_subtree_root: Option<String>,
27 pub selected_scope_kind: BackupScopeKind,
28 pub include_descendants: bool,
29 pub root_included: bool,
30 pub requires_root_controller: bool,
31 pub snapshot_read_authority: SnapshotReadAuthority,
32 pub quiescence_policy: QuiescencePolicy,
33 pub topology_hash_before_quiesce: String,
34 pub targets: Vec<BackupTarget>,
35 pub phases: Vec<BackupOperation>,
36}
37
38#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
46#[serde(rename_all = "kebab-case")]
47pub enum BackupScopeKind {
48 Member,
49 Subtree,
50 NonRootDeployment,
51 MaintenanceRoot,
52}
53
54#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
62#[serde(deny_unknown_fields)]
63pub struct BackupTarget {
64 pub canister_id: String,
65 pub role: Option<String>,
66 pub parent_canister_id: Option<String>,
67 pub depth: u32,
68 pub control_authority: ControlAuthority,
69 pub snapshot_read_authority: SnapshotReadAuthority,
70 pub identity_mode: IdentityMode,
71 pub expected_module_hash: Option<String>,
72}
73
74#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
82#[serde(rename_all = "kebab-case")]
83pub enum AuthorityEvidence {
84 Proven,
85 Declared,
86 Unknown,
87}
88
89#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
97#[serde(deny_unknown_fields)]
98pub struct ControlAuthority {
99 pub source: ControlAuthoritySource,
100 pub evidence: AuthorityEvidence,
101}
102
103impl ControlAuthority {
104 #[must_use]
105 pub const fn unknown() -> Self {
106 Self {
107 source: ControlAuthoritySource::Unknown,
108 evidence: AuthorityEvidence::Unknown,
109 }
110 }
111
112 #[must_use]
113 pub const fn root_controller(evidence: AuthorityEvidence) -> Self {
114 Self {
115 source: ControlAuthoritySource::RootController,
116 evidence,
117 }
118 }
119
120 #[must_use]
121 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
122 Self {
123 source: ControlAuthoritySource::OperatorController,
124 evidence,
125 }
126 }
127
128 #[must_use]
129 pub fn alternate_controller(
130 controller: impl Into<String>,
131 reason: impl Into<String>,
132 evidence: AuthorityEvidence,
133 ) -> Self {
134 Self {
135 source: ControlAuthoritySource::AlternateController {
136 controller: controller.into(),
137 reason: reason.into(),
138 },
139 evidence,
140 }
141 }
142
143 #[must_use]
144 pub fn is_proven(&self) -> bool {
145 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
146 }
147
148 #[must_use]
149 pub fn is_proven_root_controller(&self) -> bool {
150 self.evidence == AuthorityEvidence::Proven
151 && self.source == ControlAuthoritySource::RootController
152 }
153}
154
155#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
163#[serde(rename_all = "kebab-case", tag = "kind")]
164pub enum ControlAuthoritySource {
165 Unknown,
166 RootController,
167 OperatorController,
168 AlternateController { controller: String, reason: String },
169}
170
171#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
179#[serde(deny_unknown_fields)]
180pub struct SnapshotReadAuthority {
181 pub source: SnapshotReadAuthoritySource,
182 pub evidence: AuthorityEvidence,
183}
184
185impl SnapshotReadAuthority {
186 #[must_use]
187 pub const fn unknown() -> Self {
188 Self {
189 source: SnapshotReadAuthoritySource::Unknown,
190 evidence: AuthorityEvidence::Unknown,
191 }
192 }
193
194 #[must_use]
195 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
196 Self {
197 source: SnapshotReadAuthoritySource::OperatorController,
198 evidence,
199 }
200 }
201
202 #[must_use]
203 pub const fn snapshot_visibility(evidence: AuthorityEvidence) -> Self {
204 Self {
205 source: SnapshotReadAuthoritySource::SnapshotVisibility,
206 evidence,
207 }
208 }
209
210 #[must_use]
211 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
212 Self {
213 source: SnapshotReadAuthoritySource::RootConfiguredRead,
214 evidence,
215 }
216 }
217
218 #[must_use]
219 pub const fn root_mediated_transfer(evidence: AuthorityEvidence) -> Self {
220 Self {
221 source: SnapshotReadAuthoritySource::RootMediatedTransfer,
222 evidence,
223 }
224 }
225
226 #[must_use]
227 pub fn is_proven(&self) -> bool {
228 self.evidence == AuthorityEvidence::Proven
229 && self.source != SnapshotReadAuthoritySource::Unknown
230 }
231}
232
233#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
241#[serde(rename_all = "kebab-case")]
242pub enum SnapshotReadAuthoritySource {
243 Unknown,
244 OperatorController,
245 SnapshotVisibility,
246 RootConfiguredRead,
247 RootMediatedTransfer,
248}
249
250#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
258#[serde(rename_all = "kebab-case")]
259pub enum QuiescencePolicy {
260 CrashConsistent,
261 RootCoordinated,
262 AppQuiesced,
263}
264
265#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
273#[serde(deny_unknown_fields)]
274pub struct BackupOperation {
275 pub operation_id: String,
276 pub order: u32,
277 pub kind: BackupOperationKind,
278 pub target_canister_id: Option<String>,
279}
280
281#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
289#[serde(rename_all = "kebab-case")]
290pub enum BackupOperationKind {
291 ValidateTopology,
292 ValidateControlAuthority,
293 ValidateSnapshotReadAuthority,
294 ValidateQuiescencePolicy,
295 Stop,
296 CreateSnapshot,
297 Start,
298 DownloadSnapshot,
299 VerifyArtifact,
300 FinalizeManifest,
301}
302
303#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
311#[serde(deny_unknown_fields)]
312pub struct BackupExecutionPreflightReceipts {
313 pub plan_id: String,
314 pub preflight_id: String,
315 pub validated_at: String,
316 pub expires_at: String,
317 pub topology: TopologyPreflightReceipt,
318 pub control_authority: Vec<ControlAuthorityReceipt>,
319 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
320 pub quiescence: QuiescencePreflightReceipt,
321}
322
323#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
331#[serde(deny_unknown_fields)]
332pub struct ControlAuthorityReceipt {
333 pub plan_id: String,
334 pub preflight_id: String,
335 pub target_canister_id: String,
336 pub authority: ControlAuthority,
337 pub proof_source: AuthorityProofSource,
338 pub validated_at: String,
339 pub expires_at: String,
340 pub message: Option<String>,
341}
342
343#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
351#[serde(deny_unknown_fields)]
352pub struct SnapshotReadAuthorityReceipt {
353 pub plan_id: String,
354 pub preflight_id: String,
355 pub target_canister_id: String,
356 pub authority: SnapshotReadAuthority,
357 pub proof_source: AuthorityProofSource,
358 pub validated_at: String,
359 pub expires_at: String,
360 pub message: Option<String>,
361}
362
363#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
371#[serde(rename_all = "kebab-case")]
372pub enum AuthorityProofSource {
373 RootCoordination,
374 ManagementStatus,
375 SnapshotReadCheck,
376 Declaration,
377 Unknown,
378}
379
380#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
388#[serde(deny_unknown_fields)]
389pub struct ControlAuthorityPreflightRequest {
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 requires_root_controller: bool,
396 pub targets: Vec<ControlAuthorityPreflightTarget>,
397}
398
399#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
407#[serde(deny_unknown_fields)]
408pub struct ControlAuthorityPreflightTarget {
409 pub canister_id: String,
410 pub role: Option<String>,
411 pub parent_canister_id: Option<String>,
412 pub declared_authority: ControlAuthority,
413}
414
415impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
416 fn from(target: &BackupTarget) -> Self {
417 Self {
418 canister_id: target.canister_id.clone(),
419 role: target.role.clone(),
420 parent_canister_id: target.parent_canister_id.clone(),
421 declared_authority: target.control_authority.clone(),
422 }
423 }
424}
425
426#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
434#[serde(deny_unknown_fields)]
435pub struct SnapshotReadAuthorityPreflightRequest {
436 pub plan_id: String,
437 pub run_id: String,
438 pub fleet: String,
439 pub network: String,
440 pub root_canister_id: String,
441 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
442}
443
444#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
452#[serde(deny_unknown_fields)]
453pub struct SnapshotReadAuthorityPreflightTarget {
454 pub canister_id: String,
455 pub role: Option<String>,
456 pub parent_canister_id: Option<String>,
457 pub declared_authority: SnapshotReadAuthority,
458}
459
460impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
461 fn from(target: &BackupTarget) -> Self {
462 Self {
463 canister_id: target.canister_id.clone(),
464 role: target.role.clone(),
465 parent_canister_id: target.parent_canister_id.clone(),
466 declared_authority: target.snapshot_read_authority.clone(),
467 }
468 }
469}
470
471#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
479#[serde(deny_unknown_fields)]
480pub struct TopologyPreflightRequest {
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 selected_scope_kind: BackupScopeKind,
488 pub topology_hash_before_quiesce: String,
489 pub targets: Vec<TopologyPreflightTarget>,
490}
491
492#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
500#[serde(deny_unknown_fields)]
501pub struct TopologyPreflightTarget {
502 pub canister_id: String,
503 pub parent_canister_id: Option<String>,
504 pub depth: u32,
505}
506
507impl From<&BackupTarget> for TopologyPreflightTarget {
508 fn from(target: &BackupTarget) -> Self {
509 Self {
510 canister_id: target.canister_id.clone(),
511 parent_canister_id: target.parent_canister_id.clone(),
512 depth: target.depth,
513 }
514 }
515}
516
517#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
525#[serde(deny_unknown_fields)]
526pub struct TopologyPreflightReceipt {
527 pub plan_id: String,
528 pub preflight_id: String,
529 pub topology_hash_before_quiesce: String,
530 pub topology_hash_at_preflight: String,
531 pub targets: Vec<TopologyPreflightTarget>,
532 pub validated_at: String,
533 pub expires_at: String,
534 pub message: Option<String>,
535}
536
537#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
545#[serde(deny_unknown_fields)]
546pub struct QuiescencePreflightRequest {
547 pub plan_id: String,
548 pub run_id: String,
549 pub fleet: String,
550 pub network: String,
551 pub root_canister_id: String,
552 pub selected_subtree_root: Option<String>,
553 pub quiescence_policy: QuiescencePolicy,
554 pub targets: Vec<QuiescencePreflightTarget>,
555}
556
557#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
565#[serde(deny_unknown_fields)]
566pub struct QuiescencePreflightTarget {
567 pub canister_id: String,
568 pub role: Option<String>,
569 pub parent_canister_id: Option<String>,
570}
571
572impl From<&BackupTarget> for QuiescencePreflightTarget {
573 fn from(target: &BackupTarget) -> Self {
574 Self {
575 canister_id: target.canister_id.clone(),
576 role: target.role.clone(),
577 parent_canister_id: target.parent_canister_id.clone(),
578 }
579 }
580}
581
582#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
590#[serde(deny_unknown_fields)]
591pub struct QuiescencePreflightReceipt {
592 pub plan_id: String,
593 pub preflight_id: String,
594 pub quiescence_policy: QuiescencePolicy,
595 pub accepted: bool,
596 pub targets: Vec<QuiescencePreflightTarget>,
597 pub validated_at: String,
598 pub expires_at: String,
599 pub message: Option<String>,
600}