feat(unseal): add unseal protocol and crypto infrastructure

This commit is contained in:
hdbg
2026-02-11 13:31:49 +01:00
parent 8dd0276185
commit bbbb4feaa0
18 changed files with 1323 additions and 88 deletions

View File

@@ -1,5 +1,11 @@
use std::sync::Arc;
use diesel::{Connection as _, SqliteConnection, connection::SimpleConnection as _};
use diesel_async::sync_connection_wrapper::SyncConnectionWrapper;
use diesel_async::{
AsyncConnection, SimpleAsyncConnection as _,
pooled_connection::{AsyncDieselConnectionManager, ManagerConfig, RecyclingMethod},
sync_connection_wrapper::SyncConnectionWrapper,
};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
use miette::Diagnostic;
use thiserror::Error;
@@ -7,10 +13,12 @@ use thiserror::Error;
pub mod models;
pub mod schema;
pub type Database = SyncConnectionWrapper<SqliteConnection>;
pub type DatabaseConnection = SyncConnectionWrapper<SqliteConnection>;
pub type DatabasePool = diesel_async::pooled_connection::bb8::Pool<DatabaseConnection>;
pub type PoolInitError = diesel_async::pooled_connection::PoolError;
pub type PoolError = diesel_async::pooled_connection::bb8::RunError;
static ARBITER_HOME: &'static str = ".arbiter";
static DB_FILE: &'static str = "db.sqlite";
static DB_FILE: &'static str = "arbiter.sqlite";
const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
@@ -18,7 +26,7 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
pub enum DatabaseSetupError {
#[error("Failed to determine home directory")]
#[diagnostic(code(arbiter::db::home_dir_error))]
HomeDir(Option<std::io::Error>),
HomeDir(std::io::Error),
#[error(transparent)]
#[diagnostic(code(arbiter::db::connection_error))]
@@ -31,27 +39,22 @@ pub enum DatabaseSetupError {
#[error(transparent)]
#[diagnostic(code(arbiter::db::migration_error))]
Migration(Box<dyn std::error::Error + Send + Sync>),
#[error(transparent)]
#[diagnostic(code(arbiter::db::pool_error))]
Pool(#[from] PoolInitError),
}
fn database_path() -> Result<std::path::PathBuf, DatabaseSetupError> {
let home_dir = std::env::home_dir().ok_or_else(|| DatabaseSetupError::HomeDir(None))?;
let arbiter_home = home_dir.join(ARBITER_HOME);
let arbiter_home = arbiter_proto::home_path().map_err(DatabaseSetupError::HomeDir)?;
let db_path = arbiter_home.join(DB_FILE);
std::fs::create_dir_all(arbiter_home)
.map_err(|err| DatabaseSetupError::HomeDir(Some(err)))?;
Ok(db_path)
}
fn setup_concurrency(conn: &mut SqliteConnection) -> Result<(), diesel::result::Error> {
// see https://fractaledmind.github.io/2023/09/07/enhancing-rails-sqlite-fine-tuning/
// sleep if the database is busy, this corresponds to up to 2 seconds sleeping time.
conn.batch_execute("PRAGMA busy_timeout = 2000;")?;
// better write-concurrency
conn.batch_execute("PRAGMA journal_mode = WAL;")?;
fn db_config(conn: &mut SqliteConnection) -> Result<(), diesel::result::Error> {
// fsync only in critical moments
conn.batch_execute("PRAGMA synchronous = NORMAL;")?;
// write WAL changes back every 1000 pages, for an in average 1MB WAL file.
@@ -60,24 +63,62 @@ fn setup_concurrency(conn: &mut SqliteConnection) -> Result<(), diesel::result::
// free some space by truncating possibly massive WAL files from the last run
conn.batch_execute("PRAGMA wal_checkpoint(TRUNCATE);")?;
// sqlite foreign keys are disabled by default, enable them for safety
conn.batch_execute("PRAGMA foreign_keys = ON;")?;
// better space reclamation
conn.batch_execute("PRAGMA auto_vacuum = FULL;")?;
// secure delete, overwrite deleted content with zeros to prevent recovery
conn.batch_execute("PRAGMA secure_delete = ON;")?;
Ok(())
}
#[tracing::instrument]
pub fn connect() -> Result<Database, DatabaseSetupError> {
let database_url = format!(
"{}?mode=rwc",
database_path()?
.to_str()
.ok_or_else(|| DatabaseSetupError::HomeDir(None))?
);
let mut conn =
SqliteConnection::establish(&database_url).map_err(DatabaseSetupError::Connection)?;
fn initialize_database(url: &str) -> Result<(), DatabaseSetupError> {
let mut conn = SqliteConnection::establish(url).map_err(DatabaseSetupError::Connection)?;
setup_concurrency(&mut conn).map_err(DatabaseSetupError::ConcurrencySetup)?;
db_config(&mut conn).map_err(DatabaseSetupError::ConcurrencySetup)?;
conn.run_pending_migrations(MIGRATIONS)
.map_err(DatabaseSetupError::Migration)?;
Ok(SyncConnectionWrapper::new(conn))
Ok(())
}
pub async fn create_pool() -> Result<DatabasePool, DatabaseSetupError> {
let database_url = format!(
"{}?mode=rwc",
database_path()?
.to_str()
.expect("database path is not valid UTF-8")
);
initialize_database(&database_url)?;
let mut config = ManagerConfig::default();
config.custom_setup = Box::new(|url| {
Box::pin(async move {
let mut conn = DatabaseConnection::establish(url).await?;
// see https://fractaledmind.github.io/2023/09/07/enhancing-rails-sqlite-fine-tuning/
// sleep if the database is busy, this corresponds to up to 9 seconds sleeping time.
conn.batch_execute("PRAGMA busy_timeout = 9000;")
.await
.map_err(diesel::ConnectionError::CouldntSetupConfiguration)?;
// better write-concurrency
conn.batch_execute("PRAGMA journal_mode = WAL;")
.await
.map_err(diesel::ConnectionError::CouldntSetupConfiguration)?;
Ok(conn)
})
});
let pool = DatabasePool::builder().build(AsyncDieselConnectionManager::new_with_config(
database_url,
config,
)).await?;
Ok(pool)
}