use rcman::{CredentialBackend, EncryptedFileBackend};
fn main() {
println!("๐ Encrypted File Storage Demo (Argon2id)\n");
println!("This demonstrates storing secrets WITHOUT using the OS keychain.");
println!("Perfect for Docker, CI/CD, and headless environments.\n");
let temp_dir = std::env::temp_dir().join("rcman_encrypted_demo");
std::fs::create_dir_all(&temp_dir).unwrap();
let credentials_path = temp_dir.join("credentials.enc.json");
if credentials_path.exists() {
std::fs::remove_file(&credentials_path).unwrap();
}
println!("๐ Step 1: Create encrypted store with password\n");
let password = "my_secure_password";
let backend = EncryptedFileBackend::with_password(credentials_path.clone(), password)
.expect("Failed to create encrypted backend");
println!(
" โ
Encrypted backend created at: {}",
credentials_path.display()
);
println!(" โฑ๏ธ Key derivation uses Argon2id (memory-hard = GPU resistant)\n");
println!("๐ Step 2: Store secrets\n");
backend.store("api_key", "sk-12345-secret-key").unwrap();
backend
.store("database_password", "super_secret_db_pass")
.unwrap();
backend
.store("jwt_secret", "my-jwt-signing-secret")
.unwrap();
println!(" โ
Stored: api_key, database_password, jwt_secret\n");
println!("๐ Step 3: Encrypted file format (v3 - Argon2)\n");
let file_content = std::fs::read_to_string(&credentials_path).unwrap();
let preview: serde_json::Value = serde_json::from_str(&file_content).unwrap();
println!(" {{\n \"version\": {},", preview["version"]);
println!(
" \"salt\": \"{}\",",
&preview["salt"].as_str().unwrap()[..24]
);
println!(" \"entries\": {{");
if let Some(entries) = preview["entries"].as_object() {
for key in entries.keys().take(1) {
println!(" \"{key}\": {{ \"nonce\": \"...\", \"ciphertext\": \"...\" }}, ...");
}
}
println!(" }}\n }}\n");
println!(" โน๏ธ Salt is stored plaintext (safe - prevents rainbow tables)");
println!(" โน๏ธ Entries are AES-256-GCM encrypted\n");
println!("๐ Step 4: Simulate app restart\n");
drop(backend);
println!(" Opening encrypted store with password...");
let backend2 = EncryptedFileBackend::with_password(credentials_path.clone(), password)
.expect("Failed to reopen");
let api_key = backend2.get("api_key").unwrap().unwrap();
let db_pass = backend2.get("database_password").unwrap().unwrap();
println!(" โ
Retrieved api_key: {}...", &api_key[..10]);
println!(" โ
Retrieved database_password: {}...\n", &db_pass[..10]);
println!("๐ Step 5: Wrong password detection\n");
let wrong_backend =
EncryptedFileBackend::with_password(credentials_path.clone(), "wrong_password").unwrap();
match wrong_backend.get("api_key") {
Ok(_) => println!(" โ This shouldn't happen!"),
Err(e) => println!(" โ
Correctly rejected wrong password: {e}"),
}
println!();
println!("๐ Step 6: List stored keys\n");
let keys = backend2.list_keys().unwrap();
println!(" Stored secrets:");
for key in &keys {
println!(" - {key}");
}
println!();
println!("๐งน Cleanup\n");
std::fs::remove_dir_all(&temp_dir).unwrap();
println!(" โ
Removed temp directory\n");
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!(" USAGE SUMMARY");
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n");
println!("For new projects, use the recommended with_password() API:\n");
println!(" use rcman::EncryptedFileBackend;");
println!();
println!(" let backend = EncryptedFileBackend::with_password(path, \"password\")?;");
println!(" backend.store(\"api_key\", \"secret_value\")?;");
println!(" let value = backend.get(\"api_key\")?;");
println!();
println!("The salt is automatically:");
println!(" โข Generated on first use");
println!(" โข Stored in the JSON file");
println!(" โข Read on subsequent opens");
println!();
println!("Security features:");
println!(" โข Argon2id (v3) for state-of-the-art key derivation");
println!(" โข AES-256-GCM authenticated encryption");
println!(" โข Random 16-byte salt per file");
println!(" โข Random 12-byte nonce per entry");
}