# SQLite Storage Spike

Chanora proof-of-concept. **Not product code.**

| Field | Value |
|---|---|
| PoC name | `sqlite-storage-spike` |
| PoC plan | [`docs/architecture/proof-of-concept-plan.md`](../../docs/architecture/proof-of-concept-plan.md) §2 |
| Purpose | Prove embedded local non-secret persistence |
| Exit criterion | "Schema, migration, and repository pattern are demonstrated" |
| Authority | SRS-089 (acceptance criteria: embedded data store + migration mechanism), SDD-077, SAD-067 |

## What it proves

- A **schema** managed entirely inside SQLite (`bookmarks`, `settings`)
  with a UNIQUE index, secondary indices, and `ON CONFLICT` upsert
  semantics for the settings table.
- A **forward-only migration mechanism** keyed on
  `PRAGMA user_version`. Migrations are applied atomically inside an
  IMMEDIATE transaction and rolled back on failure.
- A **repository pattern**: `BookmarkRepository` and
  `SettingsRepository` traits, implemented once by
  `LocalDatabaseRepository`. The rest of the Rust core can depend on
  the traits and avoid leaking SQLite-specific types upward
  (SAD-067, SDD-077).
- **Cross-spike contract**: `Bookmark.identity_ref` is a *name*, not a
  secret. The secret behind that name lives in
  `SecretStorageRepository` (see `secure-storage-spike`).

## Layout

```text
sqlite-storage-spike/
  src/
    lib.rs               # crate root, re-exports
    migrate.rs           # Migrator + canonical migrations (v1..v3)
    repo.rs              # traits, DTOs, LocalDatabaseRepository
    main.rs              # sqlite-storage-cli driver
  tests/
    storage.rs           # 11 tests — migration + repo + persistence
  Cargo.toml
```

## Canonical migrations

| Version | Description |
|---|---|
| 1 | Initial `bookmarks` table + UNIQUE index on (server_host, identity_ref); `settings` key/value table. |
| 2 | `ALTER TABLE bookmarks ADD COLUMN nickname TEXT`. |
| 3 | `ALTER TABLE bookmarks ADD COLUMN last_connected_at INTEGER` + descending index. |

Future migrations are appended only. The migrator refuses to start if
the DB's `user_version` exceeds the highest known migration (downgrade
guard).

## Reproduce

Requires Rust stable (developed against 1.95). `rusqlite` is built with
the `bundled` feature, so no system `libsqlite3` is needed.

```bash
cargo test
cargo run --bin sqlite-storage-cli
```

## Scope boundaries

- **Non-secret data only.** Any secret material (identity private
  keys, server passwords) belongs to `secure-storage-spike` /
  `SecretStorageRepository`. This is the SAD-067 separation.
- **Single-process access.** Connection pooling, multi-process
  contention, and concurrent transactions are not exercised here.
  Production code will likely run on a single Rust core worker.
- **No encryption at rest.** SQLCipher and similar are not in scope.
  Secrets must not land in this DB in the first place.
- **No backup / restore.**
- **No reverse migrations.** Forward-only is the documented policy;
  rollbacks require a fresh DB.
- The migration-engine choice (`refinery`, `sqlx::migrate!`, etc.)
  remains open for the product crate. The PoC's ~50-line custom
  mechanism is intentional, to keep the audit surface explainable.

## Verification log

See `VERIFICATION.md` in this directory.
