Files
chanora/poc/secure-storage-spike/src/main.rs
T
EdisonJwa 50c95b61ad feat(poc/storage): add secure-storage spike (Linux)
Proof-of-concept proving the secure-storage exit criterion from
docs/architecture/proof-of-concept-plan.md §2:
  "Secret write/read/delete works through platform secure storage."

Implements a typed SecretStorageRepository trait per ADR-006
(SecureStore + per-platform adapters) and a Linux adapter (the only
adapter in PoC scope) that supports both equivalent Linux backends
per SysRS-053/SysRS-162: Secret Service (libsecret) and kernel
keyutils.

The audit test suite covers:
  SS-AUD-001  identity secret absent from local DB (raw file scan)
  SS-AUD-002  server password absent from local DB
  SS-AUD-003  secrets absent from logs (Secret newtype redaction)
  SS-AUD-005  failure returns safe typed error (NotFound)
  SS-AUD-006  delete removes entry
  SS-TC-003   Linux round-trip set/get/delete

Verified on 2026-05-13 against the local keyutils backend (cargo
test runs need 'keyctl session -' to provide a valid session
keyring under non-interactive shells, documented in the spike
README). The CLI driver additionally observed a real locked
gnome-keyring collection and exercised the typed-error → fallback
path live.

Surfaced finding for the decision register: DEC-013 does not pin
a Linux secure-storage backend policy. Both Secret Service and
keyutils are 'equivalent' per the requirements; production code
needs an owner ruling.

Out of scope: Windows DPAPI, macOS/iOS Keychain, Android Keystore,
SS-AUD-004 (covered by diagnostics-redaction spike), SS-AUD-007/008
(process / migration items).

Authority: PoC plan §2, ADR-006, SDD-078, SRS-091..095,
SysRS-158..162.
Not product code; not promoted into chanora_storage.
2026-05-14 12:26:23 +08:00

81 lines
2.8 KiB
Rust

//! CLI driver — exercises the full secret round-trip against the real
//! platform secure storage.
use std::process::ExitCode;
use secure_storage_spike::secret::Secret;
use secure_storage_spike::SecretStorageRepository;
use tracing::info;
const SERVICE: &str = "app.chanora.poc.secure-storage";
fn main() -> ExitCode {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
if let Err(e) = run() {
eprintln!("error: {e}");
return ExitCode::from(1);
}
ExitCode::SUCCESS
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(not(target_os = "linux"))]
{
eprintln!("This PoC currently only ships a Linux adapter.");
return Err("unsupported platform".into());
}
#[cfg(target_os = "linux")]
{
use secure_storage_spike::secret::linux::LinuxSecureStore;
// Try Secret Service first (preferred per ADR-006); fall back to
// keyutils if the default collection is locked or unavailable.
let store: Box<dyn SecretStorageRepository> = {
let ss = LinuxSecureStore::new(SERVICE);
let probe = ss.set("__chanora_probe__", &Secret::from_utf8("probe"));
match probe {
Ok(()) => {
let _ = ss.delete("__chanora_probe__");
info!(target: "spike", "using Secret Service backend");
Box::new(ss)
}
Err(e) => {
info!(target: "spike", "Secret Service unavailable ({e}); falling back to keyutils");
Box::new(LinuxSecureStore::new_keyutils(SERVICE))
}
}
};
let name = "identity.primary";
info!(target: "spike", "writing secret {}", name);
let original = Secret::from_utf8("MG0DAgeAAgEgAiAIXJBlj1hQbaH0Eq0DuLlCmH8bl+veTAO2");
store.set(name, &original)?;
info!(target: "spike", "reading back");
let round_trip = store.get(name)?;
assert_eq!(original, round_trip, "round-trip mismatch");
info!(target: "spike", "round-trip OK ({} bytes)", round_trip.len());
info!(target: "spike", "deleting");
store.delete(name)?;
match store.get(name) {
Err(secure_storage_spike::SecureStoreError::NotFound) => {
info!(target: "spike", "post-delete read correctly returned NotFound");
}
Ok(_) => return Err("secret still readable after delete".into()),
Err(e) => return Err(format!("unexpected error after delete: {e}").into()),
}
println!("OK — Linux Secret Service round-trip verified.");
Ok(())
}
}