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/Displayand zeroized on drop.