Implements issue #112: MLS group state must survive process restarts. The MLS
provider hardcoded an in-memory store separate from the chat store, so a restart
lost every group. Fold the OpenMLS StorageProvider into libchat's own ChatStore
so one durable store holds both chat state and MLS group state.
- storage: ChatStore subsumes StorageProvider<CURRENT_VERSION>, so a ChatStore is
the whole durable-storage contract (identity/ephemeral/conversation/ratchet
sub-stores plus the MLS key-value surface).
- chat-sqlite: ChatStorage implements StorageProvider over an mls_kv table
(migration 003_mls_storage), a byte-faithful port of MemoryStorage with decode
errors surfaced instead of unwrapped and the reference clear_proposal_queue
orphan-key bug fixed. The separate SqliteMlsStorage type is removed.
- conversations: MlsPqProvider becomes a transient view borrowing the shared
store and a long-lived crypto backend, so the store stays singly owned;
ServiceContext hands it out via mls_provider(). ExternalServices drops its ST
associated type, leaving one CS. GroupV2/de_mls's thread-safe-error bound is
restated on CS because an associated-type bound does not elaborate through a
supertrait.
- client: the store is always ChatStorage, so fix it as such and drop the
vestigial store generic from ChatClient and the builder. Retire the stubbed
in-memory MemStore in favour of ChatStorage::in_memory().
- GroupV1 conversations resume across a restart through the existing
MlsGroup::load path, proven by a drop-and-reload integration test.