Skip to main content

submilli_engine/runtime/
secrets.rs

1//! Runtime-side provider for `submilli:secrets`.
2
3use serde::{Deserialize, Serialize};
4use std::future::Future;
5use std::pin::Pin;
6
7/// Identifies the encryption key needed to decrypt a recorded secret.
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct SecretKeyId(pub String);
11
12/// The only secret value permitted in effect history. Ciphertext includes the
13/// encryption format's nonce and authentication data; it never contains a key.
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15pub struct EncryptedSecret {
16    pub key_id: SecretKeyId,
17    #[serde(with = "super::effects::binary")]
18    pub ciphertext: Vec<u8>,
19}
20
21/// Host-provided resolver for blueprint-declared secrets.
22///
23/// `Ok(None)` means the secret is not available to the script: undeclared,
24/// declared but missing, or otherwise absent. Infrastructure/configuration
25/// failures should be returned as `Err` so scripts cannot confuse them with
26/// ordinary absence.
27pub trait SecretProvider: Send + Sync {
28    fn get<'a>(
29        &'a self,
30        name: &'a str,
31    ) -> Pin<Box<dyn Future<Output = Result<Option<String>, String>> + Send + 'a>>;
32
33    /// Retrieve and encrypt before returning to the effect recorder. Implementations
34    /// must not include plaintext credentials in either the value or an error.
35    /// Direct-only providers fail closed when used with recorded execution.
36    fn get_encrypted<'a>(
37        &'a self,
38        _name: &'a str,
39    ) -> Pin<Box<dyn Future<Output = Result<Option<EncryptedSecret>, String>> + Send + 'a>> {
40        Box::pin(async { Err("secret provider does not support encrypted retrieval".into()) })
41    }
42
43    /// Decrypt outside effect recording, selecting the key by the envelope's ID.
44    /// A missing key is an error, never a reason to retrieve a different credential.
45    /// Errors must not include plaintext credentials.
46    fn decrypt(&self, _name: &str, _secret: &EncryptedSecret) -> Result<String, String> {
47        Err("secret provider does not support decryption".into())
48    }
49}
50
51#[derive(Default)]
52pub struct NoopSecretProvider;
53
54impl SecretProvider for NoopSecretProvider {
55    fn get_encrypted<'a>(
56        &'a self,
57        _name: &'a str,
58    ) -> Pin<Box<dyn Future<Output = Result<Option<EncryptedSecret>, String>> + Send + 'a>> {
59        Box::pin(async { Ok(None) })
60    }
61
62    fn get<'a>(
63        &'a self,
64        _name: &'a str,
65    ) -> Pin<Box<dyn Future<Output = Result<Option<String>, String>> + Send + 'a>> {
66        Box::pin(async { Ok(None) })
67    }
68}