pub struct Credential { /* private fields */ }Expand description
A credential for one request: a typed, redacted secret.
Attach it with
HttpRequestOptions::credential,
addressed to a credential scope. Only the adapter whose
CredentialSlot has that scope sends it, placed where that adapter’s
API expects it:
Credential::api_keyis the provider’s own key. It replaces the key the provider was configured with.Credential::headeris any other authentication header, such as a gateway token (cf-aig-authorization,ocp-apim-subscription-key) or a third-party adapter’s own header. The secret is the whole header value.
use allama::ChatRequest;
use allama_http::{Credential, HttpRequestOptions};
let request = ChatRequest::new("gpt-5-mini").user("Hi").with_option(
HttpRequestOptions::new()
.credential("openai", Credential::api_key("sk-tenant-key"))
.credential("openai", Credential::header("cf-aig-authorization", "Bearer gw-token")),
);
assert!(!format!("{request:?}").contains("sk-tenant-key"));§Secrets stay in the process
Debug shows the kind and never the secret, and so does every error
allama-http writes about a credential. Serializing a credential writes
its kind and omits the secret, while deserializing one reads the secret:
the serde impl is asymmetric on purpose, so a credential can come from a
configuration file but is never written back out. The JSON form of
HttpRequestOptions does not take
credentials, because JSON options are exported and printed as given;
deserialize the credential and attach it typed.
use allama_http::Credential;
let credential: Credential =
serde_json::from_str(r#"{"kind": "api_key", "secret": "sk-SECRET"}"#).unwrap();
assert_eq!(credential.expose_secret(), "sk-SECRET");
assert_eq!(serde_json::to_string(&credential).unwrap(), r#"{"kind":"api_key"}"#);§No equality, no zeroing
A credential is not PartialEq: comparing
secrets is not something a request option needs, and a derived
comparison would short-circuit on the first differing byte. Compare
kinds, or compare expose_secret
yourself, in constant time if the comparison guards anything.
HttpRequestOptions, which holds
credentials, is not PartialEq either.
use allama_http::Credential;
let _ = Credential::api_key("a") == Credential::api_key("b");The secret is a plain String: it is not zeroed when dropped. Copies of
it already live in the request headers, the HTTP client and the TLS
buffers, which a Credential cannot reach, so zeroing only its own copy
would promise more than it delivers. Zeroing can be added later without
changing this API.
Implementations§
Source§impl Credential
impl Credential
Sourcepub fn api_key(secret: impl Into<String>) -> Self
pub fn api_key(secret: impl Into<String>) -> Self
The provider’s own API key.
The adapter places it as its API expects (a bearer token, an
x-api-key header, …), replacing the key it was configured with.
Sourcepub fn header(name: impl Into<String>, secret: impl Into<String>) -> Self
pub fn header(name: impl Into<String>, secret: impl Into<String>) -> Self
An authentication header named name, whose whole value is
secret.
For gateway tokens and third-party adapters’ own headers. A header
credential replaces every header of the same name the adapter set,
so it is sent once. Include any scheme in the secret: Cloudflare’s
gateway token is Bearer <token>.
Sourcepub fn kind(&self) -> &CredentialKind
pub fn kind(&self) -> &CredentialKind
What kind of credential this is.
Sourcepub fn expose_secret(&self) -> &str
pub fn expose_secret(&self) -> &str
The secret, for an adapter that places the credential itself
(CredentialPlacement::Custom), for example to sign a request.
Never log or record it.
Sourcepub fn unsupported(&self, scope: &str) -> Error
pub fn unsupported(&self, scope: &str) -> Error
The error for a credential an adapter cannot place: an authored
allama::Error::Configuration naming the kind and the scope,
never the secret.
An adapter that places its credential itself returns it for a kind it does not understand, addressed to its scope, instead of falling back to its configured key, which would bill the wrong account.
Trait Implementations§
Source§impl Clone for Credential
impl Clone for Credential
Source§impl Debug for Credential
impl Debug for Credential
Source§impl<'de> Deserialize<'de> for Credential
Reads {"kind": "api_key", "secret": ".."} or
{"kind": "header", "name": "..", "secret": ".."}. A serialized
credential has no secret, so it does not deserialize back.
impl<'de> Deserialize<'de> for Credential
Reads {"kind": "api_key", "secret": ".."} or
{"kind": "header", "name": "..", "secret": ".."}. A serialized
credential has no secret, so it does not deserialize back.
Source§fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
Source§impl Serialize for Credential
Writes the kind, never the secret (see Secrets stay in the
process).
impl Serialize for Credential
Writes the kind, never the secret (see Secrets stay in the process).