Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libsql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ sync = [
"stream",
"remote",
"replication",
"dep:base64",
"dep:tower",
"dep:hyper",
"dep:http",
Expand Down Expand Up @@ -131,6 +132,7 @@ hrana = [
serde = ["dep:serde"]
remote = [
"hrana",
"dep:base64",
"dep:tower",
"dep:hyper",
"dep:hyper",
Expand Down
83 changes: 83 additions & 0 deletions libsql/examples/encryption_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Example of using offline writes with encryption

use libsql::{params, Builder};
use libsql::{EncryptionContext, EncryptionKey};

#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();

// The local database path where the data will be stored.
let db_path = std::env::var("LIBSQL_DB_PATH").unwrap();

// The remote sync URL to use.
let sync_url = std::env::var("LIBSQL_SYNC_URL").unwrap();

// The authentication token for the remote sync server.
let auth_token = std::env::var("LIBSQL_AUTH_TOKEN").unwrap_or("".to_string());

// Optional encryption key for the database, if provided.
let encryption = if let Ok(key) = std::env::var("LIBSQL_ENCRYPTION_KEY") {
Some(EncryptionContext {
key: EncryptionKey::Base64Encoded(key),
})
} else {
None
};

let db_builder =
Builder::new_synced_database(db_path, sync_url, auth_token).remote_encryption(encryption);

let db = match db_builder.build().await {
Ok(db) => db,
Err(error) => {
eprintln!("Error connecting to remote sync server: {}", error);
return;
}
};

let conn = db.connect().unwrap();

print!("Syncing with remote database...");
db.sync().await.unwrap();
println!(" done");

let mut results = conn.query("SELECT count(*) FROM dummy", ()).await.unwrap();
let count: u32 = results.next().await.unwrap().unwrap().get(0).unwrap();
println!("dummy table has {} entries", count);

conn.execute(
r#"
CREATE TABLE IF NOT EXISTS guest_book_entries (
text TEXT
)"#,
(),
)
.await
.unwrap();

let mut input = String::new();
println!("Please write your entry to the guestbook:");
match std::io::stdin().read_line(&mut input) {
Ok(_) => {
println!("You entered: {}", input);
let params = params![input.as_str()];
conn.execute("INSERT INTO guest_book_entries (text) VALUES (?)", params)
.await
.unwrap();
}
Err(error) => {
eprintln!("Error reading input: {}", error);
}
}
db.sync().await.unwrap();
let mut results = conn
.query("SELECT * FROM guest_book_entries", ())
.await
.unwrap();
println!("Guest book entries:");
while let Some(row) = results.next().await.unwrap() {
let text: String = row.get(0).unwrap();
println!(" {}", text);
}
}
39 changes: 37 additions & 2 deletions libsql/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub use builder::Builder;
pub use libsql_sys::{Cipher, EncryptionConfig};

use crate::{Connection, Result};
#[cfg(any(feature = "remote", feature = "sync"))]
use base64::{engine::general_purpose, Engine};
use std::fmt;
use std::sync::atomic::AtomicU64;

Expand Down Expand Up @@ -100,6 +102,7 @@ enum DbType {
auth_token: String,
connector: crate::util::ConnectorService,
_bg_abort: Option<std::sync::Arc<crate::sync::DropAbort>>,
remote_encryption: Option<EncryptionContext>,
},
#[cfg(feature = "remote")]
Remote {
Expand All @@ -108,6 +111,7 @@ enum DbType {
connector: crate::util::ConnectorService,
version: Option<String>,
namespace: Option<String>,
remote_encryption: Option<EncryptionContext>,
},
}

Expand Down Expand Up @@ -214,7 +218,7 @@ cfg_replication! {
endpoint,
auth_token,
https,
encryption_config
encryption_config,
).await
}

Expand Down Expand Up @@ -524,7 +528,7 @@ cfg_remote! {
url: impl Into<String>,
auth_token: impl Into<String>,
connector: C,
version: Option<String>
version: Option<String>,
) -> Result<Self>
where
C: tower::Service<http::Uri> + Send + Clone + Sync + 'static,
Expand All @@ -544,6 +548,7 @@ cfg_remote! {
connector: crate::util::ConnectorService::new(svc),
version,
namespace: None,
remote_encryption: None
},
max_write_replication_index: Default::default(),
})
Expand Down Expand Up @@ -677,6 +682,7 @@ impl Database {
url,
auth_token,
connector,
remote_encryption,
..
} => {
use crate::{
Expand Down Expand Up @@ -708,6 +714,7 @@ impl Database {
connector.clone(),
None,
None,
remote_encryption.clone(),
),
read_your_writes: *read_your_writes,
context: db.sync_ctx.clone().unwrap(),
Expand All @@ -730,6 +737,7 @@ impl Database {
connector,
version,
namespace,
remote_encryption,
} => {
let conn = std::sync::Arc::new(
crate::hrana::connection::HttpConnection::new_with_connector(
Expand All @@ -738,6 +746,7 @@ impl Database {
connector.clone(),
version.as_ref().map(|s| s.as_str()),
namespace.as_ref().map(|s| s.as_str()),
remote_encryption.clone(),
),
);

Expand Down Expand Up @@ -781,3 +790,29 @@ impl std::fmt::Debug for Database {
f.debug_struct("Database").finish()
}
}

#[cfg(any(feature = "remote", feature = "sync"))]
#[derive(Debug, Clone)]
pub enum EncryptionKey {
/// The key is a base64-encoded string.
Base64Encoded(String),
/// The key is a byte array.
Bytes(Vec<u8>),
}

#[cfg(any(feature = "remote", feature = "sync"))]
impl EncryptionKey {
pub fn as_string(&self) -> String {
match self {
EncryptionKey::Base64Encoded(s) => s.clone(),
EncryptionKey::Bytes(b) => general_purpose::STANDARD.encode(b),
}
}
}

#[cfg(any(feature = "remote", feature = "sync"))]
#[derive(Debug, Clone)]
pub struct EncryptionContext {
/// The base64-encoded key for the encryption, sent on every request.
pub key: EncryptionKey,
}
Loading
Loading