feat: persistent storage — SQLite backends for crash resilience

Shade sessions and keys now survive server crashes, container restarts,
and power outages via SQLite with WAL mode.

New packages:
- @shade/storage-sqlite: SQLiteStorage (StorageProvider) + SqlitePrekeyStore
  (PrekeyStore), both using bun:sqlite with auto-created tables and WAL mode
- Serialization layer in shade-core for SessionState/keys ↔ JSON/base64

Docker usage: mount volume at /data, set SHADE_DB_PATH=/data/shade-client.db
Prekey server auto-detects SHADE_PREKEY_DB_PATH for SQLite persistence

Includes crash recovery integration test: encrypt → close DB → reopen →
conversation continues seamlessly.

129 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 00:19:54 +02:00
parent d071551b2f
commit 7d214dc614
13 changed files with 1719 additions and 7 deletions

View File

@@ -1,10 +1,22 @@
import { createPrekeyServer } from './index.js';
import { createPrekeyRoutes } from './routes.js';
import type { PrekeyStore } from './store.js';
const server = createPrekeyServer();
async function createStore(): Promise<PrekeyStore> {
const dbPath = process.env.SHADE_PREKEY_DB_PATH;
if (dbPath) {
const { SqlitePrekeyStore } = await import('@shade/storage-sqlite');
console.log(`[Shade] Using SQLite prekey store: ${dbPath}`);
return new SqlitePrekeyStore(dbPath);
}
const { MemoryPrekeyStore } = await import('./memory-store.js');
console.log('[Shade] Using in-memory prekey store (data will not persist)');
return new MemoryPrekeyStore();
}
export default {
port: Number(process.env.PORT ?? 3900),
fetch: server.fetch,
};
const store = await createStore();
const server = createPrekeyRoutes(store);
const port = Number(process.env.PORT ?? 3900);
console.log(`Shade Prekey Server listening on port ${process.env.PORT ?? 3900}`);
export default { port, fetch: server.fetch };
console.log(`Shade Prekey Server listening on port ${port}`);