I'm thrilled to announce the release of C5Store 0.4.3, a major update to my Rust configuration management library. This release perfects a powerful new feature I'm calling Transparent Secret Deserialization and follows closely on the heels of another critical addition for production environments: first-class support for systemd Credentials.
You can grab the latest version now on crates.io:cargo add c5store
For those new to the project, C5Store is a powerful, layered configuration library for Rust applications. It's designed to load settings from files, directories, and environment variables, giving you a single, unified view of your app's configuration. My primary goal for C5Store has always been to make secure configuration management not just possible, but easy.
These recent releases are a huge step toward that goal.
The Problem: The Dev vs. Prod Dilemma
In the real world, our configuration needs change depending on the environment. For local development, we want our config files to be simple, human-readable, and easy to edit. We want to see our database host and API keys right there in plaintext.
# config/local.yaml
database_url: "postgres://user:pass@localhost/mydb"
api_key: "dev-key-12345"
But in production, this is a massive security risk. Sensitive values must be encrypted. c5store
has always supported this with a clean, secure format:
# config/production.yaml
database_url:
.c5encval: ["ecies_x25519", "prod_db_key", "A1B2C3..."]
api_key:
.c5encval: ["ecies_x25519", "prod_api_key", "B4C5D6..."]
This creates a dilemma. If your application code expects a String
,
how does it handle a value that starts as an encrypted blob of bytes?
Previous solutions were often clunky, forcing you to change your code to
accommodate the production environment. Your application code shouldn't
have to care how a secret is stored.
The Solution (Part 1): Make it Transparent
With version 0.4.3, this dilemma is a thing of the past. C5Store now handles the decryption and type conversion completely automatically.
Let’s say this is the configuration struct your application needs:
use serde::Deserialize;
#[derive(Deserialize)]
struct AppConfig {
database_url: String,
api_key: String,
retries: u32,
}
You can now use this exact same struct for both your local development and your secure production environments without changing a single line of code.
When you call c5store.get_into_struct::<AppConfig>()
:
- In Development: C5Store reads
database_url
as aString
from yourlocal.yaml
and populates the struct. Simple. - In Production: C5Store sees the
.c5encval
block fordatabase_url
, finds the correct private key, decrypts the ciphertext into raw bytes, and recognizes that your struct needs aString
. It then safely converts those decrypted bytes into a UTF-8 string and populates your struct.
It just works.
The Solution (Part 2): Production-Grade Key Management with systemd
Of course, transparently decrypting secrets is only half the battle. You still need to securely provide the private decryption keys to your application in the first place. Storing them on disk relies on strict file permissions, and using environment variables can be risky.
For modern Linux services, the gold standard is systemd Credentials. And as of version 0.4.2, C5Store supports this mechanism out of the box.
Systemd Credentials allow you to securely deliver sensitive data—like
your C5Store private keys—to your service at runtime. The keys are
placed in a private, in-memory directory ($CREDENTIALS_DIRECTORY
) that only your service can access.
You can configure your myservice.service
file to use either a plaintext key file (secured with file permissions) or a file encrypted at rest with systemd-creds
, which is often tied to the machine's TPM chip.
# Example using a plaintext key, secured by permissions
[Service]
ExecStart=/path/to/my_rust_app
LoadCredential=my_app_master_key:/etc/my_app/keys/master.key
# Example using a key encrypted on disk for maximum at-rest security
[Service]
ExecStart=/path/to/my_rust_app
LoadCredentialEncrypted=my_app_master_key:/etc/my_app/keys/master.key.cred
In both cases, systemd delivers the plaintext key to your running application. C5Store then seamlessly loads it from the credentials directory. This is the most secure, robust, and idiomatic way to manage keys on a modern Linux system.
A Complete, Secure Workflow
With these features combined, C5Store now offers a complete, end-to-end secure configuration workflow:
- Manage your sensitive decryption keys with the robust security of systemd Credentials.
- Encrypt your application-level secrets (database URLs, API keys) using the
c5
CLI. - Define your application's configuration needs once with simple Rust structs.
- Run your application, confident that C5Store is transparently and securely handling the entire decryption and deserialization process for you.
This is a huge step forward for the library, and I'm excited to see how it simplifies and secures workflows for its users.
Check out C5Store on crates.io and give the latest version a try. I'd love to hear your feedback.