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

90 lines
3.6 KiB
Markdown

# 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`](../../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
```text
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.
```bash
# 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.