A local-first private AI assistant for everyday use. Runs on-device models with encrypted P2P sync, and supports sharing chats publicly on ATProto.
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' into fix/streamline-db-conn

madclaws ef2d7c46 efa3ba67

+74 -27
+11
Cargo.lock
··· 3744 3744 checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" 3745 3745 dependencies = [ 3746 3746 "cc", 3747 + "openssl-sys", 3747 3748 "pkg-config", 3748 3749 "vcpkg", 3749 3750 ] ··· 4630 4631 checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 4631 4632 4632 4633 [[package]] 4634 + name = "openssl-src" 4635 + version = "300.5.5+3.5.5" 4636 + source = "registry+https://github.com/rust-lang/crates.io-index" 4637 + checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" 4638 + dependencies = [ 4639 + "cc", 4640 + ] 4641 + 4642 + [[package]] 4633 4643 name = "openssl-sys" 4634 4644 version = "0.9.112" 4635 4645 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4637 4647 dependencies = [ 4638 4648 "cc", 4639 4649 "libc", 4650 + "openssl-src", 4640 4651 "pkg-config", 4641 4652 "vcpkg", 4642 4653 ]
+18
tilekit/src/accounts.rs
··· 46 46 Ok(signing_key.to_bytes()) 47 47 } 48 48 49 + pub fn create_and_save_passkey(app: &str, key: &str) -> Result<String> { 50 + let rand_bytes = get_random_bytes_32(); 51 + let rand_hex: String = rand_bytes.iter().map(|b| format!("{:02x}", b)).collect(); 52 + let entry = Entry::new(app, key)?; 53 + entry.set_secret(rand_bytes.as_slice())?; 54 + Ok(rand_hex) 55 + } 56 + 57 + pub fn get_passkey(app: &str, key: &str) -> Result<String> { 58 + let entry = Entry::new(app, key)?; 59 + let secret = entry.get_secret()?; 60 + Ok(to_hex(secret.as_slice())) 61 + } 62 + 63 + fn to_hex(bytes: &[u8]) -> String { 64 + bytes.iter().map(|b| format!("{:02x}", b)).collect() 65 + } 66 + 49 67 pub fn get_public_key_from_did(did: &str) -> Result<[u8; 32]> { 50 68 let ed_did = Ed25519Did::from_str(did)?; 51 69 Ok(ed_did.0.to_bytes())
+1 -1
tiles/Cargo.toml
··· 17 17 rustyline = "17.0" 18 18 toml = "1.0.3" 19 19 semver = "1.0" 20 - rusqlite = { version = "0.38.0", features = ["bundled"] } 20 + rusqlite = { version = "0.38.0", features = ["bundled-sqlcipher-vendored-openssl"] } 21 21 rusqlite_migration = "2.4.1" 22 22 uuid = {version = "1.21.0", features = ["v7"]} 23 23 axum = "0.8.8"
+2 -2
tiles/src/commands/mod.rs
··· 339 339 } 340 340 341 341 pub fn show_peers() -> Result<()> { 342 - let db_conn = get_db_conn(core::storage::db::DBTYPE::COMMON)?; 342 + let db_conn = get_db_conn(&core::storage::db::DBTYPE::COMMON)?; 343 343 344 344 let peers = get_peer_list(&db_conn)?; 345 345 ··· 351 351 } 352 352 353 353 pub fn unlink_peer(user_id: &str) -> Result<()> { 354 - let db_conn = get_db_conn(core::storage::db::DBTYPE::COMMON)?; 354 + let db_conn = get_db_conn(&core::storage::db::DBTYPE::COMMON)?; 355 355 356 356 if let Err(err) = unlink(&db_conn, user_id) { 357 357 println!("{:?}", err)
+4 -12
tiles/src/core/accounts.rs
··· 321 321 322 322 fn create_root_user(root_user_config: &Table, nickname: Option<String>) -> Result<Table> { 323 323 let mut root_user_table = root_user_config.clone(); 324 - let app_name = if cfg!(debug_assertions) { 325 - "tiles_dev" 326 - } else { 327 - "tiles" 328 - }; 329 - match create_identity(app_name) { 324 + let app_name = get_app_name(); 325 + match create_identity(&app_name) { 330 326 Ok(did) => { 331 327 root_user_table.insert("id".to_owned(), toml::Value::String(did)); 332 328 if let Some(nickname) = nickname { ··· 401 397 } 402 398 403 399 pub fn get_app_secret_key(did: &str) -> Result<SecretKey> { 404 - let app_name = if cfg!(debug_assertions) { 405 - "tiles_dev" 406 - } else { 407 - "tiles" 408 - }; 409 - let signing_key = get_secret_key(app_name, did)?; 400 + let app_name = get_app_name(); 401 + let signing_key = get_secret_key(&app_name, did)?; 410 402 Ok(SecretKey::from_bytes(&signing_key)) 411 403 } 412 404
+1 -1
tiles/src/core/chats.rs
··· 227 227 let (tx, mut rx) = mpsc::channel::<SyncOp>(32); 228 228 229 229 tokio::spawn(async move { 230 - let mut chat_db_conn = get_db_conn(super::storage::db::DBTYPE::CHAT)?; 230 + let mut chat_db_conn = get_db_conn(&super::storage::db::DBTYPE::CHAT)?; 231 231 info!("DB sync channel ready.."); 232 232 while let Some(msg) = rx.recv().await { 233 233 match msg {
+2 -2
tiles/src/core/network/mod.rs
··· 100 100 } 101 101 102 102 pub async fn link(ticket: Option<String>) -> Result<()> { 103 - let user_db_conn = get_db_conn(DBTYPE::COMMON)?; 103 + let user_db_conn = get_db_conn(&DBTYPE::COMMON)?; 104 104 let user = get_current_user(&user_db_conn)?; 105 105 let endpoint = create_endpoint(&user).await?; 106 106 let is_online = is_online(&endpoint).await; ··· 419 419 } 420 420 421 421 pub async fn sync(did: Option<String>) -> Result<()> { 422 - let user_db_conn = get_db_conn(DBTYPE::COMMON)?; 422 + let user_db_conn = get_db_conn(&DBTYPE::COMMON)?; 423 423 let user = get_current_user(&user_db_conn)?; 424 424 let endpoint = create_endpoint(&user).await?; 425 425 let is_online = is_online(&endpoint).await;
+25 -7
tiles/src/core/storage/db.rs
··· 6 6 use std::path::PathBuf; 7 7 8 8 use anyhow::{Result, anyhow}; 9 + use log::info; 9 10 use rusqlite::Connection; 11 + use tilekit::accounts::{create_and_save_passkey, get_passkey}; 10 12 11 - use crate::utils::config::{ConfigProvider, DefaultProvider}; 13 + use crate::utils::config::{ConfigProvider, DefaultProvider, get_app_name}; 12 14 use rusqlite_migration::{M, Migrations}; 15 + 16 + #[derive(Debug)] 13 17 pub enum DBTYPE { 14 18 COMMON, 15 19 CHAT, ··· 73 77 const CHATS_MIGRATIONS: Migrations = Migrations::from_slice(CHATS_MIGRATION_ARRAY); 74 78 75 79 pub fn init_db() -> Result<Dbconn> { 76 - let mut chat_conn = get_db_conn(DBTYPE::CHAT)?; 77 - let mut common_conn = get_db_conn(DBTYPE::COMMON)?; 80 + let mut chat_conn = get_db_conn(&DBTYPE::CHAT)?; 81 + let mut common_conn = get_db_conn(&DBTYPE::COMMON)?; 78 82 79 83 apply_migrations(&mut common_conn, &mut chat_conn)?; 80 84 ··· 84 88 }) 85 89 } 86 90 87 - pub fn get_db_conn(db_type: DBTYPE) -> Result<Connection> { 91 + pub fn get_db_conn(db_type: &DBTYPE) -> Result<Connection> { 88 92 let db_path = get_db_path(db_type)?; 89 93 let conn = Connection::open(db_path) 90 94 .map_err(|e| anyhow!("Failed to create db connection due to {:?}", e))?; 91 95 96 + let passkey = fetch_passkey()?; 97 + let cipher_format = format!("x'{}'", passkey); 98 + conn.pragma_update(None, "KEY", cipher_format)?; 92 99 conn.pragma_update(None, "journal_mode", "WAL")?; 100 + info!("DB {:?} Opened", db_type); 93 101 Ok(conn) 94 102 } 95 103 ··· 99 107 .map_err(<rusqlite_migration::Error as Into<anyhow::Error>>::into)?; 100 108 CHATS_MIGRATIONS.to_latest(chat_conn).map_err(|e| e.into()) 101 109 } 102 - fn get_db_path(db_type: DBTYPE) -> Result<PathBuf> { 110 + fn get_db_path(db_type: &DBTYPE) -> Result<PathBuf> { 103 111 let user_data_dir = DefaultProvider.get_user_data_dir()?; 104 112 match db_type { 105 - DBTYPE::COMMON => Ok(user_data_dir.join("common.db")), 106 - DBTYPE::CHAT => Ok(user_data_dir.join("chats.db")), 113 + DBTYPE::COMMON => Ok(user_data_dir.join("common_v2.db")), 114 + DBTYPE::CHAT => Ok(user_data_dir.join("chats_v2.db")), 115 + } 116 + } 117 + 118 + fn fetch_passkey() -> Result<String> { 119 + let app_name = get_app_name(); 120 + if let Ok(passkey) = get_passkey(&app_name, "db_passkey") { 121 + Ok(passkey) 122 + } else { 123 + info!("DB passkey not found, creating one.."); 124 + create_and_save_passkey(&app_name, "db_passkey") 107 125 } 108 126 } 109 127
+2 -2
tiles/src/runtime/mlx.rs
··· 255 255 .ok_or_else(|| anyhow!("Error getting FROM from modelfile due to"))?; 256 256 257 257 println!("Running {} in interactive mode", modelname); 258 - let common_db_conn = get_db_conn(crate::core::storage::db::DBTYPE::COMMON)?; 259 - let chat_db_conn = get_db_conn(crate::core::storage::db::DBTYPE::CHAT)?; 258 + let common_db_conn = get_db_conn(&crate::core::storage::db::DBTYPE::COMMON)?; 259 + let chat_db_conn = get_db_conn(&crate::core::storage::db::DBTYPE::CHAT)?; 260 260 let current_user = get_current_user(&common_db_conn)?; 261 261 262 262 let config = Config::builder().auto_add_history(true).build();
+8
tiles/src/utils/config.rs
··· 298 298 Ok(model_dir) 299 299 } 300 300 301 + pub fn get_app_name() -> String { 302 + if cfg!(debug_assertions) { 303 + "tiles_dev".to_owned() 304 + } else { 305 + "tiles".to_owned() 306 + } 307 + } 308 + 301 309 //TODO: Add more tests for config.toml 302 310 #[cfg(test)] 303 311 mod tests {