Skip to main content

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}