From f2136bb80903d5e6476832aa3e770271fe7ec348 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Tue, 4 Nov 2025 09:14:25 -0800 Subject: [PATCH] Start scaffolding hosting applications --- akd/Cargo.lock | 29 ++++++++++++++++ akd/Cargo.toml | 3 ++ akd/crates/aio/Cargo.toml | 19 +++++++++++ akd/crates/aio/src/main.rs | 41 ++++++++++++++++++++++ akd/crates/akd_storage/src/lib.rs | 2 ++ akd/crates/akd_test_utility/Cargo.toml | 2 +- akd/crates/common/Cargo.toml | 2 +- akd/crates/publisher/Cargo.toml | 5 ++- akd/crates/publisher/src/lib.rs | 22 ++++++++++++ akd/crates/publisher/src/main.rs | 47 +++++++++++++++++++++----- akd/crates/reader/Cargo.toml | 19 +++++++++++ akd/crates/reader/src/lib.rs | 21 ++++++++++++ akd/crates/reader/src/main.rs | 40 ++++++++++++++++++++++ 13 files changed, 241 insertions(+), 11 deletions(-) create mode 100644 akd/crates/aio/Cargo.toml create mode 100644 akd/crates/aio/src/main.rs create mode 100644 akd/crates/publisher/src/lib.rs create mode 100644 akd/crates/reader/Cargo.toml create mode 100644 akd/crates/reader/src/lib.rs create mode 100644 akd/crates/reader/src/main.rs diff --git a/akd/Cargo.lock b/akd/Cargo.lock index aa2655f8bf..da57b9834d 100644 --- a/akd/Cargo.lock +++ b/akd/Cargo.lock @@ -26,6 +26,19 @@ dependencies = [ "memchr", ] +[[package]] +name = "aio" +version = "0.1.0" +dependencies = [ + "akd_storage", + "common", + "publisher", + "reader", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "akd" version = "0.11.0" @@ -1476,8 +1489,11 @@ dependencies = [ name = "publisher" version = "0.1.0" dependencies = [ + "akd", "akd_storage", "async-std", + "bitwarden-akd-configuration", + "common", "tiberius", "tokio", "tokio-util", @@ -1530,6 +1546,19 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "reader" +version = "0.1.0" +dependencies = [ + "akd", + "akd_storage", + "bitwarden-akd-configuration", + "common", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "redox_syscall" version = "0.5.17" diff --git a/akd/Cargo.toml b/akd/Cargo.toml index 77407b0f1b..4c88f5bc85 100644 --- a/akd/Cargo.toml +++ b/akd/Cargo.toml @@ -16,6 +16,9 @@ unwrap_used = "deny" [workspace.dependencies] akd = "0.11.0" async-trait = "0.1.89" +akd_storage = { path = "crates/akd_storage" } +bitwarden-akd-configuration = { path = "crates/bitwarden-akd-configuration" } +common = { path = "crates/common" } config = "0.15.18" serde = { version = "1.0.228", features = ["derive"] } tokio = { version = "1.47.1", features = ["full"] } diff --git a/akd/crates/aio/Cargo.toml b/akd/crates/aio/Cargo.toml new file mode 100644 index 0000000000..71575d16c9 --- /dev/null +++ b/akd/crates/aio/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "aio" +edition.workspace = true +version.workspace = true +authors.workspace = true +license-file.workspace = true +keywords.workspace = true + +[dependencies] +akd_storage = { workspace = true } +common = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +tokio = { workspace = true } +publisher = { path = "../publisher" } +reader = { path = "../reader" } + +[lints] +workspace = true diff --git a/akd/crates/aio/src/main.rs b/akd/crates/aio/src/main.rs new file mode 100644 index 0000000000..49563df6bd --- /dev/null +++ b/akd/crates/aio/src/main.rs @@ -0,0 +1,41 @@ +//! AIO process for an AKD. Spins up multiple async tasks to handle publisher and reader roles. +//! Requires both read and write permissions to the underlying data stores. +//! There should only be one instance of this running at a time for a given AKD. + +use akd_storage::db_config::DbConfig; +use common::VrfStorageType; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::TRACE) + .init(); + + let connection_string = std::env::var("AKD_MSSQL_CONNECTION_STRING") + .expect("AKD_MSSQL_CONNECTION_STRING must be set."); + let db_config = DbConfig::MsSql { + connection_string, + pool_size: 10, + }; + + let db = db_config + .connect() + .await + .expect("Failed to connect to database"); + let vrf = VrfStorageType::HardCodedAkdVRF; + + // Start the publisher write job + let _write_job_handle = { + let db = db.clone(); + let vrf = vrf.clone(); + tokio::spawn(async move { + publisher::start_write_job(db, vrf).await; + }) + }; + + // Create a router with both publisher and reader routes + todo!(); + + // Start the web server + todo!(); +} diff --git a/akd/crates/akd_storage/src/lib.rs b/akd/crates/akd_storage/src/lib.rs index b6bc69bdba..cdccde8a72 100644 --- a/akd/crates/akd_storage/src/lib.rs +++ b/akd/crates/akd_storage/src/lib.rs @@ -21,6 +21,8 @@ mod temp_table; pub mod akd_storage_config; pub mod db_config; +/// Enum to represent different database types supported by the storage layer. +/// Each variant is cheap to clone for reuse across threads. #[derive(Debug, Clone)] pub enum DatabaseType { MsSql(MsSql), diff --git a/akd/crates/akd_test_utility/Cargo.toml b/akd/crates/akd_test_utility/Cargo.toml index 64e233bfed..c692fb5e05 100644 --- a/akd/crates/akd_test_utility/Cargo.toml +++ b/akd/crates/akd_test_utility/Cargo.toml @@ -12,7 +12,7 @@ path = "src/main.rs" [dependencies] akd = { workspace = true } -akd_storage = { path = "../akd_storage" } +akd_storage = { workspace = true} anyhow = "1" async-trait = { workspace = true } clap = { version = "4", features = ["derive"] } diff --git a/akd/crates/common/Cargo.toml b/akd/crates/common/Cargo.toml index 739a0da0a7..32f959f15f 100644 --- a/akd/crates/common/Cargo.toml +++ b/akd/crates/common/Cargo.toml @@ -9,7 +9,7 @@ keywords.workspace = true [dependencies] akd = "0.11.0" async-trait = { workspace = true } -akd_storage = { path = "../akd_storage" } +akd_storage = { workspace = true} config = { workspace = true } serde = { workspace = true } thiserror = "2.0.17" diff --git a/akd/crates/publisher/Cargo.toml b/akd/crates/publisher/Cargo.toml index 3028583ecc..15ff563c0c 100644 --- a/akd/crates/publisher/Cargo.toml +++ b/akd/crates/publisher/Cargo.toml @@ -7,7 +7,10 @@ license-file.workspace = true keywords.workspace = true [dependencies] -akd_storage = { path = "../akd_storage" } +akd = { workspace = true } +akd_storage = { workspace = true} +bitwarden-akd-configuration = { workspace = true} +common = { workspace = true } tokio-util = {version = "0.7.16", features = ["compat"] } tokio = { workspace = true } tracing = { workspace = true } diff --git a/akd/crates/publisher/src/lib.rs b/akd/crates/publisher/src/lib.rs new file mode 100644 index 0000000000..57361ae67c --- /dev/null +++ b/akd/crates/publisher/src/lib.rs @@ -0,0 +1,22 @@ +use akd::{directory::Directory, storage::StorageManager}; +use akd_storage::DatabaseType; +use bitwarden_akd_configuration::BitwardenV1Configuration; +use common::VrfStorageType; +use tracing::instrument; + +struct AppState { + directory: Directory, +} + +#[instrument(skip_all, name = "publisher_start")] +pub async fn start_write_job(_db: DatabaseType, vrf: VrfStorageType) { + let storage_manager = StorageManager::new_no_cache(_db); + let _app_state = AppState { + directory: Directory::new(storage_manager, vrf).await.unwrap(), + }; + println!("Publisher started"); +} + +pub async fn start_web_server(_db: DatabaseType) { + println!("Web server started"); +} diff --git a/akd/crates/publisher/src/main.rs b/akd/crates/publisher/src/main.rs index b4279d80f7..aa43ca1707 100644 --- a/akd/crates/publisher/src/main.rs +++ b/akd/crates/publisher/src/main.rs @@ -1,17 +1,48 @@ +//! The Publisher crate is responsible for tracking insert requests into the AKD and performing them as batches on a schedule. +//! Write permissions are needed to the underlying data stores. +//! There should only be one instance of this running at a time for a given AKD. + +use akd_storage::db_config::DbConfig; +use publisher::{start_web_server, start_write_job}; +use tracing::info; + #[tokio::main] async fn main() { tracing_subscriber::fmt() - .with_max_level(tracing::Level::TRACE) - .init(); + .with_max_level(tracing::Level::TRACE) + .init(); // Read connection string from env var - let connection_string = std::env::var("AKD_MSSQL_CONNECTION_STRING").expect("AKD_MSSQL_CONNECTION_STRING must be set."); + let connection_string = std::env::var("AKD_MSSQL_CONNECTION_STRING") + .expect("AKD_MSSQL_CONNECTION_STRING must be set."); + let db_config = DbConfig::MsSql { + connection_string, + pool_size: 10, + }; - let mssql = akd_storage::ms_sql::MsSql::builder(connection_string) - .pool_size(10) - .build() + let db = db_config + .connect() .await - .expect("Failed to create MsSql instance"); + .expect("Failed to connect to database"); + let vrf = common::VrfStorageType::HardCodedAkdVRF; - mssql.migrate().await.expect("Failed to run migrations"); + let write_job_handle = { + let db = db.clone(); + tokio::spawn(async move { + start_write_job(db, vrf).await; + }) + }; + let web_server_handle = tokio::spawn(async move { + start_web_server(db).await; + }); + + // Wait for both services to complete + tokio::select! { + _ = write_job_handle => { + info!("Write job completed"); + } + _ = web_server_handle => { + info!("Web service completed"); + } + } } diff --git a/akd/crates/reader/Cargo.toml b/akd/crates/reader/Cargo.toml new file mode 100644 index 0000000000..effff77c3a --- /dev/null +++ b/akd/crates/reader/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "reader" +edition.workspace = true +version.workspace = true +authors.workspace = true +license-file.workspace = true +keywords.workspace = true + +[dependencies] +tokio = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +akd = { workspace = true } +akd_storage = { workspace = true} +bitwarden-akd-configuration = { workspace = true} +common = { workspace = true} + +[lints] +workspace = true diff --git a/akd/crates/reader/src/lib.rs b/akd/crates/reader/src/lib.rs new file mode 100644 index 0000000000..4d603670d7 --- /dev/null +++ b/akd/crates/reader/src/lib.rs @@ -0,0 +1,21 @@ +use std::sync::Arc; + +use akd::{directory::ReadOnlyDirectory, storage::StorageManager}; +use tracing::instrument; +use bitwarden_akd_configuration::BitwardenV1Configuration; +use akd_storage::DatabaseType; +use common::VrfStorageType; + +struct AppState { + // Add any shared state here, e.g., database connections + directory: ReadOnlyDirectory, +} + +#[instrument(skip_all, name = "reader_start")] +pub async fn start(db: DatabaseType, vrf: VrfStorageType) { + let storage_manager = StorageManager::new_no_cache(db); + let _app = AppState { + directory: ReadOnlyDirectory::new(storage_manager, vrf).await.unwrap(), + }; + println!("Reader started"); +} diff --git a/akd/crates/reader/src/main.rs b/akd/crates/reader/src/main.rs new file mode 100644 index 0000000000..28415b391e --- /dev/null +++ b/akd/crates/reader/src/main.rs @@ -0,0 +1,40 @@ +//! The Reader crate is responsible for handling read requests to the AKD. It requires only read permissions to the +//! underlying data stores, and can be horizontally scaled as needed. + +use akd::ecvrf::VRFKeyStorage; +use akd_storage::db_config::DbConfig; +use common::VrfStorageType; +use reader::start; +use tracing::info; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::TRACE) + .init(); + + // Read connection string from env var + let connection_string = std::env::var("AKD_MSSQL_CONNECTION_STRING") + .expect("AKD_MSSQL_CONNECTION_STRING must be set."); + let db_config = DbConfig::MsSql { + connection_string, + pool_size: 10, + }; + + let db = db_config + .connect() + .await + .expect("Failed to connect to database"); + let vrf = VrfStorageType::HardCodedAkdVRF; + + let web_server_handle = tokio::spawn(async move { + start(db, vrf).await; + }); + + // Wait for both services to complete + tokio::select! { + _ = web_server_handle => { + info!("Web service completed"); + } + } +}