Skip to content

Why Linux keychain storage is session-scoped by default

On macOS and Windows the platform keychain persists across reboots and this crate uses it without ceremony. On Linux the default backend is kernel keyutils, whose entries are scoped to the session and disappear when it ends. Reboot-persistent Linux storage requires opting in to the linux-persistent feature.

That asymmetry is a build-time decision showing through at runtime.

The choice is between two kinds of cost

Linux has no single system keychain. The two realistic backends behind the keyring crate are:

Kernel keyutils. Pure Rust talking to a kernel facility that is always present. No system libraries, no daemon, no build dependencies. Its keys live and die with the session.

freedesktop Secret Service. The thing GNOME Keyring and KWallet implement, reached over D-Bus. Secrets persist across reboots. It pulls in libdbus-sys, which links against system D-Bus and needs pkg-config and libdbus-1-dev on the build host.

Neither is free. One costs persistence; the other costs the ability to build anywhere without preparation.

Why the hermetic default won

The deciding factor is who pays, and when.

Making Secret Service the default means every consumer of this crate inherits a system-package requirement on their build host. Not their runtime — their build. A container image that compiles a tool depending on rtb-credentials would need libdbus-1-dev installed even if the tool never stores anything in a keychain. Cross-compilation gets harder, because now a C library has to be present for the target. CI images grow an apt step. Every one of those costs lands on people who may not use the keychain at all.

The failure is also a bad one to inherit. It appears at link time, from libdbus-sys rather than from anything recognisable, in a build that never mentioned D-Bus.

Choosing keyutils inverts that. The default build works everywhere with no system dependencies, and the cost falls only on the tools that actually need reboot-persistent Linux storage — which know they need it, and can install two packages deliberately.

There is a related benefit for security review. A crate whose default build links no C libraries is easier to reason about, and this one keeps #![forbid(unsafe_code)] over a dependency graph that stays pure Rust unless somebody opts out.

Session-scoped is less limiting than it sounds

For a long-running service the distinction rarely matters. The secret is loaded at startup, usually from an environment variable, and the process outlives the question.

It matters for an interactive tool on a Linux desktop, where a user stores a credential once and expects it to be there next week. That is the case linux-persistent exists for, and it is a minority of deployments rather than the common one — which is the other half of the argument for making it opt-in.

What the feature does and does not buy

Enabling linux-persistent adds the Secret Service backend at compile time. It does not make persistent storage available.

The backend still needs a D-Bus session bus and a running secret-service provider such as gnome-keyring or kwallet. Headless servers, containers and CI runners generally have none of these, and keychain calls fail there in exactly the same way whether or not the feature was compiled in. The feature makes persistence possible, not present.

Because Cargo features are additive across a build graph, one crate enabling it turns it on for every consumer of rtb-credentials in that build — and imposes the libdbus-1-dev build requirement on all of them. A workspace cannot have one member opt in privately.

The practical consequence for servers is that keychain storage is often the wrong tool on Linux regardless of feature flags, and an environment variable is both simpler and the crate's recommended default anyway.