Skip to main content

canic_core/dto/
fleet_activation.rs

1//! Module: dto::fleet_activation
2//!
3//! Responsibility: carry Fleet activation identity and evidence across host/runtime boundaries.
4//! Does not own: validation, persistence, hashing, activation policy, or recovery transitions.
5//! Boundary: authoritative owners validate these passive shapes before storing or acting on them.
6
7use crate::ids::{FleetBinding, FleetKey, ReleaseBuildId};
8use candid::{CandidType, Principal};
9use serde::{Deserialize, Serialize};
10
11/// Maximum managed Canisters represented by one Fleet activation.
12pub const MAX_FLEET_ACTIVATION_CANISTERS: usize = 4_096;
13/// Maximum issuer entries represented by one Fleet credential manifest.
14pub const MAX_FLEET_CREDENTIAL_MANIFEST_ENTRIES: usize = 64;
15
16///
17/// FleetCascadeManifestEntry
18///
19
20#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21pub struct FleetCascadeManifestEntry {
22    pub principal: Principal,
23    pub state_snapshot_hash: [u8; 32],
24    pub topology_snapshot_hash: [u8; 32],
25}
26
27///
28/// FleetCascadeActivationEvidence
29///
30
31#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
32pub enum FleetCascadeActivationEvidence {
33    Source {
34        cascade_manifest_hash: [u8; 32],
35    },
36    Applied {
37        state_snapshot_hash: [u8; 32],
38        topology_snapshot_hash: [u8; 32],
39    },
40}
41
42///
43/// FleetCredentialGenerationRef
44///
45
46#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
47pub struct FleetCredentialGenerationRef {
48    pub generation: u64,
49    pub manifest_hash: [u8; 32],
50}
51
52///
53/// FleetCredentialManifestEntry
54///
55
56#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
57pub struct FleetCredentialManifestEntry {
58    pub root_issuer: Principal,
59    pub subject_canister: Principal,
60    pub not_before_ns: u64,
61    pub expires_at_ns: u64,
62    pub key_identity_hash: [u8; 32],
63    pub cert_hash: [u8; 32],
64    pub proof_hash: [u8; 32],
65    pub bundle_hash: [u8; 32],
66}
67
68///
69/// FleetCredentialManifest
70///
71
72#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
73pub struct FleetCredentialManifest {
74    pub fleet: FleetKey,
75    pub activation_id: [u8; 32],
76    pub generation: u64,
77    pub root_policy_set_hash: [u8; 32],
78    pub renewal_template_set_hash: [u8; 32],
79    pub entries: Vec<FleetCredentialManifestEntry>,
80}
81
82///
83/// FleetActivationIdentity
84///
85
86#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
87pub struct FleetActivationIdentity {
88    pub fleet: FleetBinding,
89    pub operation_id: [u8; 32],
90    pub release_build_id: ReleaseBuildId,
91}
92
93///
94/// FleetActivationPhase
95///
96
97#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
98pub enum FleetActivationPhase {
99    Prepared,
100    Active,
101}
102
103///
104/// FleetActivationStatusResponse
105///
106
107#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
108pub struct FleetActivationStatusResponse {
109    pub phase: FleetActivationPhase,
110    pub identity: FleetActivationIdentity,
111    pub cascade: Option<FleetCascadeActivationEvidence>,
112    pub cascade_manifest: Option<Vec<FleetCascadeManifestEntry>>,
113    pub credential: Option<FleetCredentialGenerationRef>,
114    pub credential_manifest: Option<FleetCredentialManifest>,
115    pub activated_at_ns: Option<u64>,
116}
117
118#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
119pub struct FleetActivationResumeRequest {
120    pub operation_id: [u8; 32],
121    pub credential: FleetCredentialGenerationRef,
122}
123
124#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
125pub struct FleetCredentialGenerationRequest {
126    pub operation_id: [u8; 32],
127    pub credential: FleetCredentialGenerationRef,
128}
129
130#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
131pub struct FleetActivationRequest {
132    pub operation_id: [u8; 32],
133    pub credential: FleetCredentialGenerationRef,
134    pub activation_evidence_hash: [u8; 32],
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::ids::{AppId, CanonicalNetworkId, FleetId, FleetKey, ReleaseBuildNonce};
141
142    fn fleet_binding() -> FleetBinding {
143        FleetBinding {
144            fleet: FleetKey {
145                canonical_network_id: CanonicalNetworkId::ic_mainnet(),
146                fleet_id: FleetId::from_generated_bytes([7; 32]),
147            },
148            app: AppId::from("toko"),
149        }
150    }
151
152    #[test]
153    fn fleet_activation_status_roundtrips_as_named_candid_shapes() {
154        let input = FleetActivationStatusResponse {
155            phase: FleetActivationPhase::Prepared,
156            identity: FleetActivationIdentity {
157                fleet: fleet_binding(),
158                operation_id: [11; 32],
159                release_build_id: ReleaseBuildId::from_nonce(ReleaseBuildNonce::from_random_bytes(
160                    [12; 32],
161                )),
162            },
163            cascade: Some(FleetCascadeActivationEvidence::Source {
164                cascade_manifest_hash: [13; 32],
165            }),
166            cascade_manifest: Some(vec![FleetCascadeManifestEntry {
167                principal: Principal::from_slice(&[14; 29]),
168                state_snapshot_hash: [15; 32],
169                topology_snapshot_hash: [16; 32],
170            }]),
171            credential: None,
172            credential_manifest: None,
173            activated_at_ns: None,
174        };
175
176        let bytes = candid::encode_one(&input).expect("encode activation status");
177        let decoded: FleetActivationStatusResponse =
178            candid::decode_one(&bytes).expect("decode activation status");
179
180        assert_eq!(decoded, input);
181    }
182}