secrecy

Wrapper types for secret values that zero memory on drop and resist accidental exposure

Library
Cargo
v0.10.3
577stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
53/100Fair
Development Activity44
Maintenance12
Community68
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture80
Code Quality78
Innovation74
Learning Curve75

secrecy is a small, no_std-friendly Rust crate providing the SecretBox<T> wrapper type for handling sensitive values such as passwords, cryptographic keys, access tokens, and other credentials. It forbids unsafe code and relies on the zeroize crate to guarantee that wrapped values are securely wiped from memory as soon as they are dropped, closing a common class of accidental-leak bugs.

Access to the inner value is only possible through the explicit ExposeSecret and ExposeSecretMut traits, which makes every point where a secret touches other code auditable at a glance. The crate ships SecretString and SecretSlice type aliases for the most common cases and an opt-in serde feature that allows deserializing secrets while deliberately withholding a Serialize impl unless the wrapped type explicitly opts in via the SerializableSecret marker trait.

What You Get

  • SecretBox<T> generic wrapper for any Zeroize-able value, plus SecretString and SecretSlice<T> aliases for the most common cases
  • ExposeSecret/ExposeSecretMut traits that make every secret access explicit and greppable in code review
  • Automatic, guaranteed zeroing of the wrapped value’s memory on Drop via the zeroize crate
  • A Debug impl that always prints [REDACTED] instead of the real value, preventing accidental exposure through logs or panics
  • Optional serde support that permits deserializing secrets while withholding a Serialize impl unless the type opts in via SerializableSecret

Common Use Cases

  • Wrapping passwords and API tokens parsed from config files or environment variables so they can’t be accidentally printed in logs
  • Holding cryptographic keys and other sensitive material in memory with a guarantee they’re wiped when no longer needed
  • Passing credentials through application layers (e.g. HTTP clients, database drivers) with an explicit, auditable expose_secret() call at the point of use
  • Deserializing secret values from configuration formats via serde without risking accidental re-serialization or exfiltration

Under The Hood

Architecture: secrecy is a single-file crate (src/lib.rs, ~350 lines) centered on one type, SecretBox<S: Zeroize + ?Sized>, which owns a Box<S> and implements Zeroize/Drop/ZeroizeOnDrop so the boxed value is wiped the moment it goes out of scope. Access is gated entirely behind the ExposeSecret/ExposeSecretMut traits rather than public fields, and SecretString/SecretSlice<T> are thin type aliases (SecretBox<str>/SecretBox<[S]>) with From impls for ergonomic construction from String/Vec<T>. A CloneableSecret marker trait gates which inner types may be cloned, and an optional serde feature adds Deserialize for any T: DeserializeOwned while withholding Serialize unless the type opts into the SerializableSecret marker — a deliberate asymmetry to prevent silent secret exfiltration.

Tech Stack: Pure Rust, #![no_std] plus extern crate alloc, with #![forbid(unsafe_code)] enforced at the crate root. The only mandatory dependency is zeroize (~1.6, default-features off, alloc feature), and serde (~1) is an optional, default-off dependency gated behind the serde cargo feature. It targets Rust 1.85+ (edition 2024) per Cargo.toml, though the README references an older MSRV of 1.60, suggesting the manifest was bumped more aggressively than the docs.

Code Quality: The crate has a single inline unit test (test_secret_string_from_str) covering SecretString::from_str round-tripping through expose_secret() — thin test coverage for a security-focused crate, though the type-level design (trait-gated access, forbid(unsafe_code)) does a lot of the safety enforcement at compile time rather than via runtime tests. Naming is consistent and each public item carries a doc comment; there is no explicit runtime error handling since the API surface is infallible except for the try_init_with constructor, which correctly propagates a generic Result<Self, E>.

API Design: The API is small and intentional: three or four public types/aliases and two access traits, so the entire surface can be learned in a few minutes from the crate-level doc comment. Requiring expose_secret()/expose_secret_mut() for every read adds minor call-site boilerplate but this is the crate’s explicit design goal (auditable secret access), not accidental friction. The Debug impl redacting output by default rather than requiring an opt-in is a strong ergonomic default that prevents the most common accidental-leak mistake (debug-printing a struct that contains a secret).

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search