Skip to main content

appcore_api/http/
state.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: state.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/06/02 13:08:16 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/07/24 11:51:10 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11//! Shared HTTP runtime state and status metadata.
12
13use crate::ApiRouter;
14use appcore_core::{RuntimeController, RuntimeOperationalMode};
15use parking_lot::Mutex;
16use std::sync::atomic::AtomicU64;
17use std::sync::Arc;
18
19use super::auth::HttpCommandAuth;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22/// Runtime HTTP listener and request-size configuration.
23pub struct HttpApiConfig {
24    /// Interface or address to bind.
25    pub host: String,
26    /// TCP port to bind.
27    pub port: u16,
28    /// Whether the embedded listener should run.
29    pub enabled: bool,
30    /// Maximum accepted request body size in bytes.
31    pub max_payload_bytes: usize,
32}
33
34impl Default for HttpApiConfig {
35    fn default() -> Self {
36        Self {
37            host: "127.0.0.1".to_string(),
38            port: 8080,
39            enabled: false,
40            max_payload_bytes: 65_536,
41        }
42    }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
46/// Non-sensitive Runtime facts exposed by health and status routes.
47pub struct RuntimeStaticInfo {
48    /// Application identity hosted by this process.
49    pub app_id: String,
50    /// Runtime node identity.
51    pub node_id: String,
52    /// Tenant isolation boundary.
53    pub tenant_id: String,
54    /// Cluster isolation boundary.
55    pub cluster_id: String,
56    /// Logical Core identity.
57    pub core_id: String,
58    /// Initial operational-mode label.
59    pub operation_mode: String,
60    /// Storage provider health label.
61    pub storage_status: String,
62    /// Whether required security material initialized successfully.
63    pub security_ok: bool,
64    /// Whether HTTP ingress is enabled.
65    pub api_enabled: bool,
66    /// Whether synchronization is enabled.
67    pub sync_enabled: bool,
68    /// Local synchronization role.
69    pub sync_role: String,
70    /// Number of records currently visible in the sync log.
71    pub sync_log_len: usize,
72    /// Optional sync-log path for local diagnostics.
73    pub sync_log_path: Option<String>,
74    /// Optional sync-checkpoint path for local diagnostics.
75    pub sync_checkpoint_path: Option<String>,
76    /// Configured peer addresses without credentials.
77    pub sync_peers: Vec<String>,
78    /// Whether DNS peer discovery is enabled.
79    pub sync_dns_enabled: bool,
80    /// Configured DNS peer seeds.
81    pub sync_dns_seeds: Vec<String>,
82    /// Default port applied to DNS seeds.
83    pub sync_dns_default_port: u16,
84    /// Idempotency retention window in milliseconds.
85    pub idempotency_ttl_ms: u64,
86    /// Optional idempotency-store path for local diagnostics.
87    pub idempotency_path: Option<String>,
88}
89
90#[derive(Clone)]
91pub(crate) struct HttpState {
92    pub(crate) static_info: Arc<RuntimeStaticInfo>,
93    pub(crate) controller: Option<Arc<Mutex<RuntimeController>>>,
94    pub(crate) app_query_router: Option<Arc<Mutex<ApiRouter>>>,
95    pub(crate) sync_log: Option<Arc<dyn SyncLogView>>,
96    pub(crate) tick_counter: Option<Arc<AtomicU64>>,
97    pub(crate) operation_mode: Option<Arc<Mutex<RuntimeOperationalMode>>>,
98    pub(crate) command_policy: Option<Arc<dyn CommandCapabilityPolicy>>,
99    pub(crate) supervisor: Option<appcore_supervisor::Supervisor>,
100    pub(crate) auth: HttpCommandAuth,
101    pub(crate) max_payload_bytes: usize,
102    pub(crate) clock: Arc<dyn appcore_core::Clock>,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
106/// Stable reason a capability policy rejected an invocation.
107pub enum CommandCapabilityPolicyError {
108    /// The invocation has no declared capability descriptor.
109    CapabilityNotDeclared,
110    /// The capability requires an idempotency key.
111    MissingIdempotencyKey,
112    /// The capability requires service leadership.
113    RequiresLeader,
114    /// The applicable service lease has expired.
115    LeaseExpired,
116    /// The request uses an obsolete fencing epoch.
117    StaleEpoch,
118    /// Current operational policy permits reads only.
119    ReadOnly,
120    /// Provider-specific policy rejected the invocation.
121    Rejected(String),
122}
123
124/// Authorizes application invocations against capability and leadership policy.
125pub trait CommandCapabilityPolicy: Send + Sync {
126    /// Authorizes a named command at `now_ms`.
127    fn authorize_command(
128        &self,
129        command_name: &str,
130        idempotency_key: Option<&str>,
131        now_ms: u64,
132    ) -> Result<(), CommandCapabilityPolicyError>;
133
134    /// Authorizes a named application query at `now_ms`.
135    fn authorize_query(
136        &self,
137        _query_name: &str,
138        _now_ms: u64,
139    ) -> Result<(), CommandCapabilityPolicyError> {
140        Ok(())
141    }
142}
143
144/// Read-only synchronization-log metrics exposed to the HTTP host.
145pub trait SyncLogView: Send + Sync {
146    /// Returns the number of visible replication records.
147    fn len(&self) -> Result<usize, SyncLogViewError>;
148
149    /// Reports whether no replication records are visible.
150    fn is_empty(&self) -> Result<bool, SyncLogViewError> {
151        self.len().map(|length| length == 0)
152    }
153}
154
155/// Redacted failure to observe synchronization-log state.
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub struct SyncLogViewError;
158
159impl std::fmt::Display for SyncLogViewError {
160    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        formatter.write_str("synchronization log observation failed")
162    }
163}
164
165impl std::error::Error for SyncLogViewError {}