rtb_credentials/reference.rs
1//! Config-serialisable reference to a credential.
2
3use secrecy::SecretString;
4use serde::Deserialize;
5
6/// Declarative reference to a credential that the [`Resolver`] walks
7/// through the documented precedence chain:
8/// `env` > `keychain` > `literal` > `fallback_env`.
9///
10/// Downstream tools carry this in their config structs, e.g.
11///
12/// ```
13/// use rtb_credentials::CredentialRef;
14///
15/// #[derive(serde::Deserialize)]
16/// struct AnthropicCfg {
17/// api: CredentialRef,
18/// }
19/// ```
20///
21/// [`Resolver`]: crate::resolver::Resolver
22/// `Serialize` is deliberately **not** derived: `SecretString` does
23/// not implement `Serialize` (secrecy crate removed it to prevent
24/// blind round-trip leaks). Tools writing credentials to config
25/// should go through a dedicated "write secret" path that redacts
26/// or skips the literal.
27#[derive(Debug, Clone, Default, Deserialize)]
28#[serde(deny_unknown_fields)]
29pub struct CredentialRef {
30 /// Name of an environment variable carrying the secret.
31 /// Checked first.
32 #[serde(default)]
33 pub env: Option<String>,
34
35 /// OS-keychain lookup descriptor. Checked second.
36 #[serde(default)]
37 pub keychain: Option<KeychainRef>,
38
39 /// Literal secret embedded in config. Checked third. Refused
40 /// when the process is running under `CI=true` (see
41 /// [`CredentialError::LiteralRefusedInCi`]).
42 ///
43 /// Note: `SecretString` round-trips through serde with the
44 /// secrecy crate's `serde` feature. The raw value is zeroed on
45 /// drop and redacted in `Debug`.
46 ///
47 /// [`CredentialError::LiteralRefusedInCi`]: crate::error::CredentialError::LiteralRefusedInCi
48 #[serde(default)]
49 pub literal: Option<SecretString>,
50
51 /// Name of an ecosystem-default env var used as a last-chance
52 /// fallback (e.g. `ANTHROPIC_API_KEY`). Checked last.
53 #[serde(default)]
54 pub fallback_env: Option<String>,
55}
56
57/// Reference to an entry in an OS keychain.
58#[derive(Debug, Clone, Deserialize, serde::Serialize)]
59#[serde(deny_unknown_fields)]
60pub struct KeychainRef {
61 /// Keychain "service" / collection name — typically the tool's
62 /// name (`mytool`) or a provider identifier (`anthropic`).
63 pub service: String,
64 /// Account / username component — typically the user's login or
65 /// an API-provider identifier (`default`).
66 pub account: String,
67}