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