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.
3.6 KiB
3.6 KiB
Secure Storage Spike
Chanora proof-of-concept. Not product code.
| Field | Value |
|---|---|
| PoC name | secure-storage-spike |
| PoC plan | docs/architecture/proof-of-concept-plan.md §2 |
| Purpose | Prove platform secure storage behaviour |
| Exit criterion | "Secret write/read/delete works through platform secure storage" |
| Authority | ADR-006 (SecureStore trait + per-platform adapters), SDD-078, SRS-091..095, SysRS-158..162 |
| Audit refs | docs/security/secure-storage-audit-report.md SS-AUD-001..006 / SS-TC-003 |
What it proves
- A typed
SecretStorageRepositorytrait that the rest of the Rust core can depend on without backend leakage (SAD-067). - A Linux adapter selecting between two equivalent backends per
SysRS-162 ("Secret Service, libsecret, or equivalent"):
- Secret Service / libsecret (gnome-keyring, kwallet, KeePassXC, …).
- Kernel keyutils (
add_key(2)/request_key(2)) — used when no D-Bus session or unlocked Secret Service collection is available.
- A typed
SecureStoreErrorwithNotFound/Unavailable/Backendarms, satisfying SS-AUD-005 (safe error mapping). - A
Secretnewtype with redactingDebug/Displayand zero-on-drop, satisfying SS-AUD-003 defence-in-depth. - A miniature
LocalDatabaseRepository(rusqlite, bundled) that holds only references to secret names — proving the SAD-067 separation empirically (SS-AUD-001, SS-AUD-002).
Layout
secure-storage-spike/
src/
lib.rs # crate root + re-exports
secret.rs # SecretStorageRepository trait, Secret type, Linux adapter
sqlite_repo.rs # LocalDatabaseRepository (non-secret state only)
test_logger.rs # in-memory log capture for SS-AUD-003
main.rs # secure-storage-cli driver (SS round-trip)
tests/
audit.rs # SS-AUD-001/002/003/005/006 + SS-TC-003
Cargo.toml
Reproduce
Requires Rust stable (developed against 1.95). Linux only — non-Linux
adapters are out of scope for this spike. cargo needs network access
on first build.
# Run the audit test suite (uses kernel keyutils backend; hermetic).
keyctl session - cargo test --tests
# Run the CLI driver. It prefers Secret Service and falls back to
# keyutils if the default collection is locked. Either path satisfies
# SysRS-053 / SysRS-162.
keyctl session - cargo run --bin secure-storage-cli
Why
keyctl session -? The Linux kernel session keyring is inherited from the calling process. Cargo's test/run wrappers often inherit an expired or empty_seskeyring from non-interactive shells.keyctl session -creates a fresh session keyring before invoking the command, guaranteeing a valid backend for keyutils. Interactive desktop sessions normally do not need this wrapper.
Scope boundaries
This spike is intentionally narrow. Out of scope:
- Windows / macOS / iOS / Android adapters. SS-TC-001/002/004/005 remain unverified. The trait is shaped to accommodate them but no code is shipped here.
- SS-AUD-004 (diagnostic export redaction). Owned by the
diagnostics-redaction-spike. - SS-AUD-007 (per-platform documentation). Doc-only check; lives in the audit report itself.
- SS-AUD-008 (migration / import). No migration paths exist yet.
- Encryption at rest beyond what the platform backend provides.
- Constant-time secret comparison.
Secret::eqis best-effort. - Threading / async semantics. Trait is
Send + Syncbut no concurrent stress is exercised.
Verification log
See VERIFICATION.md in this directory.