made_core/ports/configuration.rs
1//! [`ConfigurationPort`] — read-only access to validated service
2//! configuration.
3
4use async_trait::async_trait;
5use serde::{Deserialize, Serialize};
6
7use crate::error::DomainError;
8
9/// Minimal configuration surface the core needs to reason about the
10/// service. Adapter implementations (env vars, Figment, Kubernetes
11/// downward API, …) map to this shape.
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ServiceConfig {
14 pub grpc_port: u16,
15 pub http_port: u16,
16 pub nats_enabled: bool,
17 pub nats_url: String,
18 pub trigger_subject: String,
19 pub publish_prefix: String,
20 /// When set, deliberations persist to Postgres; otherwise the
21 /// in-memory repository is wired. Empty-string is treated as
22 /// unset so the chart can carry a placeholder default.
23 pub postgres_url: Option<String>,
24 /// When set, ceremony state, its audit journal and its outbox
25 /// persist to an embedded store at this path; otherwise they are
26 /// held in memory.
27 ///
28 /// Unset is a deliberate choice, not a default that happens to be
29 /// safe: step leases, idempotency keys and pending human guards
30 /// exist to survive failure, and in memory they survive nothing.
31 /// A server left volatile says so at startup.
32 pub ceremony_store_path: Option<String>,
33 /// Transport security for the gRPC server.
34 pub grpc_tls: GrpcTlsConfig,
35}
36
37/// Mode + paths for the gRPC server's transport security. Validated
38/// at load time so the adapter never sees an internally inconsistent
39/// state (e.g. mode=server with no cert path).
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub enum GrpcTlsConfig {
42 /// Plain HTTP/2 over TCP. Acceptable inside a private mesh; not
43 /// for cross-network deployments.
44 Disabled,
45 /// One-way TLS: the server presents an identity, the client
46 /// verifies it via a trust anchor it already has.
47 Server { cert_path: String, key_path: String },
48 /// Mutual TLS: client presents an identity that the server
49 /// validates against `client_ca_path`.
50 Mutual {
51 cert_path: String,
52 key_path: String,
53 client_ca_path: String,
54 },
55}
56
57impl GrpcTlsConfig {
58 /// Disabled by default — every other mode requires explicit
59 /// configuration with file paths the binary can actually read.
60 #[must_use]
61 pub fn disabled() -> Self {
62 Self::Disabled
63 }
64
65 /// Operator-friendly name for the active mode. Useful for
66 /// startup-log honesty so deployments expose what they're doing.
67 #[must_use]
68 pub fn mode_name(&self) -> &'static str {
69 match self {
70 Self::Disabled => "none",
71 Self::Server { .. } => "server",
72 Self::Mutual { .. } => "mutual",
73 }
74 }
75}
76
77impl Default for GrpcTlsConfig {
78 fn default() -> Self {
79 Self::disabled()
80 }
81}
82
83#[async_trait]
84pub trait ConfigurationPort: Send + Sync {
85 async fn load(&self) -> Result<ServiceConfig, DomainError>;
86}