Conversation
00b6668 to
73d7abb
Compare
07cb20e to
0f79ad5
Compare
conversations/src/storage/db.rs
Outdated
| "INSERT OR REPLACE INTO identity (id, name, secret_key) VALUES (1, ?1, ?2)", | ||
| params![ | ||
| identity.get_name(), | ||
| identity.secret().DANGER_to_bytes().as_slice() |
There was a problem hiding this comment.
I guess since the whole db will be encrypted this is ok
There was a problem hiding this comment.
Yeah, it should be fine for now until there is shared logos module to help manage such credentials.
There was a problem hiding this comment.
nb: While there may be shared functionality these installation keys will need to be compartmentalized to a given instance.
There was a problem hiding this comment.
There is also a deprecation notice on DANGER_to_bytes which we may need to re-discuss.
The intention is that the conversations crate will eventually not have any access to key material, and all operations on secrets bytes would be localized to a single location such as crate crypto. This makes external reviews and safety checks straight forward by establishing an invariant that crates cannot have unsafe access to key material.
Storage poses a problem with this approach and will need to be need to be considered more fully.
There was a problem hiding this comment.
The improvement of key storage in a better place and DANGER_to_bytes is out of scope of this PR.
The target here is to implement MVP that can persist the necessary information.
A few more questions worth to be discussed before putting more efforts,
- how are such credentials should be handled in logos
- will the credentials be portable
There was a problem hiding this comment.
Pull request overview
Adds SQLite-backed persistence for conversation state, focusing initially on saving/loading the local identity, and exposes a new FFI entrypoint to open a context with an app-provided storage path.
Changes:
- Introduces a
ChatStorageSQLite layer with embedded migrations and identity save/load. - Extends
Contextto open from storage (load-or-create identity) and adds persistence tests. - Adds an FFI helper (
create_context_with_storage) plus basic shared FFI utilities (CResult,destroy_string).
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| storage/src/lib.rs | Re-exports rusqlite::Connection for domain crates to run migrations without depending on rusqlite directly. |
| storage/src/errors.rs | Adds StorageError::InvalidData for domain-level validation failures. |
| conversations/src/storage/types.rs | Adds IdentityRecord with zeroize support and a unit test. |
| conversations/src/storage/mod.rs | Introduces the conversations storage module wiring. |
| conversations/src/storage/migrations/001_initial_schema.sql | Creates initial schema with an identity table. |
| conversations/src/storage/migrations.rs | Adds embedded, ordered migration application with _migrations tracking. |
| conversations/src/storage/db.rs | Implements ChatStorage with identity save/load and a roundtrip test. |
| conversations/src/lib.rs | Wires new ffi and storage modules into the crate. |
| conversations/src/identity.rs | Adds Identity::from_secret constructor for restoring persisted identities. |
| conversations/src/ffi/utils.rs | Adds CResult and destroy_string utility for FFI. |
| conversations/src/ffi/mod.rs | Exposes the ffi::utils module. |
| conversations/src/errors.rs | Adds ChatError::Storage to propagate storage failures. |
| conversations/src/context.rs | Adds Context::open using storage; keeps new_with_name as in-memory; adds persistence test. |
| conversations/src/api.rs | Adds create_context_with_storage FFI entrypoint using a DB path. |
| conversations/Cargo.toml | Adds storage, zeroize, and tempfile dependencies. |
| Cargo.lock | Locks new dependencies. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[ffi_export] | ||
| pub fn create_context_with_storage( | ||
| name: repr_c::String, | ||
| db_path: repr_c::String, | ||
| ) -> CResult<repr_c::Box<ContextHandle>, repr_c::String> { |
There was a problem hiding this comment.
This file’s ABI notes indicate using out-parameters for returning non-trivial structs (e.g., installation_name, create_intro_bundle). create_context_with_storage returns a CResult<...> struct by value, which is inconsistent with that convention and may be ABI-problematic for Nim callers. Consider switching to an out-parameter result struct to match the established pattern.
There was a problem hiding this comment.
@osmaczko is this going to contribute to the issues with sret?
| fn from(record: IdentityRecord) -> Self { | ||
| let name = record.name.clone(); | ||
| let secret = PrivateKey::from(record.secret_key); | ||
| Identity::from_secret(name, secret) |
There was a problem hiding this comment.
impl From<IdentityRecord> for Identity clones record.name even though IdentityRecord is moved into the conversion. This adds an unnecessary allocation/copy; you can move the String out of the record directly instead of cloning it.
There was a problem hiding this comment.
Move name did not pass the compiler,
cannot move out of type `IdentityRecord`, which implements the `Drop` trait
| // ==================== Identity Operations ==================== | ||
|
|
||
| /// Saves the identity (secret key). | ||
| pub fn save_identity(&mut self, identity: &Identity) -> Result<(), StorageError> { |
There was a problem hiding this comment.
[Sand] Clearing up the language used in this library: Installation represent keys for this given instance, where Identity represents a group of installations tied to a set of Installations.
There was a problem hiding this comment.
Clear the Installation or identity terms used is out of scope for this PR.
We need to write the spec first, also there may need org level consensus on how such concept being shared or reused.
conversations/src/storage/db.rs
Outdated
| "INSERT OR REPLACE INTO identity (id, name, secret_key) VALUES (1, ?1, ?2)", | ||
| params![ | ||
| identity.get_name(), | ||
| identity.secret().DANGER_to_bytes().as_slice() |
There was a problem hiding this comment.
nb: While there may be shared functionality these installation keys will need to be compartmentalized to a given instance.
conversations/src/storage/db.rs
Outdated
| "INSERT OR REPLACE INTO identity (id, name, secret_key) VALUES (1, ?1, ?2)", | ||
| params![ | ||
| identity.get_name(), | ||
| identity.secret().DANGER_to_bytes().as_slice() |
There was a problem hiding this comment.
There is also a deprecation notice on DANGER_to_bytes which we may need to re-discuss.
The intention is that the conversations crate will eventually not have any access to key material, and all operations on secrets bytes would be localized to a single location such as crate crypto. This makes external reviews and safety checks straight forward by establishing an invariant that crates cannot have unsafe access to key material.
Storage poses a problem with this approach and will need to be need to be considered more fully.
| //! Storage module for persisting chat state. | ||
|
|
||
| mod db; | ||
| mod migrations; | ||
| pub(crate) mod types; | ||
|
|
||
| pub(crate) use db::ChatStorage; |
There was a problem hiding this comment.
This project (with the exception of double-ratchets) follows the Rust 2018 guidance - https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html
There was a problem hiding this comment.
I don't see why we should follow this "guidance".
I have randomly searched a few top repos in github, all of my searched repos includes mod.rs.
https://github.com/EvanLi/Github-Ranking/blob/master/Top100/Rust.md
| #[ffi_export] | ||
| pub fn create_context_with_storage( | ||
| name: repr_c::String, | ||
| db_path: repr_c::String, | ||
| ) -> CResult<repr_c::Box<ContextHandle>, repr_c::String> { |
There was a problem hiding this comment.
@osmaczko is this going to contribute to the issues with sret?
| pub struct CResult<T: ReprC, Err: ReprC> { | ||
| pub ok: Option<T>, | ||
| pub err: Option<Err>, | ||
| } |
There was a problem hiding this comment.
From a consistency POV, api.rs uses the safer_ffi to create unique types per each api_call. I'm not saying that is the best approach, however this does represent a different approach of using generic containers as return values.
There was a problem hiding this comment.
Exactly, that's why I put in ffi/utils, I hope it can be reused in other apis, so that we can have uniform apis and reused code.
| fn from(record: IdentityRecord) -> Self { | ||
| let name = record.name.clone(); | ||
| let secret = PrivateKey::from(record.secret_key); | ||
| Identity::from_secret(name, secret) |
0f79ad5 to
030ab47
Compare
What changes: