The credentials Whiskers' outbound adapters need, by name.
A name here is the one an operator sets it under on every backend: an environment variable
natively, a Worker secret on Cloudflare. Hosting differs in where the value is kept and how
protected it is (celld, for one, has no secret store and keeps vars in the fleet's bucket); that
is an adapter's property to state, not a different name to learn. The set is closed, so a key that
is not listed here cannot be asked for.
"Not set" and "set to nothing" are different facts (an operator who put an empty value in has made a mistake worth saying so, where an absent one is just not configured), so a lookup says which.
12use std::fmt;
14use crate::error::StoreError;
A credential Whiskers can be given.
The key for Jev (the guard and the reranker).
20 JevKey,
The key for the natural voice.
22 VoiceKey,
Which voice speaks. Not a credential in itself, but it is handled like one: it is never logged.
The name an operator sets it under.
A secret's value. Its Debug and Display say nothing, so it cannot reach a log by accident;
the only way to the text is expose, which is greppable.
51impl SecretValue { 52 pub fn new(value: impl Into<String>) -> Self { 53 Self(value.into()) 54 } 55 56 pub fn expose(&self) -> &str { 57 &self.0 58 } 59} 60 61impl fmt::Debug for SecretValue { 62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 63 write!(f, "SecretValue(..{} bytes..)", self.0.len()) 64 } 65}
Nothing is set under that name.
71 Absent,
Something is set and it is empty (or only whitespace).
73 Empty,
A value, with surrounding whitespace removed (a pasted key often brings a newline).
78impl Lookup {
The value, if there is one. Callers that treat "empty" like "absent" say so by calling this.
Classifies raw text from a store: the one place the rule is written.
Where credentials come from.
Contract: a name that was never set is Absent, one set to nothing is Empty, anything else is
Present and trimmed. A lookup changes nothing. Err means the store itself could not be asked,
which is not the same as the secret being absent.
107#[cfg(test)] 108mod tests { 109 use super::*; 110 111 #[test] 112 fn absent_empty_and_present_are_three_different_things() { 113 assert_eq!(Lookup::of(None), Lookup::Absent); 114 assert_eq!(Lookup::of(Some("")), Lookup::Empty); 115 assert_eq!(Lookup::of(Some(" \n")), Lookup::Empty); 116 assert_eq!(Lookup::of(Some(" k\n")), Lookup::Present(SecretValue::new("k"))); 117 assert!(Lookup::Empty.present().is_none() && Lookup::Absent.present().is_none()); 118 } 119 120 #[test] 121 fn a_secret_never_prints_itself() { 122 let s = SecretValue::new("hunter2"); 123 assert!(!format!("{s:?}").contains("hunter2")); 124 } 125 126 #[test] 127 fn the_operator_names_are_the_documented_ones() { 128 assert_eq!(SecretName::JevKey.to_string(), "TYPESAFE_API_KEY"); 129 assert_eq!(SecretName::VoiceKey.to_string(), "ELEVENLABS_API_KEY"); 130 assert_eq!(SecretName::VoiceId.to_string(), "ELEVENLABS_VOICE_ID"); 131 } 132}