Proof-of-concept proving the SQLite-storage exit criterion from
docs/architecture/proof-of-concept-plan.md §2:
"Schema, migration, and repository pattern are demonstrated."
Also satisfies the SRS-089 acceptance criteria explicitly:
"Storage implementation uses an embedded local data store and
migration mechanism."
Implements:
- A forward-only Migrator over a fixed Migration list, tracking
the applied version via PRAGMA user_version. Each migration is
applied inside an IMMEDIATE transaction; rolled back on failure.
- Three canonical migrations (initial schema, add nickname,
add last_connected_at) demonstrating ALTER TABLE flows.
- A LocalDatabaseRepository implementing both BookmarkRepository
and SettingsRepository traits.
- Bookmark.identity_ref is a reference to a secret name, never
a secret value (cross-checked by the secure-storage spike's
SS-AUD-001/002 scans). This is the SAD-067 separation.
Test suite (11/11 PASS on 2026-05-13):
- migrator brings fresh DB to latest version
- migrator is idempotent (no-op when already current)
- migrator applies only pending versions (catch-up upgrade)
- migrator rejects out-of-order versions
- migrator rejects DB newer than known migrations (downgrade guard)
- failed migration rolls back atomically
- bookmark CRUD round-trip
- bookmark list ordered by recency
- bookmark UNIQUE(host, identity_ref) enforcement
- settings upsert + delete
- open creates file and persists across reopen
Surfaced finding for the decision register: DEC-013 does not pin a
SQLite crate. The PoC uses rusqlite with the bundled feature
(no system libsqlite3 dependency); production code needs an
owner ruling on rusqlite vs. sqlx vs. sea-orm.
Authority: PoC plan §2, SRS-089, SDD-077, SAD-067,
SysDes-033/036/049/091.
Not product code; not promoted into chanora_storage.
95 lines
3.4 KiB
Markdown
95 lines
3.4 KiB
Markdown
# Verification record — `sqlite-storage-spike`
|
|
|
|
## Result
|
|
|
|
PASS. The PoC exit criterion (from
|
|
`docs/architecture/proof-of-concept-plan.md` §2: "Schema, migration,
|
|
and repository pattern are demonstrated") is met.
|
|
|
|
## Environment
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Date | 2026-05-13 |
|
|
| Host OS | Linux (Arch, kernel 7.0.5-arch1-1, x86_64) |
|
|
| Rust toolchain | stable 1.95.0 |
|
|
| `rusqlite` | 0.32.1 (bundled SQLite) |
|
|
| `thiserror` | 2 |
|
|
|
|
## Reproduction
|
|
|
|
```bash
|
|
cargo test
|
|
cargo run --bin sqlite-storage-cli
|
|
```
|
|
|
|
## Test run
|
|
|
|
```
|
|
running 11 tests
|
|
test migrator_rejects_out_of_order_versions ... ok
|
|
test migrator_rejects_db_newer_than_known_migrations ... ok
|
|
test failed_migration_rolls_back_atomically ... ok
|
|
test bookmark_repo_crud_round_trip ... ok
|
|
test bookmark_repo_unique_host_identity_is_enforced ... ok
|
|
test settings_repo_upsert_and_delete ... ok
|
|
test bookmark_repo_list_orders_recent_first ... ok
|
|
test migrator_applies_only_pending_versions ... ok
|
|
test migrator_is_idempotent ... ok
|
|
test open_creates_file_and_persists_across_reopen ... ok
|
|
test migrator_brings_fresh_db_to_latest_version ... ok
|
|
|
|
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured;
|
|
0 filtered out; finished in 0.01s
|
|
```
|
|
|
|
## CLI run
|
|
|
|
```
|
|
INFO spike: opening /tmp/.tmp.../chanora.sqlite
|
|
INFO spike: applying migration v1 "initial bookmarks + settings"
|
|
INFO spike: applying migration v2 "add bookmarks.nickname"
|
|
INFO spike: applying migration v3 "add bookmarks.last_connected_at"
|
|
INFO spike: schema version = 3
|
|
INFO spike: inserted bookmark ids: 1, 2
|
|
|
|
Bookmarks (most-recently-connected first):
|
|
[1] Vigorous Pro @ cn.teamspeak.app (identity_ref=identity.primary,
|
|
last_connected_at=Some(1715000000))
|
|
[2] Local Lab @ ts.example.invalid (identity_ref=identity.lab,
|
|
last_connected_at=None)
|
|
|
|
Settings.audio.aec = Some("true")
|
|
|
|
OK — schema v3 loaded, repository round-trip verified.
|
|
```
|
|
|
|
## Coverage matrix
|
|
|
|
| Concern | Test(s) | Result |
|
|
|---|---|---|
|
|
| Schema present (SDD-077) | All tests open the schema | PASS |
|
|
| Migration mechanism exists (SRS-089) | `migrator_*` (6 tests) | PASS |
|
|
| Idempotency | `migrator_is_idempotent`, `open_creates_file_and_persists_across_reopen` | PASS |
|
|
| Partial-upgrade catch-up | `migrator_applies_only_pending_versions` | PASS |
|
|
| Out-of-order migrations rejected | `migrator_rejects_out_of_order_versions` | PASS |
|
|
| Downgrade-guard | `migrator_rejects_db_newer_than_known_migrations` | PASS |
|
|
| Atomic rollback on failure | `failed_migration_rolls_back_atomically` | PASS |
|
|
| Repository pattern (CRUD) | `bookmark_repo_crud_round_trip`, `settings_repo_upsert_and_delete` | PASS |
|
|
| Ordering invariants | `bookmark_repo_list_orders_recent_first` | PASS |
|
|
| Constraint enforcement | `bookmark_repo_unique_host_identity_is_enforced` | PASS |
|
|
| On-disk persistence | `open_creates_file_and_persists_across_reopen` | PASS |
|
|
| SAD-067 separation (no secrets in DB) | DTO design (`identity_ref` is a *name*); cross-checked in `secure-storage-spike` SS-AUD-001/002 | PASS by construction |
|
|
|
|
## What this spike does NOT validate
|
|
|
|
- Concurrent access from multiple processes or threads.
|
|
- WAL checkpoint behaviour under sustained writes.
|
|
- Database growth / vacuum policy.
|
|
- Encrypted-at-rest variants (SQLCipher / SEE).
|
|
- Behaviour with corrupted or partially-written DB files.
|
|
- Mobile-specific storage paths (Android scoped storage, iOS app
|
|
group containers).
|
|
- The eventual production choice of migration engine.
|
|
- Performance characteristics under realistic dataset sizes.
|