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 is_proven(&self) -> bool {
130 self.evidence == AuthorityEvidence::Proven && self.source != ControlAuthoritySource::Unknown
131 }
132
133 #[must_use]
134 pub fn is_proven_root_controller(&self) -> bool {
135 self.evidence == AuthorityEvidence::Proven
136 && self.source == ControlAuthoritySource::RootController
137 }
138}
139
140#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
148#[serde(rename_all = "kebab-case", tag = "kind")]
149pub enum ControlAuthoritySource {
150 Unknown,
151 RootController,
152 OperatorController,
153}
154
155#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
163#[serde(deny_unknown_fields)]
164pub struct SnapshotReadAuthority {
165 pub source: SnapshotReadAuthoritySource,
166 pub evidence: AuthorityEvidence,
167}
168
169impl SnapshotReadAuthority {
170 #[must_use]
171 pub const fn unknown() -> Self {
172 Self {
173 source: SnapshotReadAuthoritySource::Unknown,
174 evidence: AuthorityEvidence::Unknown,
175 }
176 }
177
178 #[must_use]
179 pub const fn operator_controller(evidence: AuthorityEvidence) -> Self {
180 Self {
181 source: SnapshotReadAuthoritySource::OperatorController,
182 evidence,
183 }
184 }
185
186 #[must_use]
187 pub const fn root_configured_read(evidence: AuthorityEvidence) -> Self {
188 Self {
189 source: SnapshotReadAuthoritySource::RootConfiguredRead,
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)]
209#[serde(rename_all = "kebab-case")]
210pub enum SnapshotReadAuthoritySource {
211 Unknown,
212 OperatorController,
213 RootConfiguredRead,
214}
215
216#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
224#[serde(rename_all = "kebab-case")]
225pub enum QuiescencePolicy {
226 CrashConsistent,
227 RootCoordinated,
228}
229
230#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
238#[serde(deny_unknown_fields)]
239pub struct BackupOperation {
240 pub operation_id: String,
241 pub order: u32,
242 pub kind: BackupOperationKind,
243 pub target_canister_id: Option<String>,
244}
245
246#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
254#[serde(rename_all = "kebab-case")]
255pub enum BackupOperationKind {
256 ValidateTopology,
257 ValidateControlAuthority,
258 ValidateSnapshotReadAuthority,
259 ValidateQuiescencePolicy,
260 Stop,
261 CreateSnapshot,
262 Start,
263 DownloadSnapshot,
264 VerifyArtifact,
265 FinalizeManifest,
266}
267
268#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
276#[serde(deny_unknown_fields)]
277pub struct BackupExecutionPreflightReceipts {
278 pub plan_id: String,
279 pub preflight_id: String,
280 pub validated_at: String,
281 pub expires_at: String,
282 pub topology: TopologyPreflightReceipt,
283 pub control_authority: Vec<ControlAuthorityReceipt>,
284 pub snapshot_read_authority: Vec<SnapshotReadAuthorityReceipt>,
285 pub quiescence: QuiescencePreflightReceipt,
286}
287
288#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
296#[serde(deny_unknown_fields)]
297pub struct ControlAuthorityReceipt {
298 pub plan_id: String,
299 pub preflight_id: String,
300 pub target_canister_id: String,
301 pub authority: ControlAuthority,
302 pub proof_source: AuthorityProofSource,
303 pub validated_at: String,
304 pub expires_at: String,
305 pub message: Option<String>,
306}
307
308#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
316#[serde(deny_unknown_fields)]
317pub struct SnapshotReadAuthorityReceipt {
318 pub plan_id: String,
319 pub preflight_id: String,
320 pub target_canister_id: String,
321 pub authority: SnapshotReadAuthority,
322 pub proof_source: AuthorityProofSource,
323 pub validated_at: String,
324 pub expires_at: String,
325 pub message: Option<String>,
326}
327
328#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
336#[serde(rename_all = "kebab-case")]
337pub enum AuthorityProofSource {
338 ManagementStatus,
339}
340
341#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
349#[serde(deny_unknown_fields)]
350pub struct ControlAuthorityPreflightRequest {
351 pub plan_id: String,
352 pub run_id: String,
353 pub fleet: String,
354 pub network: String,
355 pub root_canister_id: String,
356 pub requires_root_controller: bool,
357 pub targets: Vec<ControlAuthorityPreflightTarget>,
358}
359
360#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
368#[serde(deny_unknown_fields)]
369pub struct ControlAuthorityPreflightTarget {
370 pub canister_id: String,
371 pub role: Option<String>,
372 pub parent_canister_id: Option<String>,
373 pub declared_authority: ControlAuthority,
374}
375
376impl From<&BackupTarget> for ControlAuthorityPreflightTarget {
377 fn from(target: &BackupTarget) -> Self {
378 Self {
379 canister_id: target.canister_id.clone(),
380 role: target.role.clone(),
381 parent_canister_id: target.parent_canister_id.clone(),
382 declared_authority: target.control_authority.clone(),
383 }
384 }
385}
386
387#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
395#[serde(deny_unknown_fields)]
396pub struct SnapshotReadAuthorityPreflightRequest {
397 pub plan_id: String,
398 pub run_id: String,
399 pub fleet: String,
400 pub network: String,
401 pub root_canister_id: String,
402 pub targets: Vec<SnapshotReadAuthorityPreflightTarget>,
403}
404
405#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
413#[serde(deny_unknown_fields)]
414pub struct SnapshotReadAuthorityPreflightTarget {
415 pub canister_id: String,
416 pub role: Option<String>,
417 pub parent_canister_id: Option<String>,
418 pub declared_authority: SnapshotReadAuthority,
419}
420
421impl From<&BackupTarget> for SnapshotReadAuthorityPreflightTarget {
422 fn from(target: &BackupTarget) -> Self {
423 Self {
424 canister_id: target.canister_id.clone(),
425 role: target.role.clone(),
426 parent_canister_id: target.parent_canister_id.clone(),
427 declared_authority: target.snapshot_read_authority.clone(),
428 }
429 }
430}
431
432#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
440#[serde(deny_unknown_fields)]
441pub struct TopologyPreflightRequest {
442 pub plan_id: String,
443 pub run_id: String,
444 pub fleet: String,
445 pub network: String,
446 pub root_canister_id: String,
447 pub selected_subtree_root: Option<String>,
448 pub selected_scope_kind: BackupScopeKind,
449 pub topology_hash_before_quiesce: String,
450 pub targets: Vec<TopologyPreflightTarget>,
451}
452
453#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
461#[serde(deny_unknown_fields)]
462pub struct TopologyPreflightTarget {
463 pub canister_id: String,
464 pub parent_canister_id: Option<String>,
465 pub depth: u32,
466}
467
468impl From<&BackupTarget> for TopologyPreflightTarget {
469 fn from(target: &BackupTarget) -> Self {
470 Self {
471 canister_id: target.canister_id.clone(),
472 parent_canister_id: target.parent_canister_id.clone(),
473 depth: target.depth,
474 }
475 }
476}
477
478#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
486#[serde(deny_unknown_fields)]
487pub struct TopologyPreflightReceipt {
488 pub plan_id: String,
489 pub preflight_id: String,
490 pub topology_hash_before_quiesce: String,
491 pub topology_hash_at_preflight: String,
492 pub targets: Vec<TopologyPreflightTarget>,
493 pub validated_at: String,
494 pub expires_at: String,
495 pub message: Option<String>,
496}
497
498#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
506#[serde(deny_unknown_fields)]
507pub struct QuiescencePreflightRequest {
508 pub plan_id: String,
509 pub run_id: String,
510 pub fleet: String,
511 pub network: String,
512 pub root_canister_id: String,
513 pub selected_subtree_root: Option<String>,
514 pub quiescence_policy: QuiescencePolicy,
515 pub targets: Vec<QuiescencePreflightTarget>,
516}
517
518#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
526#[serde(deny_unknown_fields)]
527pub struct QuiescencePreflightTarget {
528 pub canister_id: String,
529 pub role: Option<String>,
530 pub parent_canister_id: Option<String>,
531}
532
533impl From<&BackupTarget> for QuiescencePreflightTarget {
534 fn from(target: &BackupTarget) -> Self {
535 Self {
536 canister_id: target.canister_id.clone(),
537 role: target.role.clone(),
538 parent_canister_id: target.parent_canister_id.clone(),
539 }
540 }
541}
542
543#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
551#[serde(deny_unknown_fields)]
552pub struct QuiescencePreflightReceipt {
553 pub plan_id: String,
554 pub preflight_id: String,
555 pub quiescence_policy: QuiescencePolicy,
556 pub accepted: bool,
557 pub targets: Vec<QuiescencePreflightTarget>,
558 pub validated_at: String,
559 pub expires_at: String,
560 pub message: Option<String>,
561}