Skip to main content

Module sensitive

Module sensitive 

Source
Expand description

Sensitive<T>: a secret-value wrapper that redacts Debug/Display, never serializes, and zeroizes its contents on drop (#1169).

Config structs across the workspace hold credential/key fields — an LLM provider’s bearer key, a wallet signer key, an HMAC challenge secret — as plain Strings today. A plain String field prints its raw value from a derived Debug impl, from any accidental {}/{:?} in a log line, and from a derived Serialize impl the moment the enclosing struct is ever serialized (a debug endpoint, a forensics dump, a stray serde_json::to_string). Sensitive<T> closes all three holes at the type level: it only ever prints Sensitive(<redacted>), it deliberately has no Serialize impl (so a struct that embeds one fails to compile if something tries to derive Serialize over it, rather than silently leaking), and its value is wiped from memory as soon as it drops.

Reads the secret back out only through the explicit expose / expose_secret accessors (identical; expose_secret matches the naming the secrecy crate uses, so call sites read the same regardless of which wrapper backs them) — every use site is grep-able and visibly intentional.

§Why a local newtype instead of secrecy::SecretString

The secrecy crate (already resolved transitively in this workspace, via kube-client) redacts Debug and zeroizes on drop, but deliberately does not implement Display — printing a secret via {} is exactly the footgun it exists to prevent, so it forces every read through expose_secret(). Issue #1169 asks for a redacted Display too (so a stray format!("{secret}") — not just {:?} — still can’t leak), and for the wrapper to print as Sensitive(<redacted>) specifically. Bridging that gap by wrapping SecretString in another newtype would add a layer of indirection with no upside over implementing the same zeroize-on-drop + redacted-formatting contract directly against the zeroize crate, which this workspace already depends on (crates/passkey). A thin local type also stays generic over any T: Zeroize (not just String), so it can wrap a future non-String secret (e.g. raw key bytes) without another wrapper.

Structs§

Sensitive
A secret value, redacted in Debug/Display and zeroized on drop.