Files
Edison Jwa 40883c40c8 refactor: reduce map_err boilerplate with context helper methods (TODO-025)
Add per-crate variant_ctx() helper methods to StorageError, BlobCacheError,
ProtocolError, and BridgeError. Replace 78 .map_err(|e| format!(...))
closures with concise context calls. Identical error messages preserved.
2026-06-11 12:25:00 +09:00
..

chanora_storage

Two strictly separated storage concerns per SAD-067:

  1. BookmarkRepository — non-secret bookmark state via SQLite with optional encrypted password fields (rusqlite bundled, DEC-013.1).
  2. IdentityFileStore — Beta fallback storage for identity material while platform secure-storage backends mature.

Architecture

IdentityFileStore

  • Persists a single TS3 identity to <dir>/identity.tskey encrypted with ChaCha20-Poly1305.
  • The Data Encryption Key (DEK) is 32 random bytes stored in the platform keyring (Linux Secret Service, macOS Keychain, Windows Credential Manager, iOS Keychain) when available, with a best-effort file fallback at identity.dek (mode 0600 on Unix).
  • Legacy plaintext files from pre-Beta are still readable; the next save() upgrades them to encrypted form.
  • Audio metadata (transmit_mode, release_tail_ms, PTT binding) is persisted alongside as audio_meta.json (plaintext, non-secret).

BookmarkRepository

  • SQLite-backed store at <dir>/chanora.db.
  • Schema v1: basic bookmark columns. Schema v2: adds password_blob for encrypted passwords.
  • When constructed via with_crypto(), the password column is replaced by a ChaCha20-Poly1305 envelope under the same per-install DEK.
  • Legacy plaintext passwords are transparently read and upgraded on the next update().

Crypto abstraction

  • Crypto trait: encrypt(plaintext) / decrypt(blob) — callers see only the encrypt/decrypt pair.
  • DekCrypto — concrete implementation sharing the same per-install DEK with IdentityFileStore.

Public API Summary

Types

Type Role
IdentityFileStore Encrypted identity file store
BookmarkRepository SQLite bookmark store with optional password encryption
Bookmark Bookmark DTO: id, display_name, host, nickname, password
PttBindingMeta Persisted PTT binding metadata
StorageError NotFound, Migration, Sqlite, SecureStore, Io, Crypto
Crypto trait Encrypt/decrypt abstraction

IdentityFileStore methods

  • new(dir) — create or open store, ensure DEK exists
  • load()Option<String> — read identity (handles legacy plaintext)
  • save(identity) — persist encrypted (ChaCha20-Poly1305, atomic write)
  • clear() — remove identity file
  • crypto() — obtain a Crypto handle sharing the DEK
  • set_transmit_mode(mode) / get_transmit_mode() — audio settings persistence
  • set_release_tail_ms(ms) / get_release_tail_ms() — release-tail persistence
  • set_ptt_binding(...) / get_ptt_binding() — PTT binding persistence

BookmarkRepository methods

  • new(dir) / with_crypto(dir, crypto) — open (plain or encrypted)
  • add(bookmark)i64 — insert, return id
  • update(bookmark) — replace by id
  • delete(id) — remove by id
  • list()Vec<Bookmark> — all bookmarks ordered by id
  • upsert_or_add(bookmark) — insert or update by host, preserves user's display name
  • encrypts_passwords() — whether password encryption is active

Platform notes

  • Unix: files written with mode 0600.
  • Keyring access can be disabled via CHANORA_DISABLE_KEYRING=1 for tests/headless environments.
  • Android: file in app-private storage (not encrypted at rest — documented Beta gap).
  • iOS/Windows/macOS: caller provides the storage directory; platform sandbox handles access control.