appcore_provider/secret.rs
1// =============================================================================
2// #######
3// ### ### F: secret.rs
4// ## ## ## ## P: AppCore-Runtime
5// ## ##
6// C: 2026/07/22 15:41:18 by dnettoRaw
7// ## ## ## ## U: 2026/07/24 16:07:49 by dnettoRaw
8// ########### S: 1.0.1-rc.8
9// =============================================================================
10
11use crate::{ProviderError, ProviderResult};
12use appcore_contracts::SecretRef;
13use std::fmt::{Debug, Formatter};
14use zeroize::Zeroizing;
15
16/// Secret material resolved at deployment time.
17pub struct ResolvedSecret(Zeroizing<String>);
18
19impl ResolvedSecret {
20 /// Wraps secret material so it is redacted from debug output and zeroized on drop.
21 pub fn new(value: impl Into<String>) -> ProviderResult<Self> {
22 let value = value.into();
23 if value.trim().is_empty() {
24 return Err(ProviderError::SecretUnavailable(
25 "resolved value is empty".to_string(),
26 ));
27 }
28 Ok(Self(Zeroizing::new(value)))
29 }
30
31 /// Borrows the secret only for immediate provider construction.
32 pub fn expose(&self) -> &str {
33 self.0.as_str()
34 }
35
36 /// Transfers ownership without creating an intermediate plain `String`.
37 pub fn into_zeroizing(self) -> Zeroizing<String> {
38 self.0
39 }
40}
41
42impl Debug for ResolvedSecret {
43 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
44 formatter.write_str("ResolvedSecret(REDACTED)")
45 }
46}
47
48/// Provider contract for resolving deployment secrets without embedding values in manifests.
49pub trait SecretProvider: Send + Sync {
50 /// Resolves one external secret reference.
51 fn resolve(&self, reference: &SecretRef) -> ProviderResult<ResolvedSecret>;
52}