Skip to main content

core_api/node/
status.rs

1use serde::{Deserialize, Serialize};
2
3pub const STATUS_TARGET: &str = "/status";
4pub const STATUS_CHANGED_TARGET: &str = "/status/changed";
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "camelCase")]
8pub enum IdentityPhase {
9    Unknown,
10    Uncommissioned,
11    Commissioning,
12    Commissioned,
13    Stale,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17#[serde(rename_all = "camelCase")]
18pub enum HubPhase {
19    Unknown,
20    WaitingDiscovery,
21    Resolved,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25#[serde(rename_all = "camelCase")]
26pub enum ConnectionPhase {
27    Disconnected,
28    Connecting,
29    Authenticating,
30    Authenticated,
31    Connected,
32    Retrying,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36#[serde(rename_all = "camelCase")]
37pub enum RuntimePhase {
38    Starting,
39    Ready,
40    Retrying,
41    Restarting,
42    Failed,
43    Corrupted,
44    Stopping,
45    Stopped,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
49#[serde(rename_all = "camelCase")]
50pub enum ProcessPhase {
51    Unknown,
52    Starting,
53    Running,
54    Restarting,
55    Stopping,
56    Failed,
57}
58
59/// Product-facing projection derived by the Node. Neither the Hub nor a
60/// supervisor may use this value to drive lifecycle transitions.
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
62#[serde(rename_all = "camelCase")]
63pub enum EffectivePhase {
64    ProcessStarting,
65    Uncommissioned,
66    Commissioning,
67    WaitingForHub,
68    Connecting,
69    Authenticating,
70    Online,
71    PartiallyOnline,
72    Degraded,
73    Retrying,
74    Failed,
75    Stopping,
76}
77
78/// Status of one commissioned identity shell. Runtime fields are a read-only
79/// projection of the separately reported logical Runtime.
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
81#[serde(rename_all = "camelCase")]
82pub struct InstanceSnapshot {
83    pub node_id: String,
84    pub runtime_id: String,
85    pub hub_id: String,
86    pub tenant_id: String,
87    pub scope_id: String,
88    pub identity: IdentityPhase,
89    pub hub: HubPhase,
90    pub connection: ConnectionPhase,
91    pub runtime: RuntimePhase,
92    pub runtime_generation: u64,
93    pub connection_generation: u64,
94    pub revision: u64,
95    pub effective: EffectivePhase,
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub runtime_reason: Option<String>,
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub connection_reason: Option<String>,
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub reason: Option<String>,
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    pub runtime_retry_after_ms: Option<u64>,
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub connection_retry_after_ms: Option<u64>,
106    pub updated_at_ms: i64,
107}
108
109/// Authoritative state of one logical business Runtime. Several identity
110/// shells may reference this object for singleton/shared Node policies.
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
112#[serde(rename_all = "camelCase")]
113pub struct RuntimeSnapshot {
114    pub runtime_id: String,
115    pub runtime: RuntimePhase,
116    pub generation: u64,
117    pub revision: u64,
118    #[serde(default)]
119    pub member_node_ids: Vec<String>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub reason: Option<String>,
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub retry_after_ms: Option<u64>,
124    pub updated_at_ms: i64,
125}
126
127/// Complete status reported by one authenticated Node identity.
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
129#[serde(rename_all = "camelCase")]
130pub struct StatusPayload {
131    pub contract: String,
132    pub service_id: String,
133    pub node_type: String,
134    pub process_generation: String,
135    pub revision: u64,
136    pub process: ProcessPhase,
137    pub instance: InstanceSnapshot,
138    pub runtime: RuntimeSnapshot,
139    pub updated_at_ms: i64,
140}
141
142/// Hub-normalized payload stored inside a public NodeInstance resource.
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144#[serde(rename_all = "camelCase")]
145pub struct InstanceStatus {
146    pub contract: String,
147    pub service_id: String,
148    pub node_type: String,
149    pub process_generation: String,
150    pub revision: u64,
151    pub process: ProcessPhase,
152    pub instance: InstanceSnapshot,
153    pub updated_at_ms: i64,
154}
155
156/// Hub-normalized payload stored inside a public NodeRuntime resource.
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
158#[serde(rename_all = "camelCase")]
159pub struct RuntimeStatus {
160    pub contract: String,
161    pub service_id: String,
162    pub node_type: String,
163    pub process_generation: String,
164    pub revision: u64,
165    pub runtime: RuntimeSnapshot,
166    pub updated_at_ms: i64,
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn status_wire_shape_is_camel_case_and_additive() {
175        let value = serde_json::json!({
176            "contract": crate::node::STATUS_CONTRACT,
177            "serviceId": "camera",
178            "nodeType": "camera",
179            "processGeneration": "process-1",
180            "revision": 2,
181            "process": "running",
182            "instance": {
183                "nodeId": "node-1",
184                "runtimeId": "instance:node-1",
185                "hubId": "hub-1",
186                "tenantId": "tenant-1",
187                "scopeId": "scope-1",
188                "identity": "commissioned",
189                "hub": "resolved",
190                "connection": "connected",
191                "runtime": "ready",
192                "runtimeGeneration": 3,
193                "connectionGeneration": 4,
194                "revision": 5,
195                "effective": "online",
196                "updatedAtMs": 6,
197                "future": true
198            },
199            "runtime": {
200                "runtimeId": "instance:node-1",
201                "runtime": "ready",
202                "generation": 3,
203                "revision": 7,
204                "memberNodeIds": ["node-1"],
205                "updatedAtMs": 8
206            },
207            "updatedAtMs": 9,
208            "future": true
209        });
210        let status: StatusPayload = serde_json::from_value(value).unwrap();
211        assert_eq!(status.instance.node_id, "node-1");
212        assert_eq!(status.runtime.runtime_id, "instance:node-1");
213    }
214}