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.

17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18pub enum SecretName {

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.

24    VoiceId,
25}
27impl SecretName {
28    pub const ALL: [SecretName; 3] = [SecretName::JevKey, SecretName::VoiceKey, SecretName::VoiceId];

The name an operator sets it under.

31    pub const fn operator_name(self) -> &'static str {
32        match self {
33            SecretName::JevKey => "TYPESAFE_API_KEY",
34            SecretName::VoiceKey => "ELEVENLABS_API_KEY",
35            SecretName::VoiceId => "ELEVENLABS_VOICE_ID",
36        }
37    }
38}
40impl fmt::Display for SecretName {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        f.write_str(self.operator_name())
43    }
44}

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.

48#[derive(Clone, PartialEq, Eq)]
49pub struct SecretValue(String);
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}

What a lookup found.

68#[derive(Clone, Debug, PartialEq, Eq)]
69pub enum Lookup {

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).

75    Present(SecretValue),
76}
78impl Lookup {

The value, if there is one. Callers that treat "empty" like "absent" say so by calling this.

80    pub fn present(self) -> Option<SecretValue> {
81        match self {
82            Lookup::Present(v) => Some(v),
83            Lookup::Absent | Lookup::Empty => None,
84        }
85    }

Classifies raw text from a store: the one place the rule is written.

88    pub fn of(raw: Option<&str>) -> Self {
89        match raw.map(str::trim) {
90            None => Lookup::Absent,
91            Some("") => Lookup::Empty,
92            Some(v) => Lookup::Present(SecretValue::new(v)),
93        }
94    }
95}

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.

102#[expect(async_fn_in_trait, reason = "a secret store may be a binding that is awaited, and a Worker's futures cannot be Send")]
103pub trait Secrets {
104    async fn get(&self, name: SecretName) -> Result<Lookup, StoreError>;
105}
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}