Files
chanora/poc/secure-storage-spike
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
..

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 SecretStorageRepository trait 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 SecureStoreError with NotFound / Unavailable / Backend arms, satisfying SS-AUD-005 (safe error mapping).
  • A Secret newtype with redacting Debug / Display and 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 _ses keyring 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::eq is best-effort.
  • Threading / async semantics. Trait is Send + Sync but no concurrent stress is exercised.

Verification log

See VERIFICATION.md in this directory.