1use crate::manifest::IdentityMode;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19pub struct BackupPlan {
20 pub plan_id: String,
21 pub run_id: String,
22 pub fleet: String,
23 pub network: String,
24 pub root_canister_id: String,
25 pub selected_subtree_root: Option<String>,
26 pub selected_scope_kind: BackupScopeKind,
27 pub include_descendants: bool,
28 pub root_included: bool,
29 pub requires_root_controller: bool,
30 pub snapshot_read_authority: SnapshotReadAuthority,
31 pub quiescence_policy: QuiescencePolicy,
32 pub topology_hash_before_quiesce: String,
33 pub targets: Vec<BackupTarget>,
34 pub phases: Vec<BackupOperation>,
35}
36
37#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
45#[serde(rename_all = "kebab-case")]
46pub enum BackupScopeKind {
47 Member,
48 Subtree,
49 NonRootDeployment,
50 MaintenanceRoot,
51}
52
53#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
61pub struct BackupTarget {
62 pub canister_id: String,
63 pub role: Option<String>,
64 pub parent_canister_id: Option<String>,
65 pub depth: u32,
66 pub control_authority: ControlAuthority,
67 pub snapshot_read_authority: SnapshotReadAuthority,
68 pub identity_mode: IdentityMode,
69 pub expected_module_hash: Option<String>,
70}
71
72#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
80#[serde(rename_all = "kebab-case")]
81pub enum AuthorityEvidence {
82 Proven,
83 Declared,
84 Unknown,
85}
86
87#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
95pub struct ControlAuthority {
96 pub source: ControlAuthoritySource,
97 pub evidence: AuthorityEvidence,
98}
99
100impl ControlAuthority {
101 #[must_use]
102 pub const fn unknown() -> Self {
103 Self {
104 source: ControlAuthoritySource::Unknown,
105 evidence: AuthorityEvidence::Unknown,
106 }
107 }
108
109 #[must_use]
110 pub const fn root_controller(evidence: AuthorityEvidence) -> Self {
111 Self {
112 source: ControlAuthoritySource::RootController,
113 evidence,
114 }
115 }
116
117 #[must_use]
118 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
119 Self {
120 source: ControlAuthoritySource::OperatorController,
121 evidence,
122 }
123 }
124
125 #[must_use]
126 pub fn alternate_controller(
127 controller: impl Into<String>,
128 reason: impl Into<String>,
129 evidence: AuthorityEvidence,
130 ) -> Self {
131 Self {
132 source: ControlAuthoritySource::AlternateController {
133 controller: controller.into(),
134 reason: reason.into(),
135 },
136 evidence,
137 }
138 }
139
140 #[must_use]
141 pub fn is_proven(&self) -> bool {
142 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
143 }
144
145 #[must_use]
146 pub fn is_proven_root_controller(&self) -> bool {
147 self.evidence == AuthorityEvidence::Proven
148 && self.source == ControlAuthoritySource::RootController
149 }
150}
151
152#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
160#[serde(rename_all = "kebab-case", tag = "kind")]
161pub enum ControlAuthoritySource {
162 Unknown,
163 RootController,
164 OperatorController,
165 AlternateController { controller: String, reason: String },
166}
167
168#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
176pub struct SnapshotReadAuthority {
177 pub source: SnapshotReadAuthoritySource,
178 pub evidence: AuthorityEvidence,
179}
180
181impl SnapshotReadAuthority {
182 #[must_use]
183 pub const fn unknown() -> Self {
184 Self {
185 source: SnapshotReadAuthoritySource::Unknown,
186 evidence: AuthorityEvidence::Unknown,
187 }
188 }
189
190 #[must_use]
191 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
192 Self {
193 source: SnapshotReadAuthoritySource::OperatorController,
194 evidence,
195 }
196 }
197
198 #[must_use]
199 pub const fn snapshot_visibility(evidence: AuthorityEvidence) -> Self {
200 Self {
201 source: SnapshotReadAuthoritySource::SnapshotVisibility,
202 evidence,
203 }
204 }
205
206 #[must_use]
207 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
208 Self {
209 source: SnapshotReadAuthoritySource::RootConfiguredRead,
210 evidence,
211 }
212 }
213
214 #[must_use]
215 pub const fn root_mediated_transfer(evidence: AuthorityEvidence) -> Self {
216 Self {
217 source: SnapshotReadAuthoritySource::RootMediatedTransfer,
218 evidence,
219 }
220 }
221
222 #[must_use]
223 pub fn is_proven(&self) -> bool {
224 self.evidence == AuthorityEvidence::Proven
225 && self.source != SnapshotReadAuthoritySource::Unknown
226 }
227}
228
229#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
237#[serde(rename_all = "kebab-case")]
238pub enum SnapshotReadAuthoritySource {
239 Unknown,
240 OperatorController,
241 SnapshotVisibility,
242 RootConfiguredRead,
243 RootMediatedTransfer,
244}
245
246#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
254#[serde(rename_all = "kebab-case")]
255pub enum QuiescencePolicy {
256 CrashConsistent,
257 RootCoordinated,
258 AppQuiesced,
259}
260
261#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
269pub struct BackupOperation {
270 pub operation_id: String,
271 pub order: u32,
272 pub kind: BackupOperationKind,
273 pub target_canister_id: Option<String>,
274}
275
276#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
284#[serde(rename_all = "kebab-case")]
285pub enum BackupOperationKind {
286 ValidateTopology,
287 ValidateControlAuthority,
288 ValidateSnapshotReadAuthority,
289 ValidateQuiescencePolicy,
290 Stop,
291 CreateSnapshot,
292 Start,
293 DownloadSnapshot,
294 VerifyArtifact,
295 FinalizeManifest,
296}
297
298#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
306pub struct BackupReceipt {
307 pub plan_id: String,
308 pub operation_id: String,
309 pub status: BackupReceiptStatus,
310 pub target_canister_id: Option<String>,
311 pub snapshot_id: Option<String>,
312 pub message: Option<String>,
313}
314
315#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
323#[serde(rename_all = "kebab-case")]
324pub enum BackupReceiptStatus {
325 Completed,
326 Failed,
327 Skipped,
328}
329
330#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
338pub struct BackupExecutionPreflightReceipts {
339 pub plan_id: String,
340 pub preflight_id: String,
341 pub validated_at: String,
342 pub expires_at: String,
343 pub topology: TopologyPreflightReceipt,
344 pub control_authority: Vec<ControlAuthorityReceipt>,
345 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
346 pub quiescence: QuiescencePreflightReceipt,
347}
348
349#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
357pub struct ControlAuthorityReceipt {
358 pub plan_id: String,
359 pub preflight_id: String,
360 pub target_canister_id: String,
361 pub authority: ControlAuthority,
362 pub proof_source: AuthorityProofSource,
363 pub validated_at: String,
364 pub expires_at: String,
365 pub message: Option<String>,
366}
367
368#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
376pub struct SnapshotReadAuthorityReceipt {
377 pub plan_id: String,
378 pub preflight_id: String,
379 pub target_canister_id: String,
380 pub authority: SnapshotReadAuthority,
381 pub proof_source: AuthorityProofSource,
382 pub validated_at: String,
383 pub expires_at: String,
384 pub message: Option<String>,
385}
386
387#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
395#[serde(rename_all = "kebab-case")]
396pub enum AuthorityProofSource {
397 RootCoordination,
398 ManagementStatus,
399 SnapshotReadCheck,
400 Declaration,
401 Unknown,
402}
403
404#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
412pub struct ControlAuthorityPreflightRequest {
413 pub plan_id: String,
414 pub run_id: String,
415 pub fleet: String,
416 pub network: String,
417 pub root_canister_id: String,
418 pub requires_root_controller: bool,
419 pub targets: Vec<ControlAuthorityPreflightTarget>,
420}
421
422#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
430pub struct ControlAuthorityPreflightTarget {
431 pub canister_id: String,
432 pub role: Option<String>,
433 pub parent_canister_id: Option<String>,
434 pub declared_authority: ControlAuthority,
435}
436
437impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
438 fn from(target: &BackupTarget) -> Self {
439 Self {
440 canister_id: target.canister_id.clone(),
441 role: target.role.clone(),
442 parent_canister_id: target.parent_canister_id.clone(),
443 declared_authority: target.control_authority.clone(),
444 }
445 }
446}
447
448#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
456pub struct SnapshotReadAuthorityPreflightRequest {
457 pub plan_id: String,
458 pub run_id: String,
459 pub fleet: String,
460 pub network: String,
461 pub root_canister_id: String,
462 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
463}
464
465#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
473pub struct SnapshotReadAuthorityPreflightTarget {
474 pub canister_id: String,
475 pub role: Option<String>,
476 pub parent_canister_id: Option<String>,
477 pub declared_authority: SnapshotReadAuthority,
478}
479
480impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
481 fn from(target: &BackupTarget) -> Self {
482 Self {
483 canister_id: target.canister_id.clone(),
484 role: target.role.clone(),
485 parent_canister_id: target.parent_canister_id.clone(),
486 declared_authority: target.snapshot_read_authority.clone(),
487 }
488 }
489}
490
491#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
499pub struct TopologyPreflightRequest {
500 pub plan_id: String,
501 pub run_id: String,
502 pub fleet: String,
503 pub network: String,
504 pub root_canister_id: String,
505 pub selected_subtree_root: Option<String>,
506 pub selected_scope_kind: BackupScopeKind,
507 pub topology_hash_before_quiesce: String,
508 pub targets: Vec<TopologyPreflightTarget>,
509}
510
511#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
519pub struct TopologyPreflightTarget {
520 pub canister_id: String,
521 pub parent_canister_id: Option<String>,
522 pub depth: u32,
523}
524
525impl From<&BackupTarget> for TopologyPreflightTarget {
526 fn from(target: &BackupTarget) -> Self {
527 Self {
528 canister_id: target.canister_id.clone(),
529 parent_canister_id: target.parent_canister_id.clone(),
530 depth: target.depth,
531 }
532 }
533}
534
535#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
543pub struct TopologyPreflightReceipt {
544 pub plan_id: String,
545 pub preflight_id: String,
546 pub topology_hash_before_quiesce: String,
547 pub topology_hash_at_preflight: String,
548 pub targets: Vec<TopologyPreflightTarget>,
549 pub validated_at: String,
550 pub expires_at: String,
551 pub message: Option<String>,
552}
553
554#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
562pub struct QuiescencePreflightRequest {
563 pub plan_id: String,
564 pub run_id: String,
565 pub fleet: String,
566 pub network: String,
567 pub root_canister_id: String,
568 pub selected_subtree_root: Option<String>,
569 pub quiescence_policy: QuiescencePolicy,
570 pub targets: Vec<QuiescencePreflightTarget>,
571}
572
573#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
581pub struct QuiescencePreflightTarget {
582 pub canister_id: String,
583 pub role: Option<String>,
584 pub parent_canister_id: Option<String>,
585}
586
587impl From<&BackupTarget> for QuiescencePreflightTarget {
588 fn from(target: &BackupTarget) -> Self {
589 Self {
590 canister_id: target.canister_id.clone(),
591 role: target.role.clone(),
592 parent_canister_id: target.parent_canister_id.clone(),
593 }
594 }
595}
596
597#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
605pub struct QuiescencePreflightReceipt {
606 pub plan_id: String,
607 pub preflight_id: String,
608 pub quiescence_policy: QuiescencePolicy,
609 pub accepted: bool,
610 pub targets: Vec<QuiescencePreflightTarget>,
611 pub validated_at: String,
612 pub expires_at: String,
613 pub message: Option<String>,
614}