diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2589d4f5..ef8ca282 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,8 @@ FROM debian:bookworm -RUN apt update && apt install -y curl wget git build-essential pkg-config libssl-dev +RUN apt update && apt install -y \ + build-essential pkg-config libssl-dev \ + curl wget git bash-completion #################### Installation #################### #Earthly diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 662e0947..bda5e26f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,15 +1,12 @@ { "name": "MetaSecret", - "build": { "dockerfile": "Dockerfile", "context": ".." }, - "workspaceFolder": "/meta-secret", - "workspaceMount": "source=${localWorkspaceFolder},target=/meta-secret,type=bind,consistency=cached", - + "workspaceMount": "source=${localWorkspaceFolder},target=/meta-secret,type=bind", "features": { "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} - } + }, } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c38bd0b2..344fbc2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,35 +16,35 @@ exclude = [ [workspace.dependencies] # Error handling -thiserror = "1.0.49" -anyhow = "1.0.75" +thiserror = "1.0.63" +anyhow = "1.0.89" # Logging and tracing -tracing = "0.1" -tracing-subscriber = { version = "0.3" } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18" } # Json serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" -serde_derive = "1.0.188" +serde_json = "1.0.128" +serde_derive = "1.0.210" # Async utils -async-std = { version = "1.12.0" } -async-trait = "0.1" -flume = "0.11" -async-mutex = "1.4" +async-std = { version = "1.13.0" } +async-trait = "0.1.82" +flume = "0.11.0" +async-mutex = "1.4.0" # Cryptography ed25519-dalek = "1.0.1" crypto_box = { version = "0.8.2", features = ["std"] } rand = "0.8.5" -getrandom = { version = "0.2.8", features = ["js"] } -sha2 = { version = "0.10.6", features = ["oid"] } +getrandom = { version = "0.2.15", features = ["js"] } +sha2 = { version = "0.10.8", features = ["oid"] } base64 = "0.20.0" -hex = "0.4" +hex = "0.4.3" #https://github.com/dsprenkels/sss-rs -shamirsecretsharing = "0.1" +shamirsecretsharing = "0.1.5" # Sql -diesel = { version = "2.0.0" } -diesel_migrations = { version = "2.0.0" } \ No newline at end of file +diesel = { version = "2.2.4" } +diesel_migrations = { version = "2.2.0" } \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 5be8f7a7..8fc51a8b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -45,6 +45,7 @@ struct MetaSecretConfig { ///https://kerkour.com/rust-cross-compilation fn main() -> Result<()> { + let args: CmdLine = CmdLine::parse(); let config_file = File::open("config.yaml") diff --git a/core/Cargo.toml b/core/Cargo.toml index 4fdf19c8..101eb35d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -54,6 +54,7 @@ rqrr = "0.5" image = "0.24" wasm-bindgen = "0.2.93" +log = "0.4.22" [dependencies.uuid] version = "1.3.0" diff --git a/core/src/crypto/encoding.rs b/core/src/crypto/encoding.rs index f92f3edc..6ea0bad4 100644 --- a/core/src/crypto/encoding.rs +++ b/core/src/crypto/encoding.rs @@ -7,11 +7,11 @@ pub mod base64 { extern crate base64; use std::fmt::Display; - + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Base64Text(pub String); - + impl Display for Base64Text { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.clone()) diff --git a/core/src/crypto/keys.rs b/core/src/crypto/keys.rs index f0144780..227238ac 100644 --- a/core/src/crypto/keys.rs +++ b/core/src/crypto/keys.rs @@ -1,4 +1,3 @@ -use wasm_bindgen::prelude::wasm_bindgen; use crate::crypto::encoding::base64::Base64Text; use crate::crypto::key_pair::{DsaKeyPair, KeyPair, TransportDsaKeyPair}; diff --git a/core/src/errors/mod.rs b/core/src/errors/mod.rs index 01bce16c..c3627db0 100644 --- a/core/src/errors/mod.rs +++ b/core/src/errors/mod.rs @@ -128,3 +128,9 @@ pub enum SharesLoaderError { #[error(transparent)] DeserializationError(#[from] serde_json::error::Error), } + +#[derive(Debug, thiserror::Error)] +pub enum CredentialsError { + #[error("Credentials not found")] + NotFoundError(String), +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 488f6474..b51dc21c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -27,7 +27,7 @@ pub mod meta_tests; pub mod node; pub mod secret; -pub type CoreResult = std::result::Result; +pub type CoreResult = Result; #[derive(Debug, thiserror::Error)] pub enum RecoveryOperationError { diff --git a/core/src/meta_tests/action/generate_user.rs b/core/src/meta_tests/action/generate_user.rs deleted file mode 100644 index a1147876..00000000 --- a/core/src/meta_tests/action/generate_user.rs +++ /dev/null @@ -1,27 +0,0 @@ -use anyhow::Result; -use std::sync::Arc; - -use crate::node::{ - common::model::{device::DeviceName, user::UserCredentials, vault::VaultName}, - db::{ - objects::persistent_object::PersistentObject, - repo::{credentials_repo::CredentialsRepo, generic_db::KvLogEventRepo}, - }, -}; - -pub struct GenerateUserTestAction { - creds_repo: Arc>, -} - -impl GenerateUserTestAction { - pub fn new(p_obj: Arc>) -> Self { - let creds_repo = Arc::new(CredentialsRepo { p_obj }); - Self { creds_repo } - } - - pub async fn generate_user(&self) -> Result { - self.creds_repo - .get_or_generate_user_creds(DeviceName::from("client"), VaultName::from("test_vault")) - .await - } -} diff --git a/core/src/meta_tests/action/global_index_action.rs b/core/src/meta_tests/action/global_index_action.rs deleted file mode 100644 index 36e084ee..00000000 --- a/core/src/meta_tests/action/global_index_action.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::sync::Arc; - -use crate::node::{ - common::model::device::{DeviceCredentials, DeviceData}, - db::{ - descriptors::{global_index_descriptor::GlobalIndexDescriptor, object_descriptor::ToObjectDescriptor}, - events::{generic_log_event::GenericKvLogEvent, object_id::ObjectId}, - in_mem_db::InMemKvLogEventRepo, - objects::persistent_object::PersistentObject, - }, - server::{ - request::{GlobalIndexRequest, SyncRequest}, - server_app::ServerApp, - }, -}; - -pub struct ServerTestNode { - pub device: DeviceCredentials, - pub p_obj: Arc>, - pub app: ServerApp, -} - -impl ServerTestNode { - pub async fn new() -> anyhow::Result { - let repo = Arc::new(InMemKvLogEventRepo::default()); - let p_obj = Arc::new(PersistentObject::new(repo.clone())); - - let app = ServerApp::init(repo).await?; - let device = app.get_creds().await?; - - Ok(Self { p_obj, app, device }) - } -} - -pub struct GlobalIndexSyncRequestTestAction { - pub server_node: ServerTestNode, -} - -impl GlobalIndexSyncRequestTestAction { - pub async fn init() -> anyhow::Result { - let server_node = ServerTestNode::new().await?; - Ok(Self { server_node }) - } -} - -impl GlobalIndexSyncRequestTestAction { - pub async fn send_request(&self, client_device: DeviceData) -> Vec { - let sync_request = SyncRequest::GlobalIndex(GlobalIndexRequest { - sender: client_device, - global_index: ObjectId::unit(GlobalIndexDescriptor::Index.to_obj_desc()), - }); - - self.server_node.app.handle_sync_request(sync_request).await - } -} diff --git a/core/src/meta_tests/action/mod.rs b/core/src/meta_tests/action/mod.rs deleted file mode 100644 index 7d3e222d..00000000 --- a/core/src/meta_tests/action/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod generate_user; -pub mod global_index_action; -pub mod sign_up_claim_action; diff --git a/core/src/meta_tests/action/sign_up_claim_action.rs b/core/src/meta_tests/action/sign_up_claim_action.rs deleted file mode 100644 index a48dbaed..00000000 --- a/core/src/meta_tests/action/sign_up_claim_action.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anyhow::Result; -use std::sync::Arc; - -use crate::node::{ - common::model::vault::VaultStatus, - db::{ - actions::sign_up_claim::SignUpClaim, objects::persistent_object::PersistentObject, - repo::generic_db::KvLogEventRepo, - }, -}; - -use super::generate_user::GenerateUserTestAction; - -pub struct SignUpClaimTestAction { - p_obj: Arc>, - generate_user_action: Arc>, -} - -impl SignUpClaimTestAction { - pub fn new(p_obj: Arc>) -> Self { - Self { - p_obj: p_obj.clone(), - generate_user_action: Arc::new(GenerateUserTestAction::new(p_obj)), - } - } -} - -impl SignUpClaimTestAction { - pub async fn sign_up(&self) -> Result { - let _user = self.generate_user_action.generate_user().await?; - - let sign_up_claim = SignUpClaim { - p_obj: self.p_obj.clone(), - }; - - sign_up_claim.sign_up().await - } -} diff --git a/core/src/meta_tests/fixture/mod.rs b/core/src/meta_tests/fixture/mod.rs deleted file mode 100644 index 49872f80..00000000 --- a/core/src/meta_tests/fixture/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::node::common::model::{ - device::{DeviceCredentials, DeviceName}, - user::UserCredentials, - vault::VaultName, -}; - -pub struct ClientDeviceFixture { - pub device_creds: DeviceCredentials, - pub user_creds: UserCredentials, -} - -impl Default for ClientDeviceFixture { - fn default() -> Self { - let user_creds = UserCredentials::generate(DeviceName::from("client_device"), VaultName::from("test_vault")); - let device_creds = user_creds.device_creds.clone(); - - Self { - device_creds, - user_creds, - } - } -} diff --git a/core/src/meta_tests/fixture_util.rs b/core/src/meta_tests/fixture_util.rs new file mode 100644 index 00000000..955b2233 --- /dev/null +++ b/core/src/meta_tests/fixture_util.rs @@ -0,0 +1,89 @@ +#[cfg(test)] +pub mod fixture { + use crate::meta_tests::fixture_util::fixture::states::{BaseState, EmptyState, ExtendedState}; + use crate::node::app::meta_app::meta_client_service::fixture::MetaClientServiceFixture; + use crate::node::common::model::device::device_creds::fixture::DeviceCredentialsFixture; + use crate::node::common::model::user::user_creds::fixture::UserCredentialsFixture; + use crate::node::db::objects::global_index::fixture::ServerPersistentGlobalIndexFixture; + use crate::node::db::objects::persistent_object::fixture::PersistentObjectFixture; + use crate::node::db::repo::persistent_credentials::fixture::PersistentCredentialsFixture; + use crate::node::server::server_app::fixture::{ServerAppFixture, ServerDataTransferFixture}; + + pub struct FixtureRegistry { + pub state: S, + } + + impl FixtureRegistry { + pub fn empty() -> FixtureRegistry { + let p_obj = PersistentObjectFixture::generate(); + let device_creds = DeviceCredentialsFixture::generate(); + let user_creds = UserCredentialsFixture::from(&device_creds); + + FixtureRegistry { state: EmptyState { p_obj, device_creds, user_creds } } + } + + pub async fn base() -> anyhow::Result> { + let empty = FixtureRegistry::empty(); + let p_creds = PersistentCredentialsFixture::init(&empty.state).await?; + let server_p_gi = ServerPersistentGlobalIndexFixture::from(&empty); + + let base = BaseState { + empty: empty.state, + p_creds, + server_p_gi, + server_dt: ServerDataTransferFixture::generate() + }; + + Ok(FixtureRegistry { state: base }) + } + + //SyncGatewayFixture + pub async fn extended() -> anyhow::Result> { + let base = FixtureRegistry::base().await?; + + let server_app = ServerAppFixture::try_from(&base)?; + let meta_client_service = MetaClientServiceFixture::from(&base); + + let state = ExtendedState { base: base.state, server_app, meta_client_service }; + Ok(FixtureRegistry { state }) + } + } + + pub mod states { + use crate::node::app::meta_app::meta_client_service::fixture::MetaClientServiceFixture; + use crate::node::common::model::device::device_creds::fixture::DeviceCredentialsFixture; + use crate::node::common::model::user::user_creds::fixture::UserCredentialsFixture; + use crate::node::db::objects::global_index::fixture::ServerPersistentGlobalIndexFixture; + use crate::node::db::objects::persistent_object::fixture::PersistentObjectFixture; + use crate::node::db::repo::persistent_credentials::fixture::PersistentCredentialsFixture; + use crate::node::server::server_app::fixture::{ServerAppFixture, ServerDataTransferFixture}; + + pub enum Fixture { + Empty(EmptyState), + Base(BaseState), + Extended(ExtendedState), + } + + pub struct EmptyState { + pub device_creds: DeviceCredentialsFixture, + pub user_creds: UserCredentialsFixture, + + pub p_obj: PersistentObjectFixture, + } + + pub struct BaseState { + pub empty: EmptyState, + + pub p_creds: PersistentCredentialsFixture, + pub server_p_gi: ServerPersistentGlobalIndexFixture, + + pub server_dt: ServerDataTransferFixture, + } + + pub struct ExtendedState { + pub base: BaseState, + pub server_app: ServerAppFixture, + pub meta_client_service: MetaClientServiceFixture, + } + } +} diff --git a/core/src/meta_tests/mod.rs b/core/src/meta_tests/mod.rs index 65b4d58a..b737ea79 100644 --- a/core/src/meta_tests/mod.rs +++ b/core/src/meta_tests/mod.rs @@ -1,3 +1,14 @@ -pub mod action; -pub mod fixture; +use tracing::Level; + pub mod spec; +pub mod fixture_util; + +pub fn setup_tracing() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .without_time() + .compact() + .pretty() + .init(); + Ok(()) +} diff --git a/core/src/meta_tests/spec/device_log_spec.rs b/core/src/meta_tests/spec/device_log_spec.rs deleted file mode 100644 index 5d2bc593..00000000 --- a/core/src/meta_tests/spec/device_log_spec.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::sync::Arc; - -use anyhow::Result; - -use crate::node::{ - common::model::user::UserData, - db::{ - descriptors::vault_descriptor::VaultDescriptor, - events::object_id::{Next, ObjectId, UnitId}, - objects::persistent_object::PersistentObject, - repo::generic_db::KvLogEventRepo, - }, -}; - -pub struct DeviceLogSpec { - pub p_obj: Arc>, - pub user: UserData, -} - -impl DeviceLogSpec { - pub async fn check_initialization(&self) -> Result<()> { - let device_log_desc = VaultDescriptor::device_log(self.user.user_id()); - - let unit_id = UnitId::unit(&device_log_desc); - - let unit_event_vault_name = self - .p_obj - .repo - .find_one(ObjectId::from(unit_id.clone())) - .await? - .unwrap() - .device_log()? - .get_unit()? - .vault_name(); - - assert_eq!(unit_event_vault_name, self.user.vault_name()); - - let genesis_id = unit_id.clone().next(); - let genesis_user = self - .p_obj - .repo - .find_one(ObjectId::from(genesis_id)) - .await? - .unwrap() - .device_log()? - .get_genesis()? - .user(); - - assert_eq!(genesis_user, self.user); - - Ok(()) - } - - pub async fn check_sign_up_request(&self) -> Result<()> { - let device_log_desc = VaultDescriptor::device_log(self.user.user_id()); - - let sign_up_request_id = UnitId::unit(&device_log_desc).next().next(); - - let candidate = self - .p_obj - .repo - .find_one(ObjectId::from(sign_up_request_id)) - .await? - .unwrap() - .device_log()? - .get_action()? - .get_create()?; - assert_eq!(candidate.device, self.user.device); - - Ok(()) - } -} diff --git a/core/src/meta_tests/spec/global_index_specs.rs b/core/src/meta_tests/spec/global_index_specs.rs deleted file mode 100644 index 7f7016cf..00000000 --- a/core/src/meta_tests/spec/global_index_specs.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::sync::Arc; - -use crate::node::{ - common::model::device::DeviceData, - db::{ - descriptors::{global_index_descriptor::GlobalIndexDescriptor, object_descriptor::ToObjectDescriptor}, - events::{generic_log_event::ObjIdExtractor, global_index_event::GlobalIndexObject, object_id::ObjectId}, - repo::generic_db::KvLogEventRepo, - }, -}; -use anyhow::Result; - -pub struct GlobalIndexSpec { - pub repo: Arc, - pub server_device: DeviceData, -} - -impl GlobalIndexSpec { - pub async fn check(&self) -> Result<()> { - let gi_obj_desc = GlobalIndexDescriptor::Index.to_obj_desc(); - - let unit_event = { - let unit_id = ObjectId::unit(gi_obj_desc.clone()); - self.repo.find_one(unit_id).await?.unwrap().global_index()? - }; - assert_eq!(unit_event.obj_id().get_unit_id().id.id, 0); - - let genesis_event = { - let genesis_id = ObjectId::genesis(gi_obj_desc.clone()); - self.repo.find_one(genesis_id).await?.unwrap().global_index()? - }; - - if let GlobalIndexObject::Genesis(log_event) = genesis_event { - assert_eq!(log_event.value, self.server_device); - } else { - panic!("Invalid Genesis event"); - } - - Ok(()) - } -} diff --git a/core/src/meta_tests/spec/mod.rs b/core/src/meta_tests/spec/mod.rs index 84f6a76c..7ba0a3a4 100644 --- a/core/src/meta_tests/spec/mod.rs +++ b/core/src/meta_tests/spec/mod.rs @@ -1,5 +1 @@ -pub mod device_log_spec; -pub mod global_index_specs; -pub mod sign_up_claim_spec; -pub mod ss_device_log_spec; pub mod test_spec; diff --git a/core/src/meta_tests/spec/sign_up_claim_spec.rs b/core/src/meta_tests/spec/sign_up_claim_spec.rs deleted file mode 100644 index 75b47634..00000000 --- a/core/src/meta_tests/spec/sign_up_claim_spec.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::sync::Arc; - -use crate::{ - meta_tests::spec::device_log_spec::DeviceLogSpec, - node::{ - common::model::user::UserData, - db::{objects::persistent_object::PersistentObject, repo::generic_db::KvLogEventRepo}, - }, -}; -use anyhow::Result; - -use super::{ss_device_log_spec::SSDeviceLogSpec, test_spec::TestSpec}; -use async_trait::async_trait; - -pub struct SignUpClaimSpec { - pub p_obj: Arc>, - pub user: UserData, -} - -#[async_trait(? Send)] -impl TestSpec for SignUpClaimSpec { - async fn verify(&self) -> Result<()> { - let device_log_spec = DeviceLogSpec { - p_obj: self.p_obj.clone(), - user: self.user.clone(), - }; - - device_log_spec.check_initialization().await?; - device_log_spec.check_sign_up_request().await?; - - let ss_device_log_spec = SSDeviceLogSpec { - p_obj: self.p_obj.clone(), - client_user: self.user.clone(), - }; - - ss_device_log_spec.check_initialization().await?; - - Ok(()) - } -} diff --git a/core/src/meta_tests/spec/ss_device_log_spec.rs b/core/src/meta_tests/spec/ss_device_log_spec.rs deleted file mode 100644 index f5d97a3d..00000000 --- a/core/src/meta_tests/spec/ss_device_log_spec.rs +++ /dev/null @@ -1,46 +0,0 @@ -use anyhow::Result; -use std::sync::Arc; - -use crate::node::{ - common::model::user::UserData, - db::{ - descriptors::{object_descriptor::ToObjectDescriptor, shared_secret_descriptor::SharedSecretDescriptor}, - events::object_id::{Next, ObjectId, UnitId}, - objects::persistent_object::PersistentObject, - repo::generic_db::KvLogEventRepo, - }, -}; - -pub struct SSDeviceLogSpec { - pub p_obj: Arc>, - pub client_user: UserData, -} - -impl SSDeviceLogSpec { - pub async fn check_initialization(&self) -> Result<()> { - let ss_obj_desc = SharedSecretDescriptor::SSDeviceLog(self.client_user.device.id.clone()).to_obj_desc(); - - let ss_unit_id = UnitId::unit(&ss_obj_desc); - let ss_genesis_id = ss_unit_id.clone().next(); - - let maybe_unit_event = self.p_obj.repo.find_one(ObjectId::from(ss_unit_id)).await?; - - if let Some(unit_event) = maybe_unit_event { - let vault_name = unit_event.ss_device_log()?.get_unit()?.vault_name(); - assert_eq!(vault_name, self.client_user.vault_name()); - } else { - panic!("SSDevice, unit event not found"); - } - - let maybe_genesis_event = self.p_obj.repo.find_one(ObjectId::from(ss_genesis_id)).await?; - - if let Some(genesis_event) = maybe_genesis_event { - let user = genesis_event.ss_device_log()?.get_genesis()?.user(); - assert_eq!(user, self.client_user); - } else { - panic!("SSDevice, genesis event not found"); - } - - Ok(()) - } -} diff --git a/core/src/node/app/app_state_update_manager.rs b/core/src/node/app/app_state_update_manager.rs index 0b7ae512..030df5ff 100644 --- a/core/src/node/app/app_state_update_manager.rs +++ b/core/src/node/app/app_state_update_manager.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; use crate::node::db::repo::generic_db::KvLogEventRepo; +use std::sync::Arc; pub struct ApplicationManagerConfigurator where - Repo: KvLogEventRepo + Repo: KvLogEventRepo, { pub client_repo: Arc, pub server_repo: Arc, - pub device_repo: Arc + pub device_repo: Arc, } diff --git a/core/src/node/app/meta_app/messaging.rs b/core/src/node/app/meta_app/messaging.rs index 7947da06..44c0f532 100644 --- a/core/src/node/app/meta_app/messaging.rs +++ b/core/src/node/app/meta_app/messaging.rs @@ -1,9 +1,10 @@ use crate::node::common::model::secret::MetaPasswordId; +use crate::node::common::model::vault::VaultName; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum GenericAppStateRequest { - SignUp, + SignUp(VaultName), ClusterDistribution(ClusterDistributionRequest), Recover(MetaPasswordId), } diff --git a/core/src/node/app/meta_app/meta_client_service.rs b/core/src/node/app/meta_app/meta_client_service.rs index ceeb78e1..9cb0bc3d 100644 --- a/core/src/node/app/meta_app/meta_client_service.rs +++ b/core/src/node/app/meta_app/meta_client_service.rs @@ -1,26 +1,29 @@ -use std::sync::Arc; +use anyhow::bail; use flume::{Receiver, Sender}; -use tracing::{info, instrument}; +use std::sync::Arc; +use tracing::{error, info, instrument}; use crate::node::app::meta_app::messaging::{GenericAppStateRequest, GenericAppStateResponse}; use crate::node::app::sync_gateway::SyncGateway; use crate::node::common::actor::ServiceState; use crate::node::common::data_transfer::MpscDataTransfer; +use crate::node::common::model::vault::VaultStatus; use crate::node::common::model::ApplicationState; -use crate::node::common::model::device::DeviceName; +use crate::node::common::model::device::common::DeviceName; +use crate::node::common::model::user::common::UserData; use crate::node::db::actions::recover::RecoveryAction; -use crate::node::db::actions::sign_up_claim::SignUpClaim; +use crate::node::db::actions::sign_up::claim::SignUpClaim; use crate::node::db::events::local_event::CredentialsObject; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::objects::persistent_vault::PersistentVault; -use crate::node::db::repo::credentials_repo::CredentialsRepo; +use crate::node::db::repo::persistent_credentials::PersistentCredentials; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::secret::MetaDistributor; pub struct MetaClientService { pub data_transfer: Arc, pub sync_gateway: Arc>, - pub state_provider: Arc + pub state_provider: Arc, } pub struct MetaClientDataTransfer { @@ -31,50 +34,72 @@ impl MetaClientService { #[instrument(skip_all)] pub async fn run(&self) -> anyhow::Result<()> { info!("Run meta_app service"); - - //todo!("get or generate device credentials"); - let p_obj = self.sync_gateway.persistent_object.clone(); - + + let p_obj = self.sync_gateway.p_obj.clone(); + let mut service_state = self.build_service_state().await?; while let Ok(request) = self.data_transfer.dt.service_receive().await { info!( "Action execution. Request {:?}, state: {:?}", - &request, &service_state.state + &request, &service_state.app_state ); self.sync_gateway.sync().await?; match request { - GenericAppStateRequest::SignUp => { - let sign_up_claim = SignUpClaim { p_obj: p_obj.clone() }; - - sign_up_claim.sign_up().await?; - self.sync_gateway.sync().await?; + GenericAppStateRequest::SignUp(vault_name) => match &service_state.app_state { + ApplicationState::Local { device } => { + let user_data = UserData { + vault_name, + device: device.clone(), + }; - let (_, vault_status) = sign_up_claim.get_vault_status().await?; + let sign_up_claim = SignUpClaim { p_obj: p_obj.clone() }; + sign_up_claim.sign_up(user_data.clone()).await?; + self.sync_gateway.sync().await?; - todo!("update vault status"); - //service_state.state.vault = Some(vault_status); - } + let p_vault = PersistentVault { p_obj: self.p_obj() }; + let new_status = p_vault.find(user_data.clone()).await?; + service_state.app_state = ApplicationState::Vault { vault: new_status }; + } + ApplicationState::Vault { vault } => { + error!("You are already a vault member: {:?}", vault); + } + }, GenericAppStateRequest::ClusterDistribution(request) => { - let creds_repo = CredentialsRepo { p_obj: p_obj.clone() }; + let creds_repo = PersistentCredentials { p_obj: p_obj.clone() }; - let user_creds = creds_repo.get_user_creds().await?; + let maybe_user_creds = creds_repo.get_user_creds().await?; - let vault_repo = PersistentVault { - p_obj: p_obj.clone(), - }; - let vault = vault_repo.get_vault().await?; + match maybe_user_creds { + None => { + bail!("Invalid state. UserCredentials must be present") + } + Some(user_creds) => { + let vault_repo = PersistentVault { p_obj: p_obj.clone() }; + let vault_status = vault_repo.find(user_creds.user()).await?; - let distributor = MetaDistributor { - persistent_obj: p_obj.clone(), - vault, - user_creds: Arc::new(user_creds), - }; + match vault_status { + VaultStatus::NotExists(_) => { + bail!("Vault doesn't exists") + } + VaultStatus::Outsider(_) => { + bail!("Outsider user can't manage a vault") + } + VaultStatus::Member { vault, .. } => { + let distributor = MetaDistributor { + persistent_obj: p_obj.clone(), + vault, + user_creds: Arc::new(user_creds), + }; - distributor.distribute(request.pass_id, request.pass).await?; + distributor.distribute(request.pass_id, request.pass).await?; + } + } + } + } } GenericAppStateRequest::Recover(meta_pass_id) => { @@ -82,40 +107,45 @@ impl MetaClientService { persistent_obj: p_obj.clone(), }; recovery_action - .recovery_request(meta_pass_id, &service_state.state) + .recovery_request(meta_pass_id, &service_state.app_state) .await?; } } - - self.state_provider.push(&service_state.state).await + + self.state_provider.push(&service_state.app_state).await } Ok(()) } async fn build_service_state(&self) -> anyhow::Result> { - let maybe_creds_event = { - let creds_repo = CredentialsRepo { p_obj: self.p_obj() }; - creds_repo.find().await? - }; + let creds_repo = PersistentCredentials { p_obj: self.p_obj() }; + let maybe_creds = creds_repo.find().await?; - let app_state = match maybe_creds_event { - None => ApplicationState::Empty, + let app_state = match maybe_creds { + None => { + let device_creds = creds_repo.get_or_generate_device_creds(DeviceName::client()) + .await?; + ApplicationState::Local { + device: device_creds.device, + } + } Some(creds) => match creds { CredentialsObject::Device(device_creds_event) => ApplicationState::Local { - device: device_creds_event.value.device + device: device_creds_event.value.device, }, - CredentialsObject::DefaultUser(user_creds_event) => ApplicationState::User { - user: user_creds_event.value + CredentialsObject::DefaultUser(user_creds) => { + let p_vault = PersistentVault { p_obj: self.p_obj() }; + let vault_status = p_vault.find(user_creds.value.user()).await?; + + ApplicationState::Vault { vault: vault_status } } - } + }, }; - let service_state = ServiceState { - state: app_state, - }; - - self.state_provider.push(&service_state.state).await; + let service_state = ServiceState { app_state }; + + self.state_provider.push(&service_state.app_state).await; Ok(service_state) } @@ -124,7 +154,7 @@ impl MetaClientService { } fn p_obj(&self) -> Arc> { - self.sync_gateway.persistent_object.clone() + self.sync_gateway.p_obj.clone() } } @@ -140,16 +170,13 @@ impl MetaClientAccessProxy { pub struct MetaClientStateProvider { sender: Sender, - receiver: Receiver + receiver: Receiver, } impl MetaClientStateProvider { pub fn new() -> Self { let (sender, receiver) = flume::bounded(100); - Self { - sender, - receiver - } + Self { sender, receiver } } pub async fn get(&self) -> ApplicationState { @@ -159,4 +186,78 @@ impl MetaClientStateProvider { pub async fn push(&self, state: &ApplicationState) { self.sender.send_async(state.clone()).await.unwrap() } +} + +#[cfg(test)] +pub mod fixture { + use std::sync::Arc; + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::meta_tests::fixture_util::fixture::states::BaseState; + use crate::node::app::meta_app::meta_client_service::{MetaClientDataTransfer, MetaClientService, MetaClientStateProvider}; + use crate::node::app::sync_gateway::fixture::SyncGatewayFixture; + use crate::node::common::data_transfer::MpscDataTransfer; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + + pub struct MetaClientServiceFixture { + pub client: Arc>, + pub vd: Arc>, + + pub state_provider: MetaClientStateProviderFixture, + pub data_transfer: MetaClientDataTransferFixture, + + pub sync_gateway: SyncGatewayFixture, + } + + impl MetaClientServiceFixture { + pub fn from(base: &FixtureRegistry) -> Self { + let state_provider = MetaClientStateProviderFixture::generate(); + let dt_fxr = MetaClientDataTransferFixture::generate(); + + let sync_gateway = SyncGatewayFixture::from(&base); + + let client = Arc::new(MetaClientService { + data_transfer: dt_fxr.client.clone(), + sync_gateway: sync_gateway.client_gw.clone(), + state_provider: state_provider.client.clone(), + }); + + let vd = Arc::new(MetaClientService { + data_transfer: dt_fxr.vd.clone(), + sync_gateway: sync_gateway.vd_gw.clone(), + state_provider: state_provider.vd.clone(), + }); + + Self { client, vd, state_provider, data_transfer: dt_fxr, sync_gateway } + } + } + + pub struct MetaClientStateProviderFixture { + pub client: Arc, + pub vd: Arc, + } + + impl MetaClientStateProviderFixture { + pub fn generate() -> Self { + Self { + client: Arc::new(MetaClientStateProvider::new()), + vd: Arc::new(MetaClientStateProvider::new()), + } + } + } + + pub struct MetaClientDataTransferFixture { + client: Arc, + vd: Arc, + } + + impl MetaClientDataTransferFixture { + pub fn generate() -> Self { + Self { + client: Arc::new(MetaClientDataTransfer { + dt: MpscDataTransfer::new(), + }), + vd: Arc::new(MetaClientDataTransfer { dt: MpscDataTransfer::new() }), + } + } + } } \ No newline at end of file diff --git a/core/src/node/app/sync_gateway.rs b/core/src/node/app/sync_gateway.rs index ab2e60ae..011d9c30 100644 --- a/core/src/node/app/sync_gateway.rs +++ b/core/src/node/app/sync_gateway.rs @@ -5,11 +5,13 @@ use anyhow::{anyhow, bail}; use tracing::{debug, error, info, instrument}; use crate::node::common::model::crypto::{AeadAuthData, AeadPlainText, EncryptedMessage}; -use crate::node::common::model::device::{DeviceData, DeviceId, DeviceLink}; +use crate::node::common::model::device::common::{DeviceData, DeviceId}; +use crate::node::common::model::device::device_link::DeviceLink; use crate::node::common::model::secret::{ SSDistributionId, SSDistributionStatus, SecretDistributionData, SecretDistributionType, }; -use crate::node::common::model::user::{UserCredentials, UserDataMember, UserId}; +use crate::node::common::model::user::common::{UserDataMember, UserId}; +use crate::node::common::model::user::user_creds::UserCredentials; use crate::node::common::model::vault::VaultStatus; use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ToObjectDescriptor}; @@ -17,15 +19,15 @@ use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescript use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; use crate::node::db::events::db_tail::DbTail; use crate::node::db::events::generic_log_event::{GenericKvLogEvent, KeyExtractor, ToGenericEvent}; -use crate::node::db::events::global_index_event::GlobalIndexObject; use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; use crate::node::db::events::local_event::{CredentialsObject, DbTailObject}; -use crate::node::db::events::object_id::ObjectId; +use crate::node::db::events::object_id::{Next, ObjectId}; use crate::node::db::events::shared_secret_event::SharedSecretObject; +use crate::node::db::objects::global_index::ClientPersistentGlobalIndex; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::objects::persistent_shared_secret::PersistentSharedSecret; use crate::node::db::objects::persistent_vault::PersistentVault; -use crate::node::db::repo::credentials_repo::CredentialsRepo; +use crate::node::db::repo::persistent_credentials::PersistentCredentials; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::node::server::request::{GlobalIndexRequest, SyncRequest, VaultRequest}; use crate::node::server::server_app::ServerDataTransfer; @@ -33,7 +35,7 @@ use crate::node::server::server_data_sync::{DataEventsResponse, DataSyncRequest, pub struct SyncGateway { pub id: String, - pub persistent_object: Arc>, + pub p_obj: Arc>, pub server_dt: Arc, } @@ -48,7 +50,7 @@ impl SyncGateway { error!("Sync error: {:?}", err); } - async_std::task::sleep(Duration::from_millis(300)).await; + async_std::task::sleep(Duration::from_secs(1)).await; } } @@ -57,51 +59,73 @@ impl SyncGateway { /// - vault, shared secret... - user has been registered, we can sync vault related events #[instrument(skip_all)] pub async fn sync(&self) -> anyhow::Result<()> { - let creds_repo = CredentialsRepo { - p_obj: self.persistent_object.clone(), + let creds_repo = PersistentCredentials { + p_obj: self.p_obj.clone(), }; let maybe_creds_event = creds_repo.find().await?; let Some(creds_obj) = maybe_creds_event else { + error!("No device creds found on this device, skip"); return Ok(()); }; - self.sync_global_index(creds_obj.device()).await?; + self.download_global_index(creds_obj.device()).await?; let CredentialsObject::DefaultUser(user_creds_event) = creds_obj else { + //info!("No user defined"); return Ok(()); }; - //Vault synchronization let user_creds = user_creds_event.value; - let sender = user_creds.user(); + self.sync_vault(&user_creds).await?; + + let server_tail_response = self.get_tail(&user_creds).await?; + + self.sync_device_log(&server_tail_response, user_creds.user_id()) + .await?; + + //self.sync_shared_secrets(&server_tail_response, &user_creds).await?; + + Ok(()) + } + + async fn get_tail(&self, user_creds: &UserCredentials) -> anyhow::Result { let p_vault = PersistentVault { - p_obj: self.persistent_object.clone(), + p_obj: self.p_obj.clone(), }; - let vault_status = p_vault.find_for_default_user().await?; + let vault_status = p_vault.find(user_creds.user()).await?; - let VaultStatus::Member { member, .. } = vault_status else { - return Ok(()); - }; + let server_tail_response = self + .server_dt + .dt + .send_to_service_and_get(DataSyncRequest::ServerTailRequest(vault_status.user())) + .await? + .to_server_tail()?; + Ok(server_tail_response) + } + #[instrument(skip_all)] + async fn sync_vault(&self, user_creds: &UserCredentials) -> anyhow::Result<()> { let vault_sync_request = { + let sender = user_creds.user(); + let vault_name = user_creds.vault_name.clone(); let vault_log_free_id = { let obj_desc = VaultDescriptor::vault_log(vault_name.clone()); - self.persistent_object.find_free_id_by_obj_desc(obj_desc).await? + self.p_obj.find_free_id_by_obj_desc(obj_desc).await? }; let vault_free_id = { let obj_desc = VaultDescriptor::vault(vault_name.clone()); - self.persistent_object.find_free_id_by_obj_desc(obj_desc).await? + self.p_obj.find_free_id_by_obj_desc(obj_desc).await? }; let vault_status_free_id = { let obj_desc = VaultDescriptor::vault_membership(user_creds.user_id()); - self.persistent_object.find_free_id_by_obj_desc(obj_desc).await? + self.p_obj.find_free_id_by_obj_desc(obj_desc).await? }; SyncRequest::Vault(VaultRequest { @@ -120,23 +144,9 @@ impl SyncGateway { .to_data()?; for new_event in data_sync_events { - debug!("id: {:?}. Sync gateway. New event: {:?}", self.id, new_event); - self.persistent_object.repo.save(new_event).await?; + debug!("id: {:?}. Sync gateway. New event from server: {:?}", self.id, new_event); + self.p_obj.repo.save(new_event).await?; } - - // TODO Get latest device log messages and send to the server - // - get the latest device_log and ss_device_log tail ids from server - let server_tail_response = self - .server_dt - .dt - .send_to_service_and_get(DataSyncRequest::ServerTailRequest(member.user().user_id())) - .await? - .to_server_tail()?; - - self.sync_device_log(&server_tail_response, user_creds.user_id()) - .await?; - self.sync_shared_secrets(&server_tail_response, &user_creds).await?; - Ok(()) } @@ -147,41 +157,46 @@ impl SyncGateway { .unwrap_or_else(|| ObjectId::unit(SharedSecretDescriptor::SSDeviceLog(device_id).to_obj_desc())); let ss_device_log_events_to_sync = self - .persistent_object + .p_obj .find_object_events(server_ss_device_log_tail_id) .await?; - for event in ss_device_log_events_to_sync { - self.server_dt.dt.send_to_service(DataSyncRequest::Event(event)).await; + for ss_device_log_event in ss_device_log_events_to_sync { + self.server_dt.dt.send_to_service(DataSyncRequest::Event(ss_device_log_event)).await; } Ok(()) } + #[instrument(skip(self))] async fn sync_device_log(&self, server_tail: &ServerTailResponse, user_id: UserId) -> anyhow::Result<()> { - let server_device_log_tail_id = server_tail - .device_log_tail - .clone() - .unwrap_or_else(|| ObjectId::unit(VaultDescriptor::device_log(user_id))); + let tail_to_sync = match &server_tail.device_log_tail { + None => { + ObjectId::unit(VaultDescriptor::device_log(user_id)) + } + Some(server_tail_id) => { + server_tail_id.clone().next() + } + }; let device_log_events_to_sync = self - .persistent_object - .find_object_events(server_device_log_tail_id) + .p_obj + .find_object_events(tail_to_sync) .await?; - for event in device_log_events_to_sync { - self.server_dt.dt.send_to_service(DataSyncRequest::Event(event)).await; + for device_log_event in device_log_events_to_sync { + self.server_dt.dt.send_to_service(DataSyncRequest::Event(device_log_event)).await; } Ok(()) } - async fn sync_global_index(&self, sender: DeviceData) -> anyhow::Result<()> { + #[instrument(skip(self))] + async fn download_global_index(&self, sender: DeviceData) -> anyhow::Result<()> { //TODO optimization: read global index tail id from db_tail - let gi_free_id = { let gi_desc = ObjectDescriptor::GlobalIndex(GlobalIndexDescriptor::Index); - self.persistent_object.find_free_id_by_obj_desc(gi_desc).await? + self.p_obj.find_free_id_by_obj_desc(gi_desc).await? }; let sync_request = SyncRequest::GlobalIndex(GlobalIndexRequest { @@ -196,150 +211,142 @@ impl SyncGateway { .await? .to_data()?; + let p_gi_obj = ClientPersistentGlobalIndex { + p_obj: self.p_obj.clone(), + }; + for gi_event in new_gi_events { - if let GenericKvLogEvent::GlobalIndex(gi_obj) = &gi_event { - self.persistent_object.repo.save(gi_event.clone()).await?; - - // Update vault index according to global index - if let GlobalIndexObject::Update(upd_event) = gi_obj { - let vault_id = upd_event.value.clone(); - let vault_idx_evt = GlobalIndexObject::index_from_vault_id(vault_id).to_generic(); - self.persistent_object.repo.save(vault_idx_evt).await?; - } - } else { - return Err(anyhow!("Invalid event: {:?}", gi_event.key().obj_desc())); - } + let gi_obj = gi_event.global_index()?; + p_gi_obj.save(&gi_obj).await?; } Ok(()) } - #[instrument(skip_all)] + #[instrument(skip(self))] async fn sync_shared_secrets( &self, server_tail: &ServerTailResponse, creds: &UserCredentials, ) -> anyhow::Result<()> { - self.sync_ss_device_log(&server_tail, creds.device().id).await?; + debug!("Sync shared secrets"); - let key_manager = creds.device_creds.key_manager()?; + self.sync_ss_device_log(&server_tail, creds.device().device_id).await?; + + let vault_status = { + let p_vault = PersistentVault { + p_obj: self.p_obj.clone(), + }; - let p_vault = PersistentVault { - p_obj: self.persistent_object.clone(), + p_vault.find(creds.user()).await? }; - let vault_status = p_vault.find(creds.user()).await?; + let VaultStatus::Member { vault, member: UserDataMember(member_data) } = vault_status else { + return Ok(()); + }; + + //get ss_ledger + // distribute shares if needed + let persistent_ss = PersistentSharedSecret { + p_obj: self.p_obj.clone(), + }; - match vault_status { - VaultStatus::Outsider(_) => { - return Ok(()); - } - VaultStatus::Member { - vault, - member: UserDataMember(member_data), - } => { - //get ss_ledger - // distribute shares if needed - let persistent_ss = PersistentSharedSecret { - p_obj: self.persistent_object.clone(), - }; - - let ledger_obj = persistent_ss.get_ledger(member_data.vault_name).await?; - let ledger_data = ledger_obj.to_ledger_data()?; - - for (claim_id, claim) in ledger_data.claims { - for (p2p_device_link, status) in claim.distribution { - if !p2p_device_link.receiver().eq(&member_data.device.id) { - continue; - } + let ledger_obj = persistent_ss.get_ledger(member_data.vault_name).await?; + let ledger_data = ledger_obj.to_ledger_data()?; - match claim.distribution_type { - SecretDistributionType::Recover => { - //send share - if let SSDistributionStatus::Pending = status { - let local_share = persistent_ss - .get_local_share_distribution_data(claim.pass_id.clone()) - .await?; + for (claim_id, claim) in ledger_data.claims { + for (p2p_device_link, status) in claim.distribution { + if !p2p_device_link.receiver().eq(&member_data.device.device_id) { + continue; + } - // TODO decrypt local share message + match claim.distribution_type { + SecretDistributionType::Recover => { + //send share + if let SSDistributionStatus::Pending = status { + let key_manager = creds.device_creds.key_manager()?; - let plain_share = { - let encrypted_local_share = local_share.secret_message.cipher_text(); - encrypted_local_share.decrypt(&key_manager.transport.secret_key)? - }; + let local_share = persistent_ss + .get_local_share_distribution_data(claim.pass_id.clone()) + .await?; - let device_link = DeviceLink::PeerToPeer(p2p_device_link.clone()); - let maybe_channel = vault.build_communication_channel(device_link); + // TODO decrypt local share message - let Some(channel) = maybe_channel else { - bail!("Failed to build communication channel") - }; + let plain_share = { + let encrypted_local_share = local_share.secret_message.cipher_text(); + encrypted_local_share.decrypt(&key_manager.transport.secret_key)? + }; - // Since we got a device link from a claim it means the sender of a claim - // going to be the receiver of the share. - // We need to swap the sender and the receiver - let inverse_channel = channel.inverse(); - - //get user from a vault - let plain_text_response = { - AeadPlainText { - msg: plain_share.msg, - auth_data: AeadAuthData::from(inverse_channel), - } - }; + let device_link = DeviceLink::PeerToPeer(p2p_device_link.clone()); + let maybe_channel = vault.build_communication_channel(device_link); - let inverse_p2p_link = p2p_device_link.inverse(); - let inverse_device_link = DeviceLink::PeerToPeer(inverse_p2p_link.clone()); - - let ss_dist = { - let sk = &key_manager.transport.secret_key; - let encrypted_share = plain_text_response.encrypt(sk)?; - let encrypted_message = EncryptedMessage::CipherShare { - device_link: inverse_device_link.clone(), - share: encrypted_share, - }; - - SecretDistributionData { - vault_name: vault.vault_name.clone(), - pass_id: claim.pass_id.clone(), - secret_message: encrypted_message, - } - }; + let Some(channel) = maybe_channel else { + bail!("Failed to build communication channel") + }; + + // Since we got a device link from a claim it means the sender of a claim + // going to be the receiver of the share. + // We need to swap the sender and the receiver + let inverse_channel = channel.inverse(); - let ss_recover_obj = { - let ss_dist_obj_desc = { - let ss_event_id = SSDistributionId { - claim_id: claim_id.clone(), - distribution_type: SecretDistributionType::Recover, - device_link: inverse_p2p_link, - }; + //get user from a vault + let plain_text_response = { + AeadPlainText { + msg: plain_share.msg, + auth_data: AeadAuthData::from(inverse_channel), + } + }; + + let inverse_p2p_link = p2p_device_link.inverse(); + let inverse_device_link = DeviceLink::PeerToPeer(inverse_p2p_link.clone()); + + let ss_dist = { + let sk = &key_manager.transport.secret_key; + let encrypted_share = plain_text_response.encrypt(sk)?; + let encrypted_message = EncryptedMessage::CipherShare { + device_link: inverse_device_link.clone(), + share: encrypted_share, + }; + + SecretDistributionData { + vault_name: vault.vault_name.clone(), + pass_id: claim.pass_id.clone(), + secret_message: encrypted_message, + } + }; + + let ss_recover_obj = { + let ss_dist_obj_desc = { + let ss_event_id = SSDistributionId { + claim_id: claim_id.clone(), + distribution_type: SecretDistributionType::Recover, + device_link: inverse_p2p_link, + }; - SharedSecretDescriptor::SSDistribution(ss_event_id).to_obj_desc() - }; + SharedSecretDescriptor::SSDistribution(ss_event_id).to_obj_desc() + }; - let key = KvLogEvent { - key: KvKey::unit(ss_dist_obj_desc), - value: ss_dist, - }; + let key = KvLogEvent { + key: KvKey::unit(ss_dist_obj_desc), + value: ss_dist, + }; - SharedSecretObject::SSDistribution(key).to_generic() - }; + SharedSecretObject::SSDistribution(key).to_generic() + }; - self.server_dt - .dt - .send_to_service(DataSyncRequest::Event(ss_recover_obj)) - .await; - } - } - SecretDistributionType::Split => { - // NOOP - } + self.server_dt + .dt + .send_to_service(DataSyncRequest::Event(ss_recover_obj)) + .await; } } + SecretDistributionType::Split => { + // NOOP + } } - - Ok(()) } } + + Ok(()) } #[instrument(skip_all)] @@ -355,7 +362,40 @@ impl SyncGateway { }) .to_generic(); - self.persistent_object.repo.save(new_db_tail_event).await?; + self.p_obj.repo.save(new_db_tail_event).await?; Ok(()) } } + +#[cfg(test)] +pub mod fixture { + use std::sync::Arc; + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::meta_tests::fixture_util::fixture::states::BaseState; + use crate::node::app::sync_gateway::SyncGateway; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + + pub struct SyncGatewayFixture { + pub client_gw: Arc>, + pub vd_gw: Arc> + } + + impl SyncGatewayFixture { + pub fn from(registry: &FixtureRegistry) -> Self { + + let client_gw = Arc::new(SyncGateway { + id: "client_gw".to_string(), + p_obj: registry.state.empty.p_obj.client.clone(), + server_dt: registry.state.server_dt.server_dt.clone(), + }); + + let vd_gw = Arc::new(SyncGateway { + id: "vd_gw".to_string(), + p_obj: registry.state.empty.p_obj.vd.clone(), + server_dt: registry.state.server_dt.server_dt.clone(), + }); + + Self { client_gw, vd_gw } + } + } +} diff --git a/core/src/node/app/virtual_device.rs b/core/src/node/app/virtual_device.rs index df9636e8..11d85901 100644 --- a/core/src/node/app/virtual_device.rs +++ b/core/src/node/app/virtual_device.rs @@ -1,22 +1,28 @@ use std::sync::Arc; - +use log::warn; use tracing::{info, instrument}; use crate::node::app::meta_app::meta_client_service::MetaClientAccessProxy; use crate::node::app::sync_gateway::SyncGateway; -use crate::node::common::model::vault::VaultStatus; +use crate::node::common::model::device::common::DeviceName; +use crate::node::common::model::user::common::{UserDataOutsiderStatus, UserMembership}; +use crate::node::common::model::user::user_creds::UserCredentials; +use crate::node::common::model::vault::{VaultName, VaultStatus}; +use crate::node::db::actions::sign_up::claim::SignUpClaim; use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; -use crate::node::db::events::vault_event::{VaultAction, VaultLogObject}; +use crate::node::db::events::vault::vault_log_event::VaultLogObject; +use crate::node::db::events::vault_event::VaultActionEvent; use crate::node::db::objects::persistent_device_log::PersistentDeviceLog; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::objects::persistent_shared_secret::PersistentSharedSecret; use crate::node::db::objects::persistent_vault::PersistentVault; +use crate::node::db::repo::persistent_credentials::PersistentCredentials; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::node::server::server_app::ServerDataTransfer; pub struct VirtualDevice { - persistent_object: Arc>, + p_obj: Arc>, pub meta_client_proxy: Arc, pub server_dt: Arc, gateway: Arc>, @@ -30,10 +36,10 @@ impl VirtualDevice { server_dt: Arc, gateway: Arc>, ) -> anyhow::Result> { - info!("Run virtual device event handler"); + info!("Initialize virtual device event handler"); let virtual_device = Self { - persistent_object, + p_obj: persistent_object, meta_client_proxy: meta_client_access_proxy.clone(), server_dt, gateway, @@ -42,56 +48,93 @@ impl VirtualDevice { Ok(virtual_device) } + #[instrument(skip_all)] pub async fn run(&self) -> anyhow::Result<()> { + info!("Run virtual device event handler"); + + let creds_repo = PersistentCredentials { p_obj: self.p_obj() }; + + let device_name = DeviceName::virtual_device(); + let user_creds = creds_repo + .get_or_generate_user_creds(device_name, VaultName::test()) + .await?; + + self.gateway.sync().await?; + + //No matter what current vault status is, sign_up claim will handle the case properly + info!("SignUp virtual device if needed"); + let sign_up_claim = SignUpClaim { p_obj: self.p_obj() }; + sign_up_claim.sign_up(user_creds.user()).await?; + + // Handle state changes loop { - self.gateway.sync().await?; + self.do_work(&user_creds).await?; + async_std::task::sleep(std::time::Duration::from_secs(1)).await; + } + } - let p_vault = PersistentVault { - p_obj: self.persistent_object.clone(), - }; + async fn do_work(&self, user_creds: &UserCredentials) -> anyhow::Result<()> { + self.gateway.sync().await?; - let vault_status = p_vault.find_for_default_user().await?; - if let VaultStatus::Member { member, vault } = vault_status { - //vault actions - let vault_log_desc = VaultDescriptor::VaultLog(vault.vault_name.clone()).to_obj_desc(); - let maybe_vault_log_event = self.persistent_object.find_tail_event(vault_log_desc).await?; + let p_vault = PersistentVault { p_obj: self.p_obj() }; + let vault_status = p_vault.find(user_creds.user()).await?; - if let Some(vault_log_event) = maybe_vault_log_event { - let vault_log = vault_log_event.vault_log()?; + let VaultStatus::Member { member: me, vault } = vault_status else { + warn!("Not a vault member"); + return Ok(()); + }; + + let vault_name = vault.vault_name.clone(); + //vault actions + let vault_log_desc = VaultDescriptor::VaultLog(vault_name).to_obj_desc(); + + let maybe_vault_log_event = self.p_obj + .find_tail_event(vault_log_desc) + .await?; - if let VaultLogObject::Action(vault_action) = vault_log { - match vault_action.value { - VaultAction::JoinClusterRequest { candidate } => { + if let Some(vault_log_event) = maybe_vault_log_event { + let vault_log = vault_log_event.vault_log()?; + + if let VaultLogObject::Action(vault_action) = vault_log { + match vault_action.value { + VaultActionEvent::JoinClusterRequest { candidate } => { + if let UserMembership::Outsider(outsider) = vault.membership(candidate.clone()) { + if let UserDataOutsiderStatus::NonMember = outsider.status { let p_device_log = PersistentDeviceLog { - p_obj: self.persistent_object.clone(), + p_obj: self.p_obj.clone(), }; - p_device_log.save_accept_join_request_event(member, candidate).await?; - } - VaultAction::UpdateMembership { .. } => { - //changes made by another device, no need for any actions - } - VaultAction::AddMetaPassword { .. } => { - //changes made by another device, no need for any actions - } - VaultAction::CreateVault(_) => { - // server's responsibities + p_device_log + .save_accept_join_request_event(me, candidate) + .await?; } } - }; + } + VaultActionEvent::UpdateMembership { .. } => { + //changes made by another device, no need for any actions + } + VaultActionEvent::AddMetaPassword { .. } => { + //changes made by another device, no need for any actions + } + VaultActionEvent::CreateVault(_) => { + // server's responsibities + } } + }; + } - // shared secret actions - let _p_ss_log = PersistentSharedSecret { - p_obj: self.persistent_object.clone(), - }; + // shared secret actions + let _p_ss_log = PersistentSharedSecret { + p_obj: self.p_obj.clone(), + }; - todo!("Implement SS log actions - replication request. This code must read the log and handle the events. Same as above for vault"); - } + //todo!("Implement SS log actions - replication request. This code must read the log and handle the events. Same as above for vault"); - self.gateway.sync().await?; + self.gateway.sync().await?; + Ok(()) + } - async_std::task::sleep(std::time::Duration::from_millis(300)).await; - } + fn p_obj(&self) -> Arc> { + self.p_obj.clone() } } diff --git a/core/src/node/common/actor.rs b/core/src/node/common/actor.rs index 16b1f276..b2a3f923 100644 --- a/core/src/node/common/actor.rs +++ b/core/src/node/common/actor.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; pub struct ServiceState { - pub state: T, + pub app_state: T, } #[async_trait(? Send)] diff --git a/core/src/node/common/data_transfer.rs b/core/src/node/common/data_transfer.rs index eebf2876..e2c99c45 100644 --- a/core/src/node/common/data_transfer.rs +++ b/core/src/node/common/data_transfer.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use flume::{Receiver, RecvError, Sender}; use tracing::{instrument, Instrument}; @@ -45,25 +46,27 @@ impl MpscDataTransfer { } } -impl MpscDataTransfer { - #[instrument(skip_all)] +impl MpscDataTransfer { + #[instrument(skip(self))] pub async fn send_to_service(&self, message: Request) { let _ = self.service_channel.sender.send_async(message).in_current_span().await; } - #[instrument(skip_all)] + #[instrument(skip(self))] pub async fn service_receive(&self) -> Result { - self.service_channel.receiver.recv_async().in_current_span().await + let request = self.service_channel.receiver.recv_async().in_current_span().await; + request } - #[instrument(skip_all)] + #[instrument(skip(self))] pub async fn send_to_service_and_get(&self, message: Request) -> Result { let _ = self.service_channel.sender.send_async(message).in_current_span().await; //receive a message from the service via client channel - self.client_channel.receiver.recv_async().in_current_span().await + let result = self.client_channel.receiver.recv_async().in_current_span().await; + result } - #[instrument(skip_all)] + #[instrument(skip(self))] pub async fn send_to_client(&self, events: Response) { let _ = self.client_channel.sender.send_async(events).in_current_span().await; } diff --git a/core/src/node/common/model/device/common.rs b/core/src/node/common/model/device/common.rs new file mode 100644 index 00000000..2cbefdaa --- /dev/null +++ b/core/src/node/common/model/device/common.rs @@ -0,0 +1,88 @@ +use std::fmt::Display; + +use crypto::utils::generate_uuid_b64_url_enc; + +use crate::crypto; +use crate::crypto::encoding::base64::Base64Text; +use crate::crypto::keys::OpenBox; +use crate::crypto::utils::rand_uuid_b64_url_enc; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DeviceId(String); + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DeviceName(String); + +impl DeviceName { + pub fn server() -> Self { + DeviceName::from("server_device") + } + + pub fn virtual_device() -> Self { + DeviceName::from("vd_device") + } + + pub fn client() -> Self { + DeviceName::from("client_device") + } +} + +impl From for DeviceName { + fn from(device_name: String) -> Self { + DeviceName(device_name) + } +} + +impl From<&str> for DeviceName { + fn from(device_name: &str) -> Self { + DeviceName(String::from(device_name)) + } +} + +impl DeviceName { + pub fn generate() -> DeviceName { + let Base64Text(device_name) = rand_uuid_b64_url_enc(); + DeviceName(device_name) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DeviceData { + pub device_id: DeviceId, + pub device_name: DeviceName, + pub keys: OpenBox, +} + +/// Contains only public information about device +impl DeviceData { + pub fn from(device_name: DeviceName, open_box: OpenBox) -> Self { + Self { + device_name, + device_id: DeviceId::from(&open_box), + keys: open_box, + } + } +} + +impl Display for DeviceId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.clone()) + } +} + +impl From<&str> for DeviceId { + fn from(id: &str) -> Self { + Self(id.to_string()) + } +} + +impl From<&OpenBox> for DeviceId { + fn from(open_box: &OpenBox) -> Self { + let dsa_pk = String::from(&open_box.dsa_pk); + let id = generate_uuid_b64_url_enc(dsa_pk); + Self(id) + } +} diff --git a/core/src/node/common/model/device/device_creds.rs b/core/src/node/common/model/device/device_creds.rs new file mode 100644 index 00000000..fbc81eac --- /dev/null +++ b/core/src/node/common/model/device/device_creds.rs @@ -0,0 +1,47 @@ +use crate::crypto::keys::{KeyManager, OpenBox, SecretBox}; +use crate::node::common::model::device::common::{DeviceData, DeviceName}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DeviceCredentials { + pub secret_box: SecretBox, + pub device: DeviceData, +} + +/// Contains full information about device (private keys and device id) +impl DeviceCredentials { + pub fn generate(device_name: DeviceName) -> DeviceCredentials { + let secret_box = KeyManager::generate_secret_box(); + let device = DeviceData::from(device_name, OpenBox::from(&secret_box)); + DeviceCredentials { secret_box, device } + } +} + +impl DeviceCredentials { + pub fn key_manager(&self) -> anyhow::Result { + let key_manager = KeyManager::try_from(&self.secret_box)?; + Ok(key_manager) + } +} + +#[cfg(test)] +pub mod fixture { + use crate::node::common::model::device::common::DeviceName; + use crate::node::common::model::device::device_creds::DeviceCredentials; + + pub struct DeviceCredentialsFixture { + pub client: DeviceCredentials, + pub vd: DeviceCredentials, + pub server: DeviceCredentials + } + + impl DeviceCredentialsFixture { + pub fn generate() -> Self { + Self { + client: DeviceCredentials::generate(DeviceName::client()), + vd: DeviceCredentials::generate(DeviceName::virtual_device()), + server: DeviceCredentials::generate(DeviceName::server()), + } + } + } +} diff --git a/core/src/node/common/model/device.rs b/core/src/node/common/model/device/device_link.rs similarity index 59% rename from core/src/node/common/model/device.rs rename to core/src/node/common/model/device/device_link.rs index 98a97561..3a99f6ce 100644 --- a/core/src/node/common/model/device.rs +++ b/core/src/node/common/model/device/device_link.rs @@ -1,41 +1,5 @@ -use std::fmt::Display; - -use anyhow::{anyhow, Ok}; -use wasm_bindgen::prelude::wasm_bindgen; -use crypto::utils::generate_uuid_b64_url_enc; - -use crate::crypto; -use crate::crypto::encoding::base64::Base64Text; -use crate::crypto::keys::SecretBox; -use crate::crypto::keys::{KeyManager, OpenBox}; -use crate::crypto::utils::rand_uuid_b64_url_enc; - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DeviceId(String); - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DeviceName(String); - -impl From for DeviceName { - fn from(device_name: String) -> Self { - DeviceName(device_name) - } -} - -impl From<&str> for DeviceName { - fn from(device_name: &str) -> Self { - DeviceName(String::from(device_name)) - } -} - -impl DeviceName { - pub fn generate() -> DeviceName { - let Base64Text(device_name) = rand_uuid_b64_url_enc(); - DeviceName(device_name) - } -} +use anyhow::anyhow; +use crate::node::common::model::device::common::DeviceId; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -166,68 +130,14 @@ impl DeviceLink { } } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DeviceData { - pub id: DeviceId, - pub name: DeviceName, - pub keys: OpenBox, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DeviceCredentials { - pub secret_box: SecretBox, - pub device: DeviceData, -} - -/// Contains full information about device (private keys and device id) -impl DeviceCredentials { - pub fn generate(device_name: DeviceName) -> DeviceCredentials { - let secret_box = KeyManager::generate_secret_box(); - let device = DeviceData::from(device_name, OpenBox::from(&secret_box)); - DeviceCredentials { secret_box, device } - } - - pub fn key_manager(&self) -> anyhow::Result { - let key_manager = KeyManager::try_from(&self.secret_box)?; - Ok(key_manager) - } -} - -impl Display for DeviceId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0.clone()) - } -} - -/// Contains only public information about device -impl DeviceData { - pub fn from(device_name: DeviceName, open_box: OpenBox) -> Self { - Self { - name: device_name, - id: DeviceId::from(&open_box), - keys: open_box, - } - } -} - -impl From<&OpenBox> for DeviceId { - fn from(open_box: &OpenBox) -> Self { - let dsa_pk = String::from(&open_box.dsa_pk); - let id = generate_uuid_b64_url_enc(dsa_pk); - Self(id) - } -} - #[cfg(test)] mod test { use super::*; #[test] fn test_device_link_builder() -> anyhow::Result<()> { - let sender = DeviceId(String::from("sender")); - let receiver = DeviceId(String::from("receiver")); + let sender = DeviceId::from("sender"); + let receiver = DeviceId::from("receiver"); let device_link = DeviceLinkBuilder::builder() .sender(sender.clone()) diff --git a/core/src/node/common/model/device/mod.rs b/core/src/node/common/model/device/mod.rs new file mode 100644 index 00000000..cf588a11 --- /dev/null +++ b/core/src/node/common/model/device/mod.rs @@ -0,0 +1,3 @@ +pub mod device_link; +pub mod device_creds; +pub mod common; diff --git a/core/src/node/common/model/mod.rs b/core/src/node/common/model/mod.rs index 095dcd7a..3e169757 100644 --- a/core/src/node/common/model/mod.rs +++ b/core/src/node/common/model/mod.rs @@ -1,13 +1,10 @@ -use std::fmt::Display; -use wasm_bindgen::prelude::wasm_bindgen; -use crate::node::common::model::device::DeviceData; use crate::node::common::model::secret::MetaPasswordId; -use crate::node::common::model::user::UserCredentials; use crate::node::common::model::vault::VaultStatus; +use crate::node::common::model::device::common::DeviceData; pub mod device; -pub mod user; pub mod vault; +pub mod user; pub mod crypto { use crate::CoreResult; @@ -21,7 +18,7 @@ pub mod crypto { use crate::crypto::encoding::base64::Base64Text; use crate::crypto::key_pair::{CryptoBoxPublicKey, CryptoBoxSecretKey}; use crate::errors::CoreError; - use crate::node::common::model::device::DeviceLink; + use crate::node::common::model::device::device_link::DeviceLink; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -215,10 +212,9 @@ pub mod secret { use crate::crypto::utils; use crate::node::common::model::crypto::EncryptedMessage; + use crate::node::common::model::device::device_link::PeerToPeerDeviceLink; use crate::node::common::model::vault::VaultName; - use super::device::PeerToPeerDeviceLink; - #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MetaPasswordId { @@ -320,42 +316,11 @@ pub mod secret { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum RegistrationStatus { - Registered, - AlreadyExists, -} - -impl Display for RegistrationStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - Self::Registered => String::from("Registered"), - Self::AlreadyExists => String::from("AlreadyExists"), - }; - write!(f, "{}", str) - } -} - -impl Default for RegistrationStatus { - fn default() -> RegistrationStatus { - Self::Registered - } -} - #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum ApplicationState { - Empty, - Local{ - device: DeviceData - }, - User { - user: UserCredentials - }, - Vault { - vault: VaultStatus - } + Local { device: DeviceData }, + Vault { vault: VaultStatus }, } #[cfg(test)] diff --git a/core/src/node/common/model/user.rs b/core/src/node/common/model/user/common.rs similarity index 56% rename from core/src/node/common/model/user.rs rename to core/src/node/common/model/user/common.rs index 2458cb29..44ae35fa 100644 --- a/core/src/node/common/model/user.rs +++ b/core/src/node/common/model/user/common.rs @@ -1,4 +1,4 @@ -use crate::node::common::model::device::{DeviceCredentials, DeviceData, DeviceId, DeviceName}; +use crate::node::common::model::device::common::{DeviceData, DeviceId}; use crate::node::common::model::vault::VaultName; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -23,48 +23,7 @@ impl UserData { pub fn user_id(&self) -> UserId { UserId { vault_name: self.vault_name.clone(), - device_id: self.device.id.clone(), - } - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UserCredentials { - pub vault_name: VaultName, - pub device_creds: DeviceCredentials, -} - -impl UserCredentials { - pub fn from(device_creds: DeviceCredentials, vault_name: VaultName) -> UserCredentials { - UserCredentials { - vault_name, - device_creds, - } - } - - pub fn generate(device_name: DeviceName, vault_name: VaultName) -> UserCredentials { - UserCredentials { - vault_name, - device_creds: DeviceCredentials::generate(device_name), - } - } - - pub fn device(&self) -> DeviceData { - self.device_creds.device.clone() - } - - pub fn user(&self) -> UserData { - UserData { - vault_name: self.vault_name.clone(), - device: self.device(), - } - } - - pub fn user_id(&self) -> UserId { - UserId { - vault_name: self.vault_name.clone(), - device_id: self.device().id.clone(), + device_id: self.device.device_id.clone(), } } } @@ -94,19 +53,23 @@ pub struct UserDataOutsider { } impl UserDataOutsider { - pub fn unknown(user_data: UserData) -> Self { + pub fn non_member(user_data: UserData) -> Self { Self { user_data, - status: UserDataOutsiderStatus::Unknown, + status: UserDataOutsiderStatus::NonMember, } } + + pub fn is_non_member(&self) -> bool { + self.status == UserDataOutsiderStatus::NonMember + } } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum UserDataOutsiderStatus { - /// Unknown status (the user is not a member of the vault) - Unknown, + /// Unknown status (the user is not a member of the vault), but the vault exists + NonMember, Pending, Declined, } @@ -120,6 +83,6 @@ impl UserMembership { } pub fn device_id(&self) -> DeviceId { - self.user_data().device.id.clone() + self.user_data().device.device_id.clone() } } diff --git a/core/src/node/common/model/user/mod.rs b/core/src/node/common/model/user/mod.rs new file mode 100644 index 00000000..0cf34dbd --- /dev/null +++ b/core/src/node/common/model/user/mod.rs @@ -0,0 +1,2 @@ +pub mod user_creds; +pub mod common; \ No newline at end of file diff --git a/core/src/node/common/model/user/user_creds.rs b/core/src/node/common/model/user/user_creds.rs new file mode 100644 index 00000000..e64e0165 --- /dev/null +++ b/core/src/node/common/model/user/user_creds.rs @@ -0,0 +1,73 @@ +use crate::node::common::model::device::common::{DeviceData, DeviceName}; +use crate::node::common::model::device::device_creds::DeviceCredentials; +use crate::node::common::model::user::common::{UserData, UserId}; +use crate::node::common::model::vault::VaultName; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserCredentials { + pub vault_name: VaultName, + pub device_creds: DeviceCredentials, +} + +impl UserCredentials { + pub fn from(device_creds: DeviceCredentials, vault_name: VaultName) -> UserCredentials { + UserCredentials { + vault_name, + device_creds, + } + } + + pub fn generate(device_name: DeviceName, vault_name: VaultName) -> UserCredentials { + UserCredentials { + vault_name, + device_creds: DeviceCredentials::generate(device_name), + } + } + + pub fn device(&self) -> DeviceData { + self.device_creds.device.clone() + } + + pub fn user(&self) -> UserData { + UserData { + vault_name: self.vault_name.clone(), + device: self.device(), + } + } + + pub fn user_id(&self) -> UserId { + UserId { + vault_name: self.vault_name.clone(), + device_id: self.device().device_id.clone(), + } + } +} + +#[cfg(test)] +pub mod fixture { + use crate::node::common::model::device::common::DeviceName; + use crate::node::common::model::device::device_creds::fixture::DeviceCredentialsFixture; + use crate::node::common::model::user::user_creds::UserCredentials; + use crate::node::common::model::vault::VaultName; + + pub struct UserCredentialsFixture { + pub client: UserCredentials, + pub vd: UserCredentials, + } + + impl UserCredentialsFixture { + pub fn client_device_name(&self) -> DeviceName { + self.client.device_creds.device.device_name.clone() + } + } + + impl UserCredentialsFixture { + pub fn from(device_creds: &DeviceCredentialsFixture) -> Self { + Self { + client: UserCredentials::from(device_creds.client.clone(), VaultName::test()), + vd: UserCredentials::from(device_creds.vd.clone(), VaultName::test()) + } + } + } +} diff --git a/core/src/node/common/model/vault.rs b/core/src/node/common/model/vault.rs index f539b306..9b6a4456 100644 --- a/core/src/node/common/model/vault.rs +++ b/core/src/node/common/model/vault.rs @@ -1,12 +1,10 @@ +use crate::node::common::model::MetaPasswordId; use std::collections::{HashMap, HashSet}; use std::fmt::Display; -use wasm_bindgen::prelude::wasm_bindgen; -use crate::node::common::model::device::DeviceId; -use crate::node::common::model::user::{UserData, UserDataMember, UserDataOutsider, UserDataOutsiderStatus, UserMembership}; -use crate::node::common::model::MetaPasswordId; - +use crate::node::common::model::device::common::DeviceId; +use crate::node::common::model::device::device_link::DeviceLink; +use crate::node::common::model::user::common::{UserData, UserDataMember, UserDataOutsider, UserMembership}; use super::crypto::CommunicationChannel; -use super::device::DeviceLink; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -30,6 +28,13 @@ impl Display for VaultName { } } +impl VaultName { + pub fn test() -> VaultName { + VaultName::from("q") + } +} + +/////////////////// VaultData /////////////////// #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VaultData { @@ -68,27 +73,27 @@ impl VaultData { self.users.insert(membership.device_id(), membership); } + pub fn is_not_member(&self, device_id: &DeviceId) -> bool { + !self.is_member(device_id) + } + pub fn is_member(&self, device_id: &DeviceId) -> bool { let maybe_user = self.users.get(device_id); if let Some(UserMembership::Member(UserDataMember(user_data))) = maybe_user { - user_data.device.id == device_id.clone() + user_data.device.device_id.eq(&device_id) } else { false } } - pub fn status(&self, for_user: UserData) -> VaultStatus { - let maybe_vault_user = self.users.get(&for_user.device.id); - - match maybe_vault_user { - Some(vault_user) => match vault_user { - UserMembership::Outsider(outsider) => VaultStatus::Outsider(outsider.clone()), - UserMembership::Member(member) => VaultStatus::Member { - member: member.clone(), - vault: self.clone(), - }, - }, - None => VaultStatus::Outsider(UserDataOutsider::unknown(for_user)), + pub fn membership(&self, for_user: UserData) -> UserMembership { + let maybe_vault_user = self.users + .get(&for_user.device.device_id); + + if let Some(membership) = maybe_vault_user { + membership.clone() + } else { + UserMembership::Outsider(UserDataOutsider::non_member(for_user)) } } @@ -142,35 +147,26 @@ impl VaultData { } } +/////////////////// VaultStatus /////////////////// #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum VaultStatus { + NotExists(UserData), Outsider(UserDataOutsider), Member { member: UserDataMember, vault: VaultData }, } impl VaultStatus { pub fn unknown(user: UserData) -> Self { - VaultStatus::Outsider(UserDataOutsider::unknown(user)) - } - - pub fn is_new_user(&self) -> bool { - match self { - VaultStatus::Outsider(UserDataOutsider {status, ..}) => { - match status { - UserDataOutsiderStatus::Unknown => true, - UserDataOutsiderStatus::Pending => false, - UserDataOutsiderStatus::Declined => false - } - } - VaultStatus::Member { .. } => false - } + VaultStatus::Outsider(UserDataOutsider::non_member(user)) } - + pub fn user(&self) -> UserData { match self { - VaultStatus::Outsider(UserDataOutsider {user_data, ..}) => user_data.clone(), - VaultStatus::Member {member, ..} => member.user().clone() + VaultStatus::NotExists(user) => user.clone(), + VaultStatus::Outsider(UserDataOutsider { user_data, .. }) => user_data.clone(), + VaultStatus::Member { member, .. } => member.user().clone(), } } } + diff --git a/core/src/node/db/actions/mod.rs b/core/src/node/db/actions/mod.rs index c7bc2e54..88f2a5ed 100644 --- a/core/src/node/db/actions/mod.rs +++ b/core/src/node/db/actions/mod.rs @@ -1,3 +1,3 @@ pub mod recover; pub mod sign_up; -pub mod sign_up_claim; +pub mod vault; diff --git a/core/src/node/db/actions/recover.rs b/core/src/node/db/actions/recover.rs index 43beac12..d574f355 100644 --- a/core/src/node/db/actions/recover.rs +++ b/core/src/node/db/actions/recover.rs @@ -1,16 +1,9 @@ use std::sync::Arc; -use anyhow::anyhow; use tracing_attributes::instrument; -use crate::node::common::model::device::{DeviceData, DeviceLinkBuilder}; use crate::node::common::model::secret::MetaPasswordId; -use crate::node::common::model::user::UserDataMember; -use crate::node::common::model::vault::VaultStatus; use crate::node::common::model::ApplicationState; -use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; -use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; -use crate::node::db::events::object_id::ObjectId; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::repo::generic_db::KvLogEventRepo; @@ -23,8 +16,8 @@ impl RecoveryAction { #[instrument(skip_all)] pub async fn recovery_request( &self, - meta_pass_id: MetaPasswordId, - app_state: &ApplicationState, + _meta_pass_id: MetaPasswordId, + _app_state: &ApplicationState, ) -> anyhow::Result<()> { /* let Some(sender_device) = app_state.device.clone() else { diff --git a/core/src/node/db/actions/sign_up.rs b/core/src/node/db/actions/sign_up.rs deleted file mode 100644 index 2196f2ab..00000000 --- a/core/src/node/db/actions/sign_up.rs +++ /dev/null @@ -1,139 +0,0 @@ -use tracing_attributes::instrument; - -use crate::node::common::model::device::DeviceData; -use crate::node::common::model::user::{UserData, UserDataMember, UserId, UserMembership}; -use crate::node::common::model::vault::VaultData; -use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; -use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; -use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent}; -use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; -use crate::node::db::events::object_id::{Next, UnitId, VaultGenesisEvent, VaultUnitEvent}; -use crate::node::db::events::vault_event::{VaultLogObject, VaultMembershipObject, VaultObject}; - -pub struct SignUpAction {} - -impl SignUpAction { - #[instrument(skip_all)] - pub fn accept(&self, candidate: UserData, server: DeviceData) -> Vec { - let mut commit_log = vec![]; - - let vault_name = candidate.vault_name.clone(); - - let vault_log_events = { - let vault_log_obj_desc = VaultDescriptor::vault_log(vault_name.clone()); - let unit_event = VaultLogObject::Unit(VaultUnitEvent(KvLogEvent { - key: KvKey::unit(vault_log_obj_desc.clone()), - value: vault_name.clone(), - })) - .to_generic(); - - let genesis_event = VaultLogObject::Genesis(VaultGenesisEvent(KvLogEvent { - key: KvKey::genesis(vault_log_obj_desc), - value: candidate.clone(), - })) - .to_generic(); - - vec![unit_event, genesis_event] - }; - commit_log.extend(vault_log_events); - - let vault_events = { - let vault_obj_desc = VaultDescriptor::vault(vault_name.clone()); - let unit_event = VaultObject::Unit(VaultUnitEvent(KvLogEvent { - key: KvKey::unit(vault_obj_desc.clone()), - value: vault_name.clone(), - })) - .to_generic(); - - let genesis_event = VaultObject::Genesis(KvLogEvent { - key: KvKey::genesis(vault_obj_desc.clone()), - value: server, - }) - .to_generic(); - - let vault_event = { - let vault_data = { - let mut vault = VaultData::from(vault_name.clone()); - let membership = UserMembership::Member(UserDataMember(candidate.clone())); - vault.update_membership(membership); - vault - }; - - let vault_id = UnitId::vault_unit(vault_name.clone()).next().next(); - - let sign_up_event = KvLogEvent { - key: KvKey::artifact(vault_obj_desc.clone(), vault_id), - value: vault_data, - }; - VaultObject::Vault(sign_up_event).to_generic() - }; - - vec![unit_event, genesis_event, vault_event] - }; - commit_log.extend(vault_events); - - let vault_status_events = { - let user_id = UserId { - vault_name: vault_name.clone(), - device_id: candidate.device.id.clone(), - }; - let vault_status_desc = VaultDescriptor::VaultMembership(user_id).to_obj_desc(); - - let unit_event = VaultMembershipObject::Unit(VaultUnitEvent(KvLogEvent { - key: KvKey::unit(vault_status_desc.clone()), - value: vault_name.clone(), - })) - .to_generic(); - - let genesis_event = VaultMembershipObject::Genesis(VaultGenesisEvent(KvLogEvent { - key: KvKey::genesis(vault_status_desc.clone()), - value: candidate.clone(), - })) - .to_generic(); - - let status_event = { - let status_event_id = UnitId::unit(&vault_status_desc).next().next(); - - VaultMembershipObject::Membership(KvLogEvent { - key: KvKey { - obj_id: status_event_id, - obj_desc: vault_status_desc, - }, - value: UserMembership::Member(UserDataMember(candidate.clone())), - }) - .to_generic() - }; - - vec![unit_event, genesis_event, status_event] - }; - commit_log.extend(vault_status_events); - - commit_log - } -} - -#[cfg(test)] -mod test { - use anyhow::Result; - - use crate::{ - meta_tests::fixture::ClientDeviceFixture, - node::{ - common::model::device::{DeviceCredentials, DeviceName}, - db::actions::sign_up::SignUpAction, - }, - }; - - #[tokio::test] - async fn test() -> Result<()> { - let client_fixture = ClientDeviceFixture::default(); - let server_creds = DeviceCredentials::generate(DeviceName::from("server_device")); - - let sign_up_action = SignUpAction {}; - let events = sign_up_action.accept(client_fixture.user_creds.user(), server_creds.device); - - assert_eq!(events.len(), 8); - - Ok(()) - } -} diff --git a/core/src/node/db/actions/sign_up/action.rs b/core/src/node/db/actions/sign_up/action.rs new file mode 100644 index 00000000..a767de5e --- /dev/null +++ b/core/src/node/db/actions/sign_up/action.rs @@ -0,0 +1,95 @@ +use log::info; +use tracing_attributes::instrument; +use crate::node::common::model::device::common::DeviceData; +use crate::node::common::model::user::common::UserData; +use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent}; +use crate::node::db::events::vault::vault_log_event::VaultLogObject; +use crate::node::db::events::vault_event::{VaultMembershipObject, VaultObject}; + +pub struct SignUpAction {} + +impl SignUpAction { + #[instrument(skip(self))] + pub fn accept(&self, candidate: UserData, server: DeviceData) -> Vec { + info!("Create new vault"); + + let mut commit_log = vec![]; + + let vault_name = candidate.vault_name.clone(); + + let vault_log_events = { + let unit_event = VaultLogObject::unit(vault_name.clone()) + .to_generic(); + let genesis_event = VaultLogObject::genesis(vault_name.clone(), candidate.clone()) + .to_generic(); + + vec![unit_event, genesis_event] + }; + commit_log.extend(vault_log_events); + + let vault_events = { + let unit_event = VaultObject::unit(vault_name.clone()) + .to_generic(); + let genesis_event = VaultObject::genesis(vault_name.clone(), server) + .to_generic(); + let vault_event = VaultObject::sign_up(vault_name.clone(), candidate.clone()) + .to_generic(); + + vec![unit_event, genesis_event, vault_event] + }; + commit_log.extend(vault_events); + + let vault_status_events = VaultMembershipObject::init(candidate); + commit_log.extend(vault_status_events); + + commit_log + } +} + +#[cfg(test)] +mod test { + use anyhow::Result; + + use crate::{ + node::common::model::user::user_creds::fixture::UserCredentialsFixture, + node::db::actions::sign_up::action::SignUpAction + }; + use crate::node::common::model::device::device_creds::fixture::DeviceCredentialsFixture; + use crate::node::db::events::generic_log_event::GenericKvLogEvent; + use crate::node::db::events::vault::vault_log_event::VaultLogObject; + + #[tokio::test] + async fn test() -> Result<()> { + let device_creds = &DeviceCredentialsFixture::generate(); + let user_creds_fixture = UserCredentialsFixture::from(device_creds); + + let sign_up_action = SignUpAction {}; + let events = sign_up_action + .accept(user_creds_fixture.client.user(), device_creds.server.device.clone()); + + assert_eq!(events.len(), 8); + + let mut unit_event = false; + let mut genesis_event = false; + for event in events { + if let GenericKvLogEvent::VaultLog(obj) = event { + match obj { + VaultLogObject::Unit(_) => { + unit_event = true + } + VaultLogObject::Genesis(_) => { + genesis_event = true + } + VaultLogObject::Action(_) => { + //ignore + } + } + } + } + + assert!(unit_event); + assert!(genesis_event); + + Ok(()) + } +} diff --git a/core/src/node/db/actions/sign_up/claim.rs b/core/src/node/db/actions/sign_up/claim.rs new file mode 100644 index 00000000..011558e5 --- /dev/null +++ b/core/src/node/db/actions/sign_up/claim.rs @@ -0,0 +1,199 @@ +use std::sync::Arc; + +use crate::node::common::model::user::common::UserData; +use crate::node::{ + common::model::vault::VaultStatus, + db::{ + objects::{ + persistent_device_log::PersistentDeviceLog, persistent_object::PersistentObject, + persistent_vault::PersistentVault, + }, + repo::generic_db::KvLogEventRepo, + }, +}; +use anyhow::Ok; +use log::info; +use crate::node::db::objects::persistent_shared_secret::PersistentSharedSecret; +use crate::node::db::repo::persistent_credentials::PersistentCredentials; + +pub struct SignUpClaim { + pub p_obj: Arc>, +} + +impl SignUpClaim { + pub async fn sign_up(&self, user_data: UserData) -> anyhow::Result { + let creds_repo = PersistentCredentials { p_obj: self.p_obj.clone() }; + let p_device_log = PersistentDeviceLog { p_obj: self.p_obj.clone() }; + let p_vault = PersistentVault { p_obj: self.p_obj.clone(), }; + + let device_name = user_data.device.device_name.clone(); + let vault_name = user_data.vault_name.clone(); + creds_repo + .get_or_generate_user_creds(device_name, vault_name) + .await?; + + let vault_status = p_vault.find(user_data.clone()).await?; + + match &vault_status { + VaultStatus::NotExists(user_data) => { + info!("Vault doesn't exists"); + p_device_log.save_create_vault_request(user_data).await?; + } + VaultStatus::Outsider(outsider) => { + if outsider.is_non_member() { + p_device_log.save_join_request(&outsider.user_data).await?; + } else { + info!("Device is pending or declined") + } + } + VaultStatus::Member { .. } => { + //trace!("User is already a vault member: {:?}", member); + } + } + + let p_ss_device_log = PersistentSharedSecret { + p_obj: self.p_obj.clone(), + }; + + p_ss_device_log.init(user_data).await?; + + Ok(vault_status.clone()) + } +} + +#[cfg(test)] +pub mod test_action { + use crate::node::common::model::user::user_creds::fixture::UserCredentialsFixture; + use crate::node::common::model::vault::VaultStatus; + use crate::node::db::actions::sign_up::claim::SignUpClaim; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::objects::persistent_object::PersistentObject; + use std::sync::Arc; + use tracing::info; + use tracing_attributes::instrument; + + pub struct SignUpClaimTestAction { + sign_up: SignUpClaim, + } + + impl SignUpClaimTestAction { + + #[instrument(skip_all)] + pub async fn sign_up( + p_obj: Arc>, + creds_fixture: &UserCredentialsFixture + ) -> anyhow::Result { + info!("SignUp action"); + + let sign_up_claim = SignUpClaim { + p_obj: p_obj.clone(), + }; + + let status = sign_up_claim.sign_up(creds_fixture.client.user()).await?; + + Ok(status) + } + } +} + +#[cfg(test)] +pub mod spec { + use std::sync::Arc; + use async_trait::async_trait; + use crate::meta_tests::spec::test_spec::TestSpec; + use crate::node::common::model::user::common::UserData; + use crate::node::db::objects::persistent_device_log::spec::DeviceLogSpec; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::objects::persistent_shared_secret::spec::SSDeviceLogSpec; + use crate::node::db::repo::generic_db::KvLogEventRepo; + use anyhow::Result; + use log::info; + use tracing_attributes::instrument; + use crate::node::common::model::device::common::DeviceData; + use crate::node::db::objects::global_index::spec::GlobalIndexSpec; + use crate::node::db::objects::persistent_vault::spec::VaultLogSpec; + + pub struct SignUpClaimSpec { + pub p_obj: Arc>, + pub user: UserData, + pub server_device: DeviceData + } + + #[async_trait(? Send)] + impl TestSpec for SignUpClaimSpec { + #[instrument(skip(self))] + async fn verify(&self) -> Result<()> { + info!("SignUp claim spec"); + + let gi_spec = GlobalIndexSpec { + repo: self.p_obj.repo.clone(), + server_device: self.server_device.clone() + }; + gi_spec.verify().await?; + + let device_log_spec = DeviceLogSpec { + p_obj: self.p_obj.clone(), + user: self.user.clone(), + }; + + device_log_spec.check_initialization().await?; + device_log_spec.check_sign_up_request().await?; + + let vault_log_spec = VaultLogSpec { + p_obj: self.p_obj.clone(), + user: self.user.clone(), + }; + + //vault_log_spec.verify_initial_state().await?; + + let ss_device_log_spec = SSDeviceLogSpec { + p_obj: self.p_obj.clone(), + client_user: self.user.clone(), + }; + + ss_device_log_spec.check_initialization().await?; + + Ok(()) + } + } + +} + +#[cfg(test)] +mod test { + use anyhow::{bail, Result}; + + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::node::db::actions::sign_up::claim::test_action::SignUpClaimTestAction; + use crate::{node::common::model::vault::VaultStatus}; + use crate::meta_tests::spec::test_spec::TestSpec; + use crate::node::db::actions::sign_up::claim::spec::SignUpClaimSpec; + + #[tokio::test] + #[ignore] + async fn test_sign_up() -> Result<()> { + + let registry = FixtureRegistry::extended().await?; + let p_obj = registry.state.base.empty.p_obj.client.clone(); + let creds = registry.state.base.empty.user_creds; + + let vault_status = SignUpClaimTestAction::sign_up(p_obj.clone(), &creds) + .await?; + + let VaultStatus::Outsider(outsider) = vault_status else { + bail!("Invalid state: {:?}", vault_status); + }; + + let db = p_obj.repo.get_db().await; + assert_eq!(db.len(), 8); + + let claim_spec = SignUpClaimSpec { + p_obj, + user: outsider.user_data, + server_device: registry.state.base.empty.device_creds.server.device, + }; + claim_spec.verify().await?; + + Ok(()) + } +} diff --git a/core/src/node/db/actions/sign_up/mod.rs b/core/src/node/db/actions/sign_up/mod.rs new file mode 100644 index 00000000..eaf69032 --- /dev/null +++ b/core/src/node/db/actions/sign_up/mod.rs @@ -0,0 +1,2 @@ +pub mod action; +pub mod claim; diff --git a/core/src/node/db/actions/sign_up_claim.rs b/core/src/node/db/actions/sign_up_claim.rs deleted file mode 100644 index 9609cf1b..00000000 --- a/core/src/node/db/actions/sign_up_claim.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::sync::Arc; - -use anyhow::{bail, Ok}; - -use crate::node::{ - common::model::{ - user::{UserData, UserDataOutsiderStatus}, - vault::VaultStatus, - }, - db::{ - events::local_event::CredentialsObject, - objects::{ - persistent_device_log::PersistentDeviceLog, persistent_object::PersistentObject, - persistent_shared_secret::PersistentSharedSecret, persistent_vault::PersistentVault, - }, - repo::{credentials_repo::CredentialsRepo, generic_db::KvLogEventRepo}, - }, -}; - -pub struct SignUpClaim { - pub p_obj: Arc>, -} - -impl SignUpClaim { - pub async fn sign_up(&self) -> anyhow::Result { - let (user, vault_status) = self.get_vault_status().await?; - - let VaultStatus::Outsider(outsider) = &vault_status else { - return Ok(vault_status); - }; - - let UserDataOutsiderStatus::Unknown = &outsider.status else { - return Ok(vault_status); - }; - - let p_device_log = PersistentDeviceLog { - p_obj: self.p_obj.clone(), - }; - - p_device_log.save_create_vault_request(&user).await?; - - //Init SSDeviceLog - let p_ss = PersistentSharedSecret { - p_obj: self.p_obj.clone(), - }; - p_ss.init(user.clone()).await?; - - Ok(vault_status) - } - - pub async fn get_vault_status(&self) -> anyhow::Result<(UserData, VaultStatus)> { - let p_vault = PersistentVault { - p_obj: self.p_obj.clone(), - }; - - let vault_status = p_vault.find_for_default_user().await?; - Ok((vault_status.user(), vault_status)) - } -} - -#[cfg(test)] -mod test { - use anyhow::{bail, Result}; - use std::sync::Arc; - - use crate::{ - meta_tests::{ - action::sign_up_claim_action::SignUpClaimTestAction, - spec::{sign_up_claim_spec::SignUpClaimSpec, test_spec::TestSpec}, - }, - node::{ - common::model::vault::VaultStatus, - db::{in_mem_db::InMemKvLogEventRepo, objects::persistent_object::PersistentObject}, - }, - }; - - #[tokio::test] - async fn test_sign_up() -> Result<()> { - let repo = Arc::new(InMemKvLogEventRepo::default()); - let p_obj = Arc::new(PersistentObject::new(repo.clone())); - - let claim_action = SignUpClaimTestAction::new(p_obj.clone()); - let vault_status = claim_action.sign_up().await?; - let VaultStatus::Outsider(outsider) = vault_status else { - bail!("Invalid state"); - }; - - let db = repo.get_db().await; - assert_eq!(db.len(), 8); - - let claim_spec = SignUpClaimSpec { - p_obj, - user: outsider.user_data, - }; - claim_spec.verify().await?; - - Ok(()) - } -} diff --git a/core/src/node/db/actions/vault/mod.rs b/core/src/node/db/actions/vault/mod.rs new file mode 100644 index 00000000..e4fd401b --- /dev/null +++ b/core/src/node/db/actions/vault/mod.rs @@ -0,0 +1 @@ +pub mod vault_action; diff --git a/core/src/node/db/actions/vault/vault_action.rs b/core/src/node/db/actions/vault/vault_action.rs new file mode 100644 index 00000000..120e6881 --- /dev/null +++ b/core/src/node/db/actions/vault/vault_action.rs @@ -0,0 +1,206 @@ +use std::sync::Arc; +use anyhow::bail; +use tracing::info; +use crate::node::common::model::device::common::DeviceData; +use crate::node::common::model::user::common::{UserData, UserDataMember}; +use crate::node::common::model::vault::{VaultStatus}; +use crate::node::db::actions::sign_up::action::SignUpAction; +use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; +use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; +use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent}; +use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; +use crate::node::db::events::object_id::{Next, ObjectId}; +use crate::node::db::events::vault::vault_log_event::VaultLogObject; +use crate::node::db::events::vault_event::{VaultActionEvent, VaultMembershipObject, VaultObject}; +use crate::node::db::objects::global_index::ServerPersistentGlobalIndex; +use crate::node::db::objects::persistent_object::PersistentObject; +use crate::node::db::objects::persistent_vault::PersistentVault; +use crate::node::db::repo::generic_db::KvLogEventRepo; + +pub struct VaultAction { + pub p_obj: Arc>, + pub server_device: DeviceData +} + +impl VaultAction { + + pub async fn do_processing(&self, action_event: VaultActionEvent) -> anyhow::Result<()> { + let p_vault = PersistentVault { + p_obj: self.p_obj.clone(), + }; + + match &action_event { + VaultActionEvent::CreateVault(user) => { + let action = CreateVaultAction { + p_obj: self.p_obj.clone(), + server_device: self.server_device.clone(), + }; + action.create(user).await + } + + VaultActionEvent::JoinClusterRequest { candidate } => { + let vault_log_desc = VaultDescriptor::vault_log(candidate.vault_name.clone()); + + let vault_log_free_id = self + .p_obj + .find_free_id_by_obj_desc(vault_log_desc.clone()) + .await?; + + if let ObjectId::Artifact(vault_log_artifact_id) = vault_log_free_id { + let action = VaultLogObject::Action(KvLogEvent { + key: KvKey::artifact(vault_log_desc, vault_log_artifact_id), + value: action_event.clone(), + }); + let vault_log_event = GenericKvLogEvent::VaultLog(action); + + self.p_obj.repo.save(vault_log_event).await?; + anyhow::Ok(()) + } else { + bail!( + "JoinClusterRequest: Invalid vault log id, must be ArtifactId, but it's: {:?}", + vault_log_free_id + ); + } + } + + VaultActionEvent::UpdateMembership { + sender: UserDataMember(sender_user), + update, + } => { + let vault_name = action_event.vault_name(); + //check if a sender is a member of the vault and update the vault then + let vault_log_desc = VaultDescriptor::VaultLog(vault_name.clone()).to_obj_desc(); + + let vault_log_free_id = self + .p_obj + .find_free_id_by_obj_desc(vault_log_desc.clone()) + .await?; + + let ObjectId::Artifact(_) = vault_log_free_id else { + bail!( + "UpdateMembership: Invalid vault log id, must be ArtifactId, but it's: {:?}", + vault_log_free_id + ); + }; + + let (vault_artifact_id, vault) = p_vault.get_vault(&sender_user).await?; + + let vault_event = { + let mut new_vault = vault.clone(); + new_vault.update_membership(update.clone()); + + let key = KvKey { + obj_id: vault_artifact_id, + obj_desc: VaultDescriptor::vault(vault_name.clone()), + }; + VaultObject::Vault(KvLogEvent { key, value: new_vault }).to_generic() + }; + + self.p_obj.repo.save(vault_event).await?; + + let vault_status_free_id = { + let vault_membership_desc = { + VaultDescriptor::VaultMembership(update.user_data().user_id()).to_obj_desc() + }; + + self + .p_obj + .find_free_id_by_obj_desc(vault_membership_desc.clone()) + .await? + }; + + let vault_status_events = match vault_status_free_id { + ObjectId::Unit(_) => { + VaultMembershipObject::init(update.user_data()) + } + ObjectId::Genesis(artifact_id) => { + let genesis = VaultMembershipObject::genesis(update.user_data()).to_generic(); + let member = VaultMembershipObject::member(update.user_data(), artifact_id.next()) + .to_generic(); + vec![genesis, member] + } + ObjectId::Artifact(artifact_id) => { + let event = VaultMembershipObject::membership(update.clone(), artifact_id) + .to_generic(); + vec![event] + } + }; + + for vault_status_event in vault_status_events { + self.p_obj.repo.save(vault_status_event).await?; + } + anyhow::Ok(()) + } + + VaultActionEvent::AddMetaPassword { sender, meta_pass_id } => { + + let user = sender.user(); + let (vault_artifact_id, vault) = p_vault.get_vault(&user).await?; + + let vault_event = { + let mut new_vault = vault.clone(); + new_vault.add_secret(meta_pass_id.clone()); + + let event = KvLogEvent { + key: KvKey { + obj_id: vault_artifact_id, + obj_desc: VaultDescriptor::vault(user.vault_name.clone()), + }, + value: new_vault, + }; + + VaultObject::Vault(event).to_generic() + }; + + self.p_obj.repo.save(vault_event).await?; + + anyhow::Ok(()) + } + } + } +} + + +pub struct CreateVaultAction { + pub p_obj: Arc>, + pub server_device: DeviceData +} + +impl CreateVaultAction { + pub async fn create(&self, user: &UserData) -> anyhow::Result<()> { + // create vault if not exists + let p_vault = PersistentVault { + p_obj: self.p_obj.clone(), + }; + + let vault_status = p_vault.find(user.clone()).await?; + if let VaultStatus::NotExists(_) = vault_status { + //create vault_log, vault and vault status + self.create_vault(user.clone()).await + } else { + // vault already exists, and the event have been saved into vault_log already, + // no action needed + anyhow::Ok(()) + } + } + + async fn create_vault(&self, candidate: UserData) -> anyhow::Result<()> { + //vault not found, we can create our new vault + info!("Accept SignUp request, for the vault: {:?}", candidate.vault_name()); + + let sign_up_action = SignUpAction {}; + let sign_up_events = sign_up_action.accept(candidate.clone(), self.server_device.clone()); + + for sign_up_event in sign_up_events { + self.p_obj.repo.save(sign_up_event).await?; + } + + let p_gi = ServerPersistentGlobalIndex { + p_obj: self.p_obj.clone(), + server_device: self.server_device.clone() , + }; + p_gi.update(candidate.vault_name()).await?; + + anyhow::Ok(()) + } +} diff --git a/core/src/node/db/descriptors/global_index_descriptor.rs b/core/src/node/db/descriptors/global_index_descriptor.rs index 3f90eb1c..2f286c02 100644 --- a/core/src/node/db/descriptors/global_index_descriptor.rs +++ b/core/src/node/db/descriptors/global_index_descriptor.rs @@ -31,9 +31,16 @@ impl ObjectName for GlobalIndexDescriptor { match self { GlobalIndexDescriptor::Index => String::from("index"), GlobalIndexDescriptor::VaultIndex { vault_id } => { - let json_str = serde_json::to_string(&vault_id.id).unwrap(); + let id = [ + vault_id.id.fqdn.obj_type.clone(), + vault_id.id.fqdn.obj_instance.clone(), + vault_id.id.id.to_string() + ].join("-"); + //utils::generate_uuid_b64_url_enc(json_str) - json_str + //let json_str = serde_json::to_string(&vault_id.id).unwrap(); + //json_str + id } } } @@ -73,7 +80,7 @@ mod test { let expected = json!({ "objType":"VaultIdx", - "objInstance": "{\"fqdn\":{\"objType\":\"Vault\",\"objInstance\":\"test_vault\"},\"id\":0}" + "objInstance": "Vault-test_vault-0" }); assert_eq!(expected, vault_index_json); diff --git a/core/src/node/db/descriptors/shared_secret_descriptor.rs b/core/src/node/db/descriptors/shared_secret_descriptor.rs index a5551f1f..7434c6d0 100644 --- a/core/src/node/db/descriptors/shared_secret_descriptor.rs +++ b/core/src/node/db/descriptors/shared_secret_descriptor.rs @@ -1,4 +1,4 @@ -use crate::node::common::model::device::DeviceId; +use crate::node::common::model::device::common::DeviceId; use crate::node::common::model::secret::{MetaPasswordId, SSDistributionId}; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ObjectType, ToObjectDescriptor}; @@ -12,7 +12,8 @@ pub enum SharedSecretDescriptor { SSDeviceLog(DeviceId), /// Ledgers traditionally track financial transactions - /// but can be applied metaphorically to any situation where maintaining a detailed history of exchanges is crucial. + /// but can be applied metaphorically to any situation + /// where maintaining a detailed history of exchanges is crucial. /// In this case, the ledger logs password shards transferred between devices. SSLedger(VaultName), diff --git a/core/src/node/db/descriptors/vault_descriptor.rs b/core/src/node/db/descriptors/vault_descriptor.rs index 6a17ea77..901241a8 100644 --- a/core/src/node/db/descriptors/vault_descriptor.rs +++ b/core/src/node/db/descriptors/vault_descriptor.rs @@ -1,6 +1,4 @@ -use std::ops::Add; - -use crate::node::common::model::user::UserId; +use crate::node::common::model::user::common::UserId; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ObjectName, ObjectType, ToObjectDescriptor}; @@ -41,12 +39,8 @@ impl VaultDescriptor { impl ObjectType for VaultDescriptor { fn object_type(&self) -> String { match self { - VaultDescriptor::DeviceLog(user_id) => { - String::from("DeviceLog:").add(user_id.device_id.to_string().as_str()) - } - VaultDescriptor::VaultMembership(user_id) => { - String::from("VaultStatus:").add(user_id.device_id.to_string().as_str()) - } + VaultDescriptor::DeviceLog(_) => String::from("DeviceLog"), + VaultDescriptor::VaultMembership(_) => String::from("VaultStatus"), VaultDescriptor::Vault(_) => String::from("Vault"), VaultDescriptor::VaultLog(_) => String::from("VaultLog"), } @@ -57,30 +51,27 @@ impl ObjectName for VaultDescriptor { fn object_name(&self) -> String { match self { VaultDescriptor::Vault(vault_name) => vault_name.to_string(), - VaultDescriptor::DeviceLog(user_id) => user_id.vault_name.to_string(), + VaultDescriptor::DeviceLog(user_id) => user_id.device_id.to_string(), VaultDescriptor::VaultLog(vault_name) => vault_name.to_string(), - VaultDescriptor::VaultMembership(user_id) => user_id.vault_name.to_string(), + VaultDescriptor::VaultMembership(user_id) => user_id.device_id.to_string(), } } } #[cfg(test)] pub mod test { - use std::ops::Add; - use serde_json::json; - use crate::crypto::keys::{KeyManager, OpenBox}; - use crate::node::common::model::device::DeviceId; - use crate::node::common::model::user::UserId; + use crate::node::common::model::device::common::DeviceName; + use crate::node::common::model::user::user_creds::UserCredentials; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::object_descriptor::{ObjectName, ObjectType}; use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; - use crate::node::db::events::object_id::UnitId; + use crate::node::db::events::object_id::{ObjectId, UnitId}; #[test] fn test_vault_naming() { - let vault_name = VaultName::from("test"); + let vault_name = VaultName::test(); let descriptor = VaultDescriptor::vault(vault_name.clone()); assert_eq!(descriptor.object_type(), "Vault"); assert_eq!(descriptor.object_name(), vault_name.to_string()); @@ -96,21 +87,17 @@ pub mod test { #[test] fn test_device_log_naming() { - let vault_name = VaultName::from("test_vault"); - let device_id = { - let secret_box = KeyManager::generate_secret_box(); - DeviceId::from(&OpenBox::from(&secret_box)) - }; - - let user_id = UserId { - device_id: device_id.clone(), - vault_name: vault_name.clone(), - }; - let descriptor = VaultDescriptor::device_log(user_id); - let device_log_type = String::from("DeviceLog:").add(device_id.to_string().as_str()); + let vault_name = VaultName::test(); + let user_creds = UserCredentials::generate(DeviceName::client(), vault_name.clone()); + let user_id = user_creds.user_id(); + + let descriptor = VaultDescriptor::device_log(user_id.clone()); + let device_log_type = String::from("DeviceLog"); + + println!("{:?}", ObjectId::unit(descriptor.clone()).id_str()); assert_eq!(descriptor.object_type(), device_log_type); - assert_eq!(descriptor.object_name(), vault_name.to_string()); + assert_eq!(descriptor.object_name(), user_id.device_id.to_string()); let unit_id = UnitId::unit(&descriptor); @@ -118,7 +105,7 @@ pub mod test { let expected = json!({ "fqdn": { "objType": device_log_type, - "objInstance": vault_name.to_string() + "objInstance": user_id.device_id.to_string() }, "id": 0 }); diff --git a/core/src/node/db/events/error.rs b/core/src/node/db/events/error.rs index 4d6b0ab2..1eee2656 100644 --- a/core/src/node/db/events/error.rs +++ b/core/src/node/db/events/error.rs @@ -1,4 +1,8 @@ +use crate::node::db::events::generic_log_event::GenericKvLogEvent; +use crate::node::db::events::shared_secret_event::{SSDeviceLogObject, SSLedgerObject, SharedSecretObject}; +use crate::node::db::events::vault_event::VaultActionEvent; use thiserror::Error; +use crate::node::db::events::vault::device_log_event::DeviceLogObject; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -8,8 +12,37 @@ pub struct ErrorMessage { #[derive(Error, Debug)] pub enum LogEventCastError { - #[error("Invalid event")] - InvalidEventType, + #[error("InvalidGlobalIndex: Invalid event")] + InvalidGlobalIndex(GenericKvLogEvent), + #[error("InvalidCredentials: Invalid event")] + InvalidCredentials(GenericKvLogEvent), + #[error("InvalidDbTail: Invalid event")] + InvalidDbTail(GenericKvLogEvent), + #[error("InvalidDeviceLog: Invalid event")] + InvalidDeviceLog(GenericKvLogEvent), + #[error("InvalidVaultLog: Invalid event")] + InvalidVaultLog(GenericKvLogEvent), + #[error("InvalidVault: Invalid event")] + InvalidVault(GenericKvLogEvent), + #[error("InvalidVaultMembership: Invalid event")] + InvalidVaultMembership(GenericKvLogEvent), + #[error("InvalidSharedSecret: Invalid event")] + InvalidSharedSecret(GenericKvLogEvent), + #[error("InvalidSSDeviceLog: Invalid event")] + InvalidSSDeviceLog(GenericKvLogEvent), + #[error("InvalidSSLedger: Invalid event")] + InvalidSSLedger(GenericKvLogEvent), + #[error("WrongSSLedger: wrong event")] + WrongSSLedger(SSLedgerObject), + #[error("WrongSSDeviceLog: wrong event")] + WrongSSDeviceLog(SSDeviceLogObject), + #[error("WrongSharedSecret: wrong event")] + WrongSharedSecret(SharedSecretObject), + #[error("WrongDeviceLog: wrong event")] + WrongDeviceLog(DeviceLogObject), + #[error("WrongVaultAction. Expected: {0}, actual: {1}")] + WrongVaultAction(String, VaultActionEvent), + } impl From<&anyhow::Error> for ErrorMessage { diff --git a/core/src/node/db/events/generic_log_event.rs b/core/src/node/db/events/generic_log_event.rs index a499cd6b..1e68a61c 100644 --- a/core/src/node/db/events/generic_log_event.rs +++ b/core/src/node/db/events/generic_log_event.rs @@ -8,7 +8,9 @@ use crate::node::db::events::kv_log_event::{GenericKvKey, KvKey, KvLogEvent}; use crate::node::db::events::local_event::{CredentialsObject, DbTailObject}; use crate::node::db::events::object_id::{ArtifactId, ObjectId}; use crate::node::db::events::shared_secret_event::{SSDeviceLogObject, SharedSecretObject}; -use crate::node::db::events::vault_event::{DeviceLogObject, VaultLogObject, VaultMembershipObject, VaultObject}; +use crate::node::db::events::vault::device_log_event::DeviceLogObject; +use crate::node::db::events::vault::vault_log_event::VaultLogObject; +use crate::node::db::events::vault_event::{VaultMembershipObject, VaultObject}; use super::shared_secret_event::SSLedgerObject; diff --git a/core/src/node/db/events/global_index_event.rs b/core/src/node/db/events/global_index_event.rs index 31e5c564..23b0f801 100644 --- a/core/src/node/db/events/global_index_event.rs +++ b/core/src/node/db/events/global_index_event.rs @@ -1,4 +1,4 @@ -use crate::node::common::model::device::DeviceData; +use crate::node::common::model::device::common::DeviceData; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; diff --git a/core/src/node/db/events/kv_log_event.rs b/core/src/node/db/events/kv_log_event.rs index e0c4e43d..b4182e13 100644 --- a/core/src/node/db/events/kv_log_event.rs +++ b/core/src/node/db/events/kv_log_event.rs @@ -1,4 +1,4 @@ -use crate::node::common::model::device::DeviceData; +use crate::node::common::model::device::common::DeviceData; use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; use crate::node::db::events::object_id::{ArtifactId, GenesisId, Next, UnitId}; diff --git a/core/src/node/db/events/local_event.rs b/core/src/node/db/events/local_event.rs index e95531cb..6ab3d8be 100644 --- a/core/src/node/db/events/local_event.rs +++ b/core/src/node/db/events/local_event.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Error}; - -use crate::node::common::model::device::{DeviceCredentials, DeviceData}; -use crate::node::common::model::user::UserCredentials; +use crate::node::common::model::device::common::DeviceData; +use crate::node::common::model::device::device_creds::DeviceCredentials; +use crate::node::common::model::user::user_creds::UserCredentials; use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; use crate::node::db::events::db_tail::DbTail; use crate::node::db::events::generic_log_event::{ @@ -20,7 +20,10 @@ pub enum CredentialsObject { impl ObjIdExtractor for CredentialsObject { fn obj_id(&self) -> ObjectId { - CredentialsObject::unit_id() + match self { + CredentialsObject::Device(event) => ObjectId::from(event.key.obj_id.clone()), + CredentialsObject::DefaultUser(event) => ObjectId::from(event.key.obj_id.clone()), + } } } diff --git a/core/src/node/db/events/mod.rs b/core/src/node/db/events/mod.rs index 02c3a247..0a5eacad 100644 --- a/core/src/node/db/events/mod.rs +++ b/core/src/node/db/events/mod.rs @@ -7,3 +7,4 @@ pub mod local_event; pub mod object_id; pub mod shared_secret_event; pub mod vault_event; +pub mod vault; diff --git a/core/src/node/db/events/object_id.rs b/core/src/node/db/events/object_id.rs index 2669d9ba..e48d639f 100644 --- a/core/src/node/db/events/object_id.rs +++ b/core/src/node/db/events/object_id.rs @@ -1,7 +1,7 @@ use serde_derive::{Deserialize, Serialize}; use crate::crypto::utils::NextId; -use crate::node::common::model::user::UserData; +use crate::node::common::model::user::common::UserData; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ObjectDescriptorId, ToObjectDescriptor}; @@ -122,7 +122,7 @@ impl UnitId { } pub fn vault_unit(vault_name: VaultName) -> UnitId { - let vault_desc = VaultDescriptor::Vault(vault_name).to_obj_desc(); + let vault_desc = VaultDescriptor::Vault(vault_name.clone()).to_obj_desc(); UnitId::unit(&vault_desc) } } diff --git a/core/src/node/db/events/shared_secret_event.rs b/core/src/node/db/events/shared_secret_event.rs index cf1d696a..010edaee 100644 --- a/core/src/node/db/events/shared_secret_event.rs +++ b/core/src/node/db/events/shared_secret_event.rs @@ -4,7 +4,6 @@ use crate::node::db::events::generic_log_event::{GenericKvLogEvent, KeyExtractor use crate::node::db::events::kv_log_event::{GenericKvKey, KvLogEvent}; use crate::node::db::events::object_id::{ArtifactId, ObjectId, UnitId}; use anyhow::{anyhow, bail, Ok}; - use super::object_id::{VaultGenesisEvent, VaultUnitEvent}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -19,7 +18,7 @@ impl SharedSecretObject { if let SharedSecretObject::LocalShare(event) = self { Ok(event.value.clone()) } else { - bail!(LogEventCastError::InvalidEventType) + bail!(LogEventCastError::WrongSharedSecret(self.clone())) } } } @@ -40,7 +39,7 @@ impl TryFrom for SharedSecretObject { if let GenericKvLogEvent::SharedSecret(ss_obj) = event { Ok(ss_obj) } else { - bail!(LogEventCastError::InvalidEventType) + bail!(LogEventCastError::InvalidSharedSecret(event)) } } } @@ -57,21 +56,21 @@ impl SSDeviceLogObject { pub fn get_unit(&self) -> anyhow::Result { match self { SSDeviceLogObject::Unit(event) => Ok(event.clone()), - _ => bail!(LogEventCastError::InvalidEventType), + _ => bail!(LogEventCastError::WrongSSDeviceLog(self.clone())), } } pub fn get_genesis(&self) -> anyhow::Result { match self { SSDeviceLogObject::Genesis(event) => Ok(event.clone()), - _ => bail!(LogEventCastError::InvalidEventType), + _ => bail!(LogEventCastError::WrongSSDeviceLog(self.clone())), } } pub fn get_distribution_request(&self) -> anyhow::Result { match self { SSDeviceLogObject::SSDeviceLog(event) => Ok(event.value.clone()), - _ => bail!(LogEventCastError::InvalidEventType), + _ => bail!(LogEventCastError::WrongSSDeviceLog(self.clone())), } } } @@ -141,10 +140,10 @@ impl TryFrom for SSLedgerObject { type Error = anyhow::Error; fn try_from(event: GenericKvLogEvent) -> Result { - if let GenericKvLogEvent::SSLedger(ss_obj) = event { - Ok(ss_obj) + if let GenericKvLogEvent::SSLedger(ss_obj) = &event { + Ok(ss_obj.clone()) } else { - bail!(LogEventCastError::InvalidEventType) + bail!(LogEventCastError::InvalidSSLedger(event)) } } } @@ -154,7 +153,7 @@ impl SSLedgerObject { if let SSLedgerObject::Ledger(ledger_event) = self { Ok(ledger_event.value.clone()) } else { - bail!(LogEventCastError::InvalidEventType) + bail!(LogEventCastError::WrongSSLedger(self.clone())) } } @@ -162,7 +161,7 @@ impl SSLedgerObject { if let SSLedgerObject::Ledger(ledger_event) = self { Ok(ledger_event.key.obj_id.clone()) } else { - bail!(LogEventCastError::InvalidEventType) + bail!(LogEventCastError::WrongSSLedger(self.clone())) } } } diff --git a/core/src/node/db/events/vault/device_log_event.rs b/core/src/node/db/events/vault/device_log_event.rs new file mode 100644 index 00000000..c6c38695 --- /dev/null +++ b/core/src/node/db/events/vault/device_log_event.rs @@ -0,0 +1,77 @@ +use anyhow::{anyhow, bail}; +use crate::node::db::events::error::LogEventCastError; +use crate::node::db::events::generic_log_event::{GenericKvLogEvent, KeyExtractor, ObjIdExtractor, ToGenericEvent}; +use crate::node::db::events::kv_log_event::{GenericKvKey, KvLogEvent}; +use crate::node::db::events::object_id::{ArtifactId, ObjectId, VaultGenesisEvent, VaultUnitEvent}; +use crate::node::db::events::vault_event::VaultActionEvent; + +/// Each device has its own unique device_log table, to prevent conflicts in updates vault state +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum DeviceLogObject { + Unit(VaultUnitEvent), + /// Device sends its data to ensure that the only this device can send events to this log + Genesis(VaultGenesisEvent), + Action(KvLogEvent), +} + +impl DeviceLogObject { + pub fn get_unit(&self) -> anyhow::Result { + match self { + DeviceLogObject::Unit(event) => Ok(event.clone()), + _ => bail!(LogEventCastError::WrongDeviceLog(self.clone())), + } + } + + pub fn get_genesis(&self) -> anyhow::Result { + match self { + DeviceLogObject::Genesis(event) => Ok(event.clone()), + _ => bail!(LogEventCastError::WrongDeviceLog(self.clone())), + } + } + + pub fn get_action(&self) -> anyhow::Result { + match self { + DeviceLogObject::Action(event) => Ok(event.value.clone()), + _ => bail!(LogEventCastError::WrongDeviceLog(self.clone())), + } + } +} + +impl TryFrom for DeviceLogObject { + type Error = anyhow::Error; + + fn try_from(event: GenericKvLogEvent) -> Result { + if let GenericKvLogEvent::DeviceLog(device_log) = event { + Ok(device_log) + } else { + Err(anyhow!("Not a device log event")) + } + } +} + +impl ToGenericEvent for DeviceLogObject { + fn to_generic(self) -> GenericKvLogEvent { + GenericKvLogEvent::DeviceLog(self) + } +} + +impl ObjIdExtractor for DeviceLogObject { + fn obj_id(&self) -> ObjectId { + match self { + DeviceLogObject::Unit(event) => ObjectId::from(event.key().obj_id.clone()), + DeviceLogObject::Genesis(event) => ObjectId::from(event.key().obj_id.clone()), + DeviceLogObject::Action(event) => ObjectId::from(event.key.obj_id.clone()), + } + } +} + +impl KeyExtractor for DeviceLogObject { + fn key(&self) -> GenericKvKey { + match self { + DeviceLogObject::Unit(event) => GenericKvKey::from(event.key().clone()), + DeviceLogObject::Genesis(event) => GenericKvKey::from(event.key().clone()), + DeviceLogObject::Action(event) => GenericKvKey::from(event.key.clone()), + } + } +} diff --git a/core/src/node/db/events/vault/mod.rs b/core/src/node/db/events/vault/mod.rs new file mode 100644 index 00000000..4a5ba4ff --- /dev/null +++ b/core/src/node/db/events/vault/mod.rs @@ -0,0 +1,2 @@ +pub mod device_log_event; +pub mod vault_log_event; diff --git a/core/src/node/db/events/vault/vault_log_event.rs b/core/src/node/db/events/vault/vault_log_event.rs new file mode 100644 index 00000000..478ccb9c --- /dev/null +++ b/core/src/node/db/events/vault/vault_log_event.rs @@ -0,0 +1,75 @@ +use anyhow::anyhow; +use crate::node::common::model::user::common::UserData; +use crate::node::common::model::vault::VaultName; +use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; +use crate::node::db::events::generic_log_event::{GenericKvLogEvent, KeyExtractor, ObjIdExtractor, ToGenericEvent}; +use crate::node::db::events::kv_log_event::{GenericKvKey, KvKey, KvLogEvent}; +use crate::node::db::events::object_id::{ArtifactId, ObjectId, VaultGenesisEvent, VaultUnitEvent}; +use crate::node::db::events::vault_event::VaultActionEvent; + +/// VaultLog keeps incoming events in order, the log is a queue for incoming messages and used to +/// recreate the vault state from events (event sourcing) +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum VaultLogObject { + Unit(VaultUnitEvent), + Genesis(VaultGenesisEvent), + Action(KvLogEvent), +} + +impl VaultLogObject { + pub fn unit(vault_name: VaultName) -> Self { + let desc = VaultDescriptor::vault_log(vault_name.clone()); + + VaultLogObject::Unit(VaultUnitEvent(KvLogEvent { + key: KvKey::unit(desc), + value: vault_name, + })) + } + + pub fn genesis(vault_name: VaultName, candidate: UserData) -> Self { + let desc = VaultDescriptor::vault_log(vault_name.clone()); + VaultLogObject::Genesis(VaultGenesisEvent(KvLogEvent { + key: KvKey::genesis(desc), + value: candidate.clone(), + })) + } +} + +impl TryFrom for VaultLogObject { + type Error = anyhow::Error; + + fn try_from(event: GenericKvLogEvent) -> Result { + if let GenericKvLogEvent::VaultLog(vault_log) = event { + Ok(vault_log) + } else { + Err(anyhow!("Not a vault log event")) + } + } +} + +impl ToGenericEvent for VaultLogObject { + fn to_generic(self) -> GenericKvLogEvent { + GenericKvLogEvent::VaultLog(self) + } +} + +impl KeyExtractor for VaultLogObject { + fn key(&self) -> GenericKvKey { + match self { + VaultLogObject::Unit(event) => GenericKvKey::from(event.key().clone()), + VaultLogObject::Genesis(event) => GenericKvKey::from(event.key().clone()), + VaultLogObject::Action(event) => GenericKvKey::from(event.key.clone()), + } + } +} + +impl ObjIdExtractor for VaultLogObject { + fn obj_id(&self) -> ObjectId { + match self { + VaultLogObject::Unit(event) => ObjectId::from(event.key().obj_id.clone()), + VaultLogObject::Genesis(event) => ObjectId::from(event.key().obj_id.clone()), + VaultLogObject::Action(event) => ObjectId::from(event.key.obj_id.clone()), + } + } +} diff --git a/core/src/node/db/events/vault_event.rs b/core/src/node/db/events/vault_event.rs index fef3619c..ce168746 100644 --- a/core/src/node/db/events/vault_event.rs +++ b/core/src/node/db/events/vault_event.rs @@ -1,152 +1,91 @@ +use std::fmt::Display; use anyhow::{anyhow, bail}; - -use crate::node::common::model::device::DeviceData; +use tracing::error; +use crate::node::common::model::device::common::DeviceData; use crate::node::common::model::secret::MetaPasswordId; -use crate::node::common::model::user::{UserData, UserDataMember, UserDataOutsider, UserMembership}; +use crate::node::common::model::user::common::{UserData, UserDataMember, UserMembership}; use crate::node::common::model::vault::{VaultData, VaultName, VaultStatus}; +use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; +use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; use crate::node::db::events::error::LogEventCastError; use crate::node::db::events::generic_log_event::{GenericKvLogEvent, KeyExtractor, ObjIdExtractor, ToGenericEvent}; -use crate::node::db::events::kv_log_event::{GenericKvKey, KvLogEvent}; -use crate::node::db::events::object_id::{ArtifactId, GenesisId, ObjectId}; +use crate::node::db::events::kv_log_event::{GenericKvKey, KvKey, KvLogEvent}; +use crate::node::db::events::object_id::{ArtifactId, GenesisId, Next, ObjectId, UnitId}; use super::object_id::{VaultGenesisEvent, VaultUnitEvent}; -/// Each device has its own unique device_log table, to prevent conflicts in updates vault state -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum DeviceLogObject { - Unit(VaultUnitEvent), - /// Device sends its data to ensure that the only this device can send events to this log - Genesis(VaultGenesisEvent), - Action(KvLogEvent), -} - -impl DeviceLogObject { - pub fn get_unit(&self) -> anyhow::Result { - match self { - DeviceLogObject::Unit(event) => Ok(event.clone()), - _ => bail!(LogEventCastError::InvalidEventType), - } - } - - pub fn get_genesis(&self) -> anyhow::Result { - match self { - DeviceLogObject::Genesis(event) => Ok(event.clone()), - _ => bail!(LogEventCastError::InvalidEventType), - } - } - - pub fn get_action(&self) -> anyhow::Result { - match self { - DeviceLogObject::Action(event) => Ok(event.value.clone()), - _ => bail!(LogEventCastError::InvalidEventType), - } - } -} - -impl TryFrom for DeviceLogObject { - type Error = anyhow::Error; - - fn try_from(event: GenericKvLogEvent) -> Result { - if let GenericKvLogEvent::DeviceLog(device_log) = event { - Ok(device_log) - } else { - Err(anyhow!("Not a device log event")) - } - } -} - -impl ToGenericEvent for DeviceLogObject { - fn to_generic(self) -> GenericKvLogEvent { - GenericKvLogEvent::DeviceLog(self) - } -} - -impl ObjIdExtractor for DeviceLogObject { - fn obj_id(&self) -> ObjectId { - match self { - DeviceLogObject::Unit(event) => ObjectId::from(event.key().obj_id.clone()), - DeviceLogObject::Genesis(event) => ObjectId::from(event.key().obj_id.clone()), - DeviceLogObject::Action(event) => ObjectId::from(event.key.obj_id.clone()), - } - } -} - -impl KeyExtractor for DeviceLogObject { - fn key(&self) -> GenericKvKey { - match self { - DeviceLogObject::Unit(event) => GenericKvKey::from(event.key().clone()), - DeviceLogObject::Genesis(event) => GenericKvKey::from(event.key().clone()), - DeviceLogObject::Action(event) => GenericKvKey::from(event.key.clone()), - } - } -} - -/// VaultLog keeps incoming events in order, the log is a queue for incoming messages and used to -/// recreate the vault state from events (event sourcing) #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub enum VaultLogObject { +pub enum VaultObject { Unit(VaultUnitEvent), - Genesis(VaultGenesisEvent), - Action(KvLogEvent), + /// Vault creator + Genesis(KvLogEvent), + Vault(KvLogEvent), } -impl TryFrom for VaultLogObject { - type Error = anyhow::Error; +impl VaultObject { + pub fn sign_up(vault_name: VaultName, candidate: UserData) -> Self { + let desc = VaultDescriptor::vault(vault_name.clone()); + let vault_data = { + let mut vault = VaultData::from(vault_name.clone()); + let membership = UserMembership::Member(UserDataMember(candidate.clone())); + vault.update_membership(membership); + vault + }; - fn try_from(event: GenericKvLogEvent) -> Result { - if let GenericKvLogEvent::VaultLog(vault_log) = event { - Ok(vault_log) - } else { - Err(anyhow!("Not a vault log event")) - } - } -} + let vault_id = UnitId::vault_unit(vault_name).next().next(); -impl ToGenericEvent for VaultLogObject { - fn to_generic(self) -> GenericKvLogEvent { - GenericKvLogEvent::VaultLog(self) + let sign_up_event = KvLogEvent { + key: KvKey::artifact(desc.clone(), vault_id), + value: vault_data, + }; + VaultObject::Vault(sign_up_event) } } -impl KeyExtractor for VaultLogObject { - fn key(&self) -> GenericKvKey { - match self { - VaultLogObject::Unit(event) => GenericKvKey::from(event.key().clone()), - VaultLogObject::Genesis(event) => GenericKvKey::from(event.key().clone()), - VaultLogObject::Action(event) => GenericKvKey::from(event.key.clone()), - } +impl VaultObject { + pub fn genesis(vault_name: VaultName, server_device: DeviceData) -> Self { + let desc = VaultDescriptor::vault(vault_name.clone()); + VaultObject::Genesis(KvLogEvent { + key: KvKey::genesis(desc), + value: server_device, + }) } } -impl ObjIdExtractor for VaultLogObject { - fn obj_id(&self) -> ObjectId { - match self { - VaultLogObject::Unit(event) => ObjectId::from(event.key().obj_id.clone()), - VaultLogObject::Genesis(event) => ObjectId::from(event.key().obj_id.clone()), - VaultLogObject::Action(event) => ObjectId::from(event.key.obj_id.clone()), - } +impl VaultObject { + pub fn unit(vault_name: VaultName) -> Self { + let desc = VaultDescriptor::vault(vault_name.clone()); + VaultObject::Unit(VaultUnitEvent(KvLogEvent { + key: KvKey::unit(desc), + value: vault_name, + })) } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum VaultObject { - Unit(VaultUnitEvent), - /// Meta Server public keys - Genesis(KvLogEvent), - Vault(KvLogEvent), -} - impl VaultObject { pub fn status(&self, user: UserData) -> VaultStatus { match self { - VaultObject::Unit(_) => VaultStatus::Outsider(UserDataOutsider::unknown(user)), - VaultObject::Genesis(_) => VaultStatus::Outsider(UserDataOutsider::unknown(user)), + VaultObject::Unit(_) => { + error!("Invalid state (only unit event is present)"); + VaultStatus::NotExists(user.clone()) + }, + VaultObject::Genesis(_) => { + // We believe that if there are only unit and genesis events in the database, then + // the table is broken, so vault not exists + error!("Invalid state (only genesis event is present)"); + VaultStatus::NotExists(user.clone()) + }, VaultObject::Vault(event) => { let vault = event.value.clone(); - vault.status(user) + match vault.membership(user) { + UserMembership::Outsider(outsider) => { + VaultStatus::Outsider(outsider) + } + UserMembership::Member(member) => { + VaultStatus::Member { member, vault } + } + } } } } @@ -200,6 +139,57 @@ pub enum VaultMembershipObject { } impl VaultMembershipObject { + + pub fn init(candidate: UserData) -> Vec { + let unit_event = VaultMembershipObject::unit(candidate.clone()).to_generic(); + let genesis_event = VaultMembershipObject::genesis(candidate.clone()).to_generic(); + + let member_event = { + let desc = VaultDescriptor::VaultMembership(candidate.user_id()).to_obj_desc(); + let member_event_id = UnitId::unit(&desc).next().next(); + VaultMembershipObject::member(candidate, member_event_id).to_generic() + }; + + vec![unit_event, genesis_event, member_event] + } + + fn unit(candidate: UserData) -> Self { + let user_id = candidate.user_id(); + let desc = VaultDescriptor::VaultMembership(user_id).to_obj_desc(); + + VaultMembershipObject::Unit(VaultUnitEvent(KvLogEvent { + key: KvKey::unit(desc), + value: candidate.vault_name, + })) + } + + pub fn genesis(candidate: UserData) -> Self { + let desc = VaultDescriptor::VaultMembership(candidate.user_id()).to_obj_desc(); + + VaultMembershipObject::Genesis(VaultGenesisEvent(KvLogEvent { + key: KvKey::genesis(desc), + value: candidate.clone(), + })) + } + + pub fn member(candidate: UserData, event_id: ArtifactId) -> Self { + let member = UserMembership::Member(UserDataMember(candidate.clone())); + Self::membership(member, event_id) + } + + pub fn membership(membership: UserMembership, event_id: ArtifactId) -> Self { + let user_id = membership.user_data().user_id(); + let desc = VaultDescriptor::VaultMembership(user_id).to_obj_desc(); + + VaultMembershipObject::Membership(KvLogEvent { + key: KvKey { obj_id: event_id, obj_desc: desc }, + value: membership, + }) + } +} + +impl VaultMembershipObject { + pub fn is_member(&self) -> bool { let VaultMembershipObject::Membership(membership_event) = self else { return false; @@ -253,7 +243,7 @@ impl ObjIdExtractor for VaultMembershipObject { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub enum VaultAction { +pub enum VaultActionEvent { CreateVault(UserData), JoinClusterRequest { @@ -271,30 +261,42 @@ pub enum VaultAction { }, } -impl VaultAction { +impl Display for VaultActionEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + VaultActionEvent::CreateVault(_) => String::from("CreateVault"), + VaultActionEvent::JoinClusterRequest { .. } => String::from("JoinClusterRequest"), + VaultActionEvent::UpdateMembership { .. } => String::from("UpdateMembership"), + VaultActionEvent::AddMetaPassword { .. } => String::from("AddMetaPassword"), + }; + write!(f, "{}", str) + } +} + +impl VaultActionEvent { pub fn get_create(self) -> anyhow::Result { match self { - VaultAction::CreateVault(user) => Ok(user), - _ => bail!(LogEventCastError::InvalidEventType), + VaultActionEvent::CreateVault(user) => Ok(user), + _ => bail!(LogEventCastError::WrongVaultAction(String::from("CreateVault"), self.clone())), } } pub fn get_join_request(self) -> anyhow::Result { match self { - VaultAction::JoinClusterRequest { candidate } => Ok(candidate), - _ => bail!(LogEventCastError::InvalidEventType), + VaultActionEvent::JoinClusterRequest { candidate } => Ok(candidate), + _ => bail!(LogEventCastError::WrongVaultAction(String::from("JoinClusterRequest"), self.clone())), } } pub fn vault_name(&self) -> VaultName { match self { - VaultAction::JoinClusterRequest { candidate } => candidate.vault_name.clone(), - VaultAction::UpdateMembership { update, .. } => update.user_data().vault_name, - VaultAction::AddMetaPassword { + VaultActionEvent::JoinClusterRequest { candidate } => candidate.vault_name.clone(), + VaultActionEvent::UpdateMembership { update, .. } => update.user_data().vault_name, + VaultActionEvent::AddMetaPassword { sender: UserDataMember(user), .. } => user.vault_name.clone(), - VaultAction::CreateVault(user) => user.vault_name.clone(), + VaultActionEvent::CreateVault(user) => user.vault_name.clone(), } } } diff --git a/core/src/node/db/objects/global_index.rs b/core/src/node/db/objects/global_index.rs index b2f7f362..efd45b7b 100644 --- a/core/src/node/db/objects/global_index.rs +++ b/core/src/node/db/objects/global_index.rs @@ -1,22 +1,23 @@ -use crate::node::common::model::device::DeviceData; +use crate::node::common::model::device::common::DeviceData; +use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; -use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; -use crate::node::db::events::generic_log_event::ToGenericEvent; +use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ToObjectDescriptor}; +use crate::node::db::events::generic_log_event::{ObjIdExtractor, ToGenericEvent, UnitEventWithEmptyValue}; use crate::node::db::events::global_index_event::GlobalIndexObject; -use crate::node::db::events::kv_log_event::KvLogEvent; -use crate::node::db::events::object_id::ObjectId; +use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; +use crate::node::db::events::object_id::{ObjectId, UnitId}; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::repo::generic_db::KvLogEventRepo; +use anyhow::anyhow; use std::sync::Arc; -use tracing::info; -use tracing_attributes::instrument; +use tracing::{info, instrument}; -pub struct PersistentGlobalIndex { +pub struct ServerPersistentGlobalIndex { pub p_obj: Arc>, pub server_device: DeviceData, } -impl PersistentGlobalIndex { +impl ServerPersistentGlobalIndex { ///create a genesis event and save into the database #[instrument(skip(self))] pub async fn init(&self) -> anyhow::Result<()> { @@ -40,36 +41,183 @@ impl PersistentGlobalIndex { info!("Init global index"); - let unit_event = GlobalIndexObject::Unit(KvLogEvent::global_index_unit()).to_generic(); - let genesis_event = - GlobalIndexObject::Genesis(KvLogEvent::global_index_genesis(self.server_device.clone())).to_generic(); - + let unit_event = GlobalIndexObject::unit().to_generic(); + let genesis_event = GlobalIndexObject::genesis(self.server_device.clone()).to_generic(); + self.p_obj.repo.save(unit_event.clone()).await?; self.p_obj.repo.save(genesis_event.clone()).await?; Ok(()) } + + pub async fn update(&self, vault_name: VaultName) -> anyhow::Result<()> { + //find the latest global_index_id??? + let gi_free_id = self + .p_obj + .find_free_id_by_obj_desc(ObjectDescriptor::GlobalIndex(GlobalIndexDescriptor::Index)) + .await?; + + let ObjectId::Artifact(gi_artifact_id) = gi_free_id else { + return Err(anyhow!("Invalid global index state")); + }; + + let vault_id = UnitId::vault_unit(vault_name.clone()); + + let gi_update_event = { + GlobalIndexObject::Update(KvLogEvent { + key: KvKey { + obj_id: gi_artifact_id, + obj_desc: GlobalIndexDescriptor::Index.to_obj_desc(), + }, + value: vault_id.clone(), + }) + .to_generic() + }; + + let gi_events = vec![gi_update_event]; + + for gi_event in gi_events { + self.p_obj.repo.save(gi_event).await?; + } + + let vault_idx_evt = GlobalIndexObject::index_from_vault_id(vault_id).to_generic(); + + self.p_obj.repo.save(vault_idx_evt).await?; + + anyhow::Ok(()) + } +} + +pub struct ClientPersistentGlobalIndex { + pub p_obj: Arc>, +} + +impl ClientPersistentGlobalIndex { + /// Save global index event and also save a "vault index" record + pub async fn save(&self, gi_obj: &GlobalIndexObject) -> anyhow::Result<()> { + self.p_obj.repo.save(gi_obj.clone().to_generic()).await?; + + // Update vault index according to global index + if let GlobalIndexObject::Update(upd_event) = gi_obj { + let vault_id = upd_event.value.clone(); + let vault_idx_evt = GlobalIndexObject::index_from_vault_id(vault_id).to_generic(); + self.p_obj.repo.save(vault_idx_evt).await?; + } + + Ok(()) + } + + pub async fn not_exists(&self, vault_name: VaultName) -> anyhow::Result { + let exists = self.exists(vault_name).await?; + Ok(!exists) + } + + pub async fn exists(&self, vault_name: VaultName) -> anyhow::Result { + let vault_idx_obj = GlobalIndexObject::index_from_vault_name(vault_name).to_generic(); + let db_idx_event = self.p_obj.repo.get_key(vault_idx_obj.obj_id()).await?; + Ok(db_idx_event.is_some()) + } +} + +#[cfg(test)] +pub mod spec { + use crate::node::common::model::device::common::DeviceData; + use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; + use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; + use crate::node::db::events::generic_log_event::ObjIdExtractor; + use crate::node::db::events::global_index_event::GlobalIndexObject; + use crate::node::db::events::object_id::ObjectId; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::objects::global_index::fixture::ServerPersistentGlobalIndexFixture; + use crate::node::db::repo::generic_db::KvLogEventRepo; + use std::sync::Arc; + + pub struct GlobalIndexSpec { + pub repo: Arc, + pub server_device: DeviceData, + } + + impl GlobalIndexSpec { + pub async fn verify(&self) -> anyhow::Result<()> { + let gi_obj_desc = GlobalIndexDescriptor::Index.to_obj_desc(); + + let unit_event = { + let unit_id = ObjectId::unit(gi_obj_desc.clone()); + self.repo.find_one(unit_id).await?.unwrap().global_index()? + }; + assert_eq!(unit_event.obj_id().get_unit_id().id.id, 0); + + let genesis_event = { + let genesis_id = ObjectId::genesis(gi_obj_desc.clone()); + self.repo.find_one(genesis_id).await?.unwrap().global_index()? + }; + + if let GlobalIndexObject::Genesis(log_event) = genesis_event { + assert_eq!(log_event.value, self.server_device); + } else { + panic!("Invalid Genesis event"); + } + + Ok(()) + } + } + + impl From for GlobalIndexSpec { + fn from(gi_fixture: ServerPersistentGlobalIndexFixture) -> Self { + Self { + repo: gi_fixture.server_gi.p_obj.repo.clone(), + server_device: gi_fixture.server_gi.server_device.clone(), + } + } + } +} + +#[cfg(test)] +pub mod fixture { + use crate::meta_tests::fixture_util::fixture::states::EmptyState; + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::objects::global_index::{ClientPersistentGlobalIndex, ServerPersistentGlobalIndex}; + + pub struct ServerPersistentGlobalIndexFixture { + pub server_gi: ServerPersistentGlobalIndex, + pub client_gi: ClientPersistentGlobalIndex + } + + impl From<&FixtureRegistry> for ServerPersistentGlobalIndexFixture { + fn from(registry: &FixtureRegistry) -> Self { + + Self { + server_gi: ServerPersistentGlobalIndex { + p_obj: registry.state.p_obj.server.clone(), + server_device: registry.state.device_creds.server.device.clone(), + }, + client_gi: ClientPersistentGlobalIndex { + p_obj: registry.state.p_obj.client.clone(), + }, + } + } + } + + impl ServerPersistentGlobalIndexFixture { + pub fn generate() -> Self { + ServerPersistentGlobalIndexFixture::from(&FixtureRegistry::empty()) + } + } } #[cfg(test)] mod test { - use crate::meta_tests::action::global_index_action::GlobalIndexSyncRequestTestAction; - use crate::meta_tests::fixture::ClientDeviceFixture; - use crate::meta_tests::spec::global_index_specs::GlobalIndexSpec; + use crate::node::db::objects::global_index::fixture::ServerPersistentGlobalIndexFixture; + use crate::node::db::objects::global_index::spec::GlobalIndexSpec; #[tokio::test] async fn test_init() -> anyhow::Result<()> { - let gi_request_action = GlobalIndexSyncRequestTestAction::init().await?; - let device_fixture = ClientDeviceFixture::default(); - - let _ = gi_request_action.send_request(device_fixture.device_creds.device).await; - - let gi_spec = GlobalIndexSpec { - repo: gi_request_action.server_node.p_obj.repo.clone(), - server_device: gi_request_action.server_node.device.device.clone(), - }; + let gi_fixture = ServerPersistentGlobalIndexFixture::generate(); + gi_fixture.server_gi.init().await?; - gi_spec.check().await?; + let gi_spec = GlobalIndexSpec::from(gi_fixture); + gi_spec.verify().await?; Ok(()) } diff --git a/core/src/node/db/objects/persistent_device_log.rs b/core/src/node/db/objects/persistent_device_log.rs index b228db93..5a1fd48e 100644 --- a/core/src/node/db/objects/persistent_device_log.rs +++ b/core/src/node/db/objects/persistent_device_log.rs @@ -1,15 +1,15 @@ use std::sync::Arc; use anyhow::bail; -use tracing::debug; +use tracing::{debug, info}; use tracing_attributes::instrument; - -use crate::node::common::model::user::{UserData, UserDataMember, UserId, UserMembership}; +use crate::node::common::model::user::common::{UserData, UserDataMember, UserId, UserMembership}; use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; -use crate::node::db::events::generic_log_event::ToGenericEvent; +use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent}; use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; use crate::node::db::events::object_id::{ArtifactId, Next, ObjectId, UnitId, VaultGenesisEvent, VaultUnitEvent}; -use crate::node::db::events::vault_event::{DeviceLogObject, VaultAction}; +use crate::node::db::events::vault::device_log_event::DeviceLogObject; +use crate::node::db::events::vault_event::VaultActionEvent; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::repo::generic_db::KvLogEventRepo; @@ -19,7 +19,6 @@ pub struct PersistentDeviceLog { impl PersistentDeviceLog { pub async fn find_tail_id(&self, user_id: &UserId) -> anyhow::Result> { - todo!("Add validation steps that the user is a member of the vault"); let obj_desc = VaultDescriptor::device_log(user_id.clone()); self.p_obj.find_tail_id_by_obj_desc(obj_desc.clone()).await } @@ -32,11 +31,13 @@ impl PersistentDeviceLog { member: UserDataMember, candidate: UserData, ) -> anyhow::Result<()> { + info!("Accept join request"); + let member_user = member.user(); self.init(member_user).await?; - let key = self.get_device_log_artifact_key(&member_user).await?; - let update = VaultAction::UpdateMembership { + let key = self.get_device_log_key(&member_user).await?; + let update = VaultActionEvent::UpdateMembership { sender: member, update: UserMembership::Member(UserDataMember(candidate)), }; @@ -47,20 +48,49 @@ impl PersistentDeviceLog { Ok(()) } - #[instrument(skip_all)] + #[instrument(skip(self))] pub async fn save_create_vault_request(&self, user: &UserData) -> anyhow::Result<()> { + info!("Save event: CreateVault request"); + + self.init(user).await?; + + let maybe_generic_event = self + .p_obj + .find_tail_event(VaultDescriptor::device_log(user.user_id())) + .await?; + + if let Some(GenericKvLogEvent::DeviceLog(DeviceLogObject::Action(event))) = maybe_generic_event { + if let VaultActionEvent::CreateVault(_) = event.value { + info!("SignUp request already exists"); + return Ok(()); + } + } + + let create_request = DeviceLogObject::Action(KvLogEvent { + key: self.get_device_log_key(&user).await?, + value: VaultActionEvent::CreateVault(user.clone()), + }); + self.p_obj.repo.save(create_request.to_generic()).await?; + + Ok(()) + } + + #[instrument(skip_all)] + pub async fn save_join_request(&self, user: &UserData) -> anyhow::Result<()> { self.init(user).await?; let join_request = DeviceLogObject::Action(KvLogEvent { - key: self.get_device_log_artifact_key(&user).await?, - value: VaultAction::CreateVault(user.clone()), + key: self.get_device_log_key(&user).await?, + value: VaultActionEvent::JoinClusterRequest { + candidate: user.clone(), + }, }); self.p_obj.repo.save(join_request.to_generic()).await?; Ok(()) } - async fn get_device_log_artifact_key(&self, user: &UserData) -> anyhow::Result> { + async fn get_device_log_key(&self, user: &UserData) -> anyhow::Result> { let obj_desc = VaultDescriptor::device_log(user.user_id()); let free_id = self.p_obj.find_free_id_by_obj_desc(obj_desc.clone()).await?; @@ -86,6 +116,7 @@ impl PersistentDeviceLog { //create new unit and genesis events let unit_key = KvKey::unit(obj_desc.clone()); + let unit_event = DeviceLogObject::Unit(VaultUnitEvent(KvLogEvent { key: unit_key.clone(), value: user_id.vault_name.clone(), @@ -103,3 +134,82 @@ impl PersistentDeviceLog { Ok(()) } } + +#[cfg(test)] +pub mod spec { + use std::sync::Arc; + use crate::node::common::model::user::common::UserData; + use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; + use crate::node::db::events::object_id::{Next, ObjectId, UnitId}; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::repo::generic_db::KvLogEventRepo; + use anyhow::Result; + use log::info; + use tracing_attributes::instrument; + + pub struct DeviceLogSpec { + pub p_obj: Arc>, + pub user: UserData, + } + + impl DeviceLogSpec { + + #[instrument(skip(self))] + pub async fn check_initialization(&self) -> Result<()> { + info!("Check initialization"); + + let device_log_desc = VaultDescriptor::device_log(self.user.user_id()); + + let unit_id = UnitId::unit(&device_log_desc); + + let unit_event_vault_name = self + .p_obj + .repo + .find_one(ObjectId::from(unit_id.clone())) + .await? + .unwrap() + .device_log()? + .get_unit()? + .vault_name(); + + assert_eq!(unit_event_vault_name, self.user.vault_name()); + + let genesis_id = unit_id.clone().next(); + let genesis_user = self + .p_obj + .repo + .find_one(ObjectId::from(genesis_id)) + .await? + .unwrap() + .device_log()? + .get_genesis()? + .user(); + + assert_eq!(genesis_user, self.user); + + Ok(()) + } + + #[instrument(skip(self))] + pub async fn check_sign_up_request(&self) -> Result<()> { + info!("check_sign_up_request"); + + let device_log_desc = VaultDescriptor::device_log(self.user.user_id()); + + let sign_up_request_id = UnitId::unit(&device_log_desc).next().next(); + + let candidate = self + .p_obj + .repo + .find_one(ObjectId::from(sign_up_request_id)) + .await? + .unwrap() + .device_log()? + .get_action()? + .get_create()?; + assert_eq!(candidate.device, self.user.device); + + Ok(()) + } + } +} \ No newline at end of file diff --git a/core/src/node/db/objects/persistent_object.rs b/core/src/node/db/objects/persistent_object.rs index b58132f3..4a05c53b 100644 --- a/core/src/node/db/objects/persistent_object.rs +++ b/core/src/node/db/objects/persistent_object.rs @@ -1,11 +1,12 @@ use std::sync::Arc; -use tracing::{debug, instrument, Instrument}; +use tracing::{instrument, Instrument}; use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; use crate::node::db::events::db_tail::DbTail; use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ObjIdExtractor}; use crate::node::db::events::object_id::{Next, ObjectId}; +use crate::node::db::in_mem_db::InMemKvLogEventRepo; use crate::node::db::objects::persistent_object_navigator::PersistentObjectNavigator; use crate::node::db::repo::generic_db::KvLogEventRepo; @@ -19,8 +20,6 @@ impl PersistentObject { &self, obj_desc: ObjectDescriptor, ) -> anyhow::Result> { - debug!("get_object_events_from_beginning"); - let unit_id = ObjectId::unit(obj_desc); let commit_log = self.find_object_events(unit_id).await?; @@ -36,10 +35,6 @@ impl PersistentObject { let maybe_curr_db_event = self.repo.find_one(curr_tail_id.clone()).await?; if let Some(curr_db_event) = maybe_curr_db_event { - if let GenericKvLogEvent::SharedSecret(_) = &curr_db_event { - self.repo.delete(curr_tail_id.clone()).in_current_span().await; - } - curr_tail_id = curr_tail_id.next(); commit_log.push(curr_db_event); } else { @@ -154,3 +149,33 @@ impl PersistentObject { PersistentObject { repo } } } + +impl PersistentObject { + pub fn in_mem() -> PersistentObject { + let repo = Arc::new(InMemKvLogEventRepo::default()); + PersistentObject::new(repo) + } +} + +#[cfg(test)] +pub mod fixture { + use std::sync::Arc; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::objects::persistent_object::PersistentObject; + + pub struct PersistentObjectFixture { + pub client: Arc>, + pub vd: Arc>, + pub server: Arc>, + } + + impl PersistentObjectFixture { + pub fn generate() -> Self { + Self { + client: Arc::new(PersistentObject::in_mem()), + vd: Arc::new(PersistentObject::in_mem()), + server: Arc::new(PersistentObject::in_mem()), + } + } + } +} diff --git a/core/src/node/db/objects/persistent_object_navigator.rs b/core/src/node/db/objects/persistent_object_navigator.rs index 4cacfe03..aed9a6a7 100644 --- a/core/src/node/db/objects/persistent_object_navigator.rs +++ b/core/src/node/db/objects/persistent_object_navigator.rs @@ -33,7 +33,7 @@ mod test { use std::sync::Arc; use crate::crypto::keys::{KeyManager, OpenBox}; - use crate::node::common::model::device::{DeviceData, DeviceName}; + use crate::node::common::model::device::common::{DeviceData, DeviceName}; use crate::node::db::events::generic_log_event::{ObjIdExtractor, ToGenericEvent, UnitEventWithEmptyValue}; use crate::node::db::events::global_index_event::GlobalIndexObject; use crate::node::db::in_mem_db::InMemKvLogEventRepo; diff --git a/core/src/node/db/objects/persistent_shared_secret.rs b/core/src/node/db/objects/persistent_shared_secret.rs index 42b28cea..0788b9c0 100644 --- a/core/src/node/db/objects/persistent_shared_secret.rs +++ b/core/src/node/db/objects/persistent_shared_secret.rs @@ -1,11 +1,11 @@ use std::sync::Arc; -use crate::node::common::model::device::DeviceId; + use anyhow::{bail, Ok}; use tracing::debug; - +use crate::node::common::model::device::common::DeviceId; use crate::node::common::model::secret::{MetaPasswordId, SecretDistributionData}; -use crate::node::common::model::user::UserData; +use crate::node::common::model::user::common::UserData; use crate::node::common::model::vault::VaultName; use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; @@ -58,13 +58,13 @@ impl PersistentSharedSecret { } pub async fn init(&self, user: UserData) -> anyhow::Result<()> { - self.init_device_log(user.clone()).await?; + self.init_ss_device_log(user.clone()).await?; self.init_ss_ledger(user).await?; Ok(()) } - pub async fn init_device_log(&self, user: UserData) -> anyhow::Result<()> { + async fn init_ss_device_log(&self, user: UserData) -> anyhow::Result<()> { let user_id = user.user_id(); let obj_desc = SharedSecretDescriptor::SSDeviceLog(user_id.device_id).to_obj_desc(); let unit_id = UnitId::unit(&obj_desc); @@ -124,3 +124,57 @@ impl PersistentSharedSecret { Ok(()) } } + +#[cfg(test)] +pub mod spec { + use std::sync::Arc; + use crate::node::common::model::user::common::UserData; + use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; + use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; + use crate::node::db::events::object_id::{ObjectId}; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::repo::generic_db::KvLogEventRepo; + use anyhow::{bail, Result}; + use log::info; + use tracing_attributes::instrument; + + pub struct SSDeviceLogSpec { + pub p_obj: Arc>, + pub client_user: UserData, + } + + impl SSDeviceLogSpec { + + #[instrument(skip(self))] + pub async fn check_initialization(&self) -> Result<()> { + info!("SSDeviceLogSpec check_initialization"); + + let device_id = self.client_user.device.device_id.clone(); + let ss_obj_desc = SharedSecretDescriptor::SSDeviceLog(device_id) + .to_obj_desc(); + + let ss_unit_id = ObjectId::unit(ss_obj_desc.clone()); + let ss_genesis_id = ObjectId::genesis(ss_obj_desc); + + let maybe_unit_event = self.p_obj.repo.find_one(ss_unit_id).await?; + + if let Some(unit_event) = maybe_unit_event { + let vault_name = unit_event.ss_device_log()?.get_unit()?.vault_name(); + assert_eq!(vault_name, self.client_user.vault_name()); + } else { + bail!("SSDevice, unit event not found"); + } + + let maybe_genesis_event = self.p_obj.repo.find_one(ss_genesis_id).await?; + + if let Some(genesis_event) = maybe_genesis_event { + let user = genesis_event.ss_device_log()?.get_genesis()?.user(); + assert_eq!(user, self.client_user); + } else { + bail!("SSDevice, genesis event not found"); + } + + Ok(()) + } + } +} diff --git a/core/src/node/db/objects/persistent_vault.rs b/core/src/node/db/objects/persistent_vault.rs index 933d82ec..86e44a34 100644 --- a/core/src/node/db/objects/persistent_vault.rs +++ b/core/src/node/db/objects/persistent_vault.rs @@ -1,79 +1,130 @@ use std::sync::Arc; -use anyhow::{anyhow, bail}; -use tracing_attributes::instrument; - -use crate::node::common::model::user::{UserData, UserDataMember, UserDataOutsider, UserMembership}; -use crate::node::common::model::vault::{VaultData, VaultStatus}; +use crate::node::common::model::user::common::{UserData, UserDataOutsider}; +use crate::node::common::model::vault::{VaultData, VaultName, VaultStatus}; use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; -use crate::node::db::events::vault_event::VaultMembershipObject; +use crate::node::db::events::object_id::{ArtifactId, ObjectId}; +use crate::node::db::events::vault::vault_log_event::VaultLogObject; +use crate::node::db::objects::global_index::ClientPersistentGlobalIndex; use crate::node::db::objects::persistent_object::PersistentObject; -use crate::node::db::repo::credentials_repo::CredentialsRepo; use crate::node::db::repo::generic_db::KvLogEventRepo; +use anyhow::{anyhow, bail}; +use tracing_attributes::instrument; pub struct PersistentVault { pub p_obj: Arc>, } impl PersistentVault { - pub async fn get_vault(&self) -> anyhow::Result { - let vault_status = self.find_for_default_user().await?; - match vault_status { - VaultStatus::Outsider(_) => Err(anyhow!("Vault not found")), - VaultStatus::Member { vault, .. } => Ok(vault), - } - } - pub async fn find_for_default_user(&self) -> anyhow::Result { - let creds_repo = CredentialsRepo { + pub async fn get_vault(&self, user_data: &UserData) -> anyhow::Result<(ArtifactId, VaultData)> { + let p_vault = PersistentVault { p_obj: self.p_obj.clone(), }; - let creds = creds_repo.get_user_creds().await?; - self.find(creds.user()).await - } + let vault_status = p_vault.find(user_data.clone()).await?; + match vault_status { + VaultStatus::NotExists(_) => { + Err(anyhow!("Vault not found")) + } + VaultStatus::Outsider(_) => { + Err(anyhow!("Sender is not a member of the vault")) + } + VaultStatus::Member { vault, .. } => { + //save new vault state + let vault_desc = VaultDescriptor::vault(vault.vault_name.clone()); - #[instrument(skip_all)] - pub async fn find(&self, user: UserData) -> anyhow::Result { - let membership = self.vault_membership(user).await?; - - match membership { - UserMembership::Outsider(outsider) => Ok(VaultStatus::Outsider(outsider)), - UserMembership::Member(UserDataMember(member)) => { - let maybe_vault = { - let vault_desc = VaultDescriptor::vault(member.vault_name.clone()); - self.p_obj.find_tail_event(vault_desc).await? + let vault_free_id = self.p_obj + .find_free_id_by_obj_desc(vault_desc.clone()) + .await?; + + let ObjectId::Artifact(vault_artifact_id) = vault_free_id else { + return Err(anyhow!("Invalid vault id, must be ArtifactId, but it's: {:?}",vault_free_id)); }; - if let Some(vault_event) = maybe_vault { - let vault_status = vault_event.vault()?.status(member); - Ok(vault_status) - } else { - bail!("Invalid db structure. Vault not found"); - } + anyhow::Ok((vault_artifact_id, vault)) } } } + + #[instrument(skip_all)] + pub async fn find(&self, user: UserData) -> anyhow::Result { + let p_gi = ClientPersistentGlobalIndex { p_obj: self.p_obj.clone() }; + let vault_exists = p_gi.exists(user.vault_name()).await?; + if !vault_exists { + return Ok(VaultStatus::NotExists(user)); + } - pub async fn vault_membership(&self, user_data: UserData) -> anyhow::Result { - let desc = VaultDescriptor::vault_membership(user_data.user_id()); - let maybe_tail_event = self.p_obj.find_tail_event(desc).await?; - - match maybe_tail_event { - None => Ok(UserMembership::Outsider(UserDataOutsider::unknown(user_data))), - Some(tail_event) => { - let vault_membership_obj = VaultMembershipObject::try_from(tail_event)?; - - match vault_membership_obj { - VaultMembershipObject::Unit { .. } => { - Ok(UserMembership::Outsider(UserDataOutsider::unknown(user_data))) - } - VaultMembershipObject::Genesis { .. } => { - Ok(UserMembership::Outsider(UserDataOutsider::unknown(user_data))) - } - VaultMembershipObject::Membership(event) => Ok(event.value), - } + let vault_desc = VaultDescriptor::vault(user.vault_name()); + let maybe_vault_event = self.p_obj + .find_tail_event(vault_desc) + .await?; + + let gi_and_status = (vault_exists, maybe_vault_event); + match gi_and_status { + (false, Some(_)) => { + bail!("Invalid state. Vault not in global index") + } + //Vault is not in global index, hence vault not exists + (false, None) => { + Ok(VaultStatus::NotExists(user)) + }, + //There is no vault table on local machine, but it is present in global index, + //which means, current user is outsider + (true, None) => { + Ok(VaultStatus::Outsider(UserDataOutsider::non_member(user))) + }, + (true, Some(vault_event)) => { + let vault_obj = vault_event.vault()?; + Ok(vault_obj.status(user.clone())) } } } + + async fn vault_log_events(&self, vault_name: VaultName) -> anyhow::Result> { + let desc = VaultDescriptor::vault_log(vault_name); + let events = self.p_obj + .find_object_events(ObjectId::unit(desc)) + .await?; + + let mut vault_log_events = vec![]; + for event in events { + let vault_log_event = event.vault_log()?; + vault_log_events.push(vault_log_event); + } + + Ok(vault_log_events) + } } + + +#[cfg(test)] +pub mod spec { + use crate::node::common::model::user::common::UserData; + use crate::node::db::events::vault::vault_log_event::VaultLogObject; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::objects::persistent_vault::PersistentVault; + use crate::node::db::repo::generic_db::KvLogEventRepo; + use std::sync::Arc; + + pub struct VaultLogSpec { + pub p_obj: Arc>, + pub user: UserData + } + + impl VaultLogSpec { + pub async fn verify_initial_state(&self) -> anyhow::Result<()> { + let events = self.vault_log().await?; + assert_eq!(3, events.len()); + + Ok(()) + } + + async fn vault_log(&self) -> anyhow::Result> { + let p_vault = PersistentVault { + p_obj: self.p_obj.clone(), + }; + p_vault.vault_log_events(self.user.vault_name()).await + } + } +} \ No newline at end of file diff --git a/core/src/node/db/repo/credentials_repo.rs b/core/src/node/db/repo/credentials_repo.rs deleted file mode 100644 index 9103c39e..00000000 --- a/core/src/node/db/repo/credentials_repo.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::sync::Arc; - -use anyhow::anyhow; -use tracing::{info, instrument}; - -use crate::node::common::model::device::{DeviceCredentials, DeviceName}; -use crate::node::common::model::user::UserCredentials; -use crate::node::common::model::vault::VaultName; -use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; -use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent, UnitEvent}; -use crate::node::db::events::kv_log_event::KvLogEvent; -use crate::node::db::events::local_event::CredentialsObject; -use crate::node::db::events::object_id::ObjectId; -use crate::node::db::objects::persistent_object::PersistentObject; -use crate::node::db::repo::generic_db::KvLogEventRepo; - -pub struct CredentialsRepo { - pub p_obj: Arc>, -} - -impl CredentialsRepo { - #[instrument(skip(self))] - pub async fn generate_user( - &self, - device_name: DeviceName, - vault_name: VaultName, - ) -> anyhow::Result { - info!("Create a new user locally"); - - let device_creds = self.get_or_generate_device_creds(device_name).await?; - - let creds = UserCredentials::from(device_creds, vault_name); - let user_creds = CredentialsObject::default_user(creds); - - self.save(user_creds.clone()).await?; - - Ok(user_creds) - } - - pub async fn get_or_generate_device_creds(&self, device_name: DeviceName) -> anyhow::Result { - let maybe_creds = self.find().await?; - - let device_creds = match maybe_creds { - None => self.generate_device_creds(device_name).await?, - Some(creds) => match creds { - CredentialsObject::Device(event) => event.value, - CredentialsObject::DefaultUser(event) => event.value.device_creds - }, - }; - Ok(device_creds) - } - - pub async fn save(&self, creds: CredentialsObject) -> anyhow::Result { - let generic_event = creds.to_generic(); - self.p_obj.repo.save(generic_event).await - } - - #[instrument(skip_all)] - pub async fn get_user_creds(&self) -> anyhow::Result { - let creds_obj = self.find().await?.ok_or_else(|| anyhow!("No credentials found"))?; - - match creds_obj { - CredentialsObject::Device { .. } => Err(anyhow!("Device credentials found, User credentials expected")), - CredentialsObject::DefaultUser(event) => Ok(event.value), - } - } - - #[instrument(skip_all)] - pub async fn get(&self) -> anyhow::Result { - self.find().await?.ok_or_else(|| anyhow!("No credentials found")) - } - - #[instrument(skip_all)] - pub async fn find(&self) -> anyhow::Result> { - let maybe_creds = self.p_obj - .find_tail_event(ObjectDescriptor::CredsIndex) - .await?; - - let Some(creds) = maybe_creds else { - return Ok(None); - }; - - let creds_obj = CredentialsObject::try_from(creds)?; - Ok(Some(creds_obj)) - } - - pub async fn generate_device_creds(&self, device_name: DeviceName) -> anyhow::Result { - let device_creds = DeviceCredentials::generate(device_name); - let creds_obj = CredentialsObject::unit(device_creds.clone()); - self.p_obj - .repo - .save(GenericKvLogEvent::Credentials(creds_obj.clone())) - .await?; - Ok(device_creds) - } - - #[instrument(skip_all)] - pub async fn generate_user_creds( - &self, - device_name: DeviceName, - vault_name: VaultName, - ) -> anyhow::Result { - let user_creds = UserCredentials::generate(device_name, vault_name); - let creds_obj = CredentialsObject::default_user(user_creds.clone()); - - self.save(creds_obj).await?; - - Ok(user_creds) - } - - #[instrument(skip_all)] - pub async fn get_or_generate_user_creds( - &self, - device_name: DeviceName, - vault_name: VaultName, - ) -> anyhow::Result { - let maybe_creds = self.find().await?; - - match maybe_creds { - None => self.generate_user_creds(device_name, vault_name).await, - Some(CredentialsObject::Device(KvLogEvent { value: creds, .. })) => { - let user_creds = UserCredentials::from(creds, vault_name); - self.save(CredentialsObject::default_user(user_creds.clone())).await?; - Ok(user_creds) - } - Some(CredentialsObject::DefaultUser(event)) => Ok(event.value), - } - } -} diff --git a/core/src/node/db/repo/mod.rs b/core/src/node/db/repo/mod.rs index 42f06ce1..8ee916f3 100644 --- a/core/src/node/db/repo/mod.rs +++ b/core/src/node/db/repo/mod.rs @@ -1,2 +1,2 @@ -pub mod credentials_repo; +pub mod persistent_credentials; pub mod generic_db; diff --git a/core/src/node/db/repo/persistent_credentials.rs b/core/src/node/db/repo/persistent_credentials.rs new file mode 100644 index 00000000..3e75ee9a --- /dev/null +++ b/core/src/node/db/repo/persistent_credentials.rs @@ -0,0 +1,213 @@ +use std::sync::Arc; +use log::info; +use crate::node::common::model::user::user_creds::UserCredentials; +use crate::node::common::model::vault::VaultName; +use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; +use crate::node::db::events::generic_log_event::{ToGenericEvent, UnitEvent}; +use crate::node::db::events::kv_log_event::KvLogEvent; +use crate::node::db::events::local_event::CredentialsObject; +use crate::node::db::events::object_id::ObjectId; +use crate::node::db::objects::persistent_object::PersistentObject; +use crate::node::db::repo::generic_db::KvLogEventRepo; +use tracing::instrument; +use crate::node::common::model::device::common::DeviceName; +use crate::node::common::model::device::device_creds::DeviceCredentials; + +pub struct PersistentCredentials { + pub p_obj: Arc>, +} + +impl PersistentCredentials { + + #[instrument(skip(self))] + pub async fn get_or_generate_device_creds(&self, device_name: DeviceName) -> anyhow::Result { + let maybe_creds = self.find().await?; + + let device_creds = match maybe_creds { + None => self.generate_device_creds(device_name).await?, + Some(creds) => match creds { + CredentialsObject::Device(event) => event.value, + CredentialsObject::DefaultUser(event) => event.value.device_creds, + }, + }; + Ok(device_creds) + } + + #[instrument(skip(self))] + async fn save(&self, creds: CredentialsObject) -> anyhow::Result { + let generic_event = creds.to_generic(); + self.p_obj.repo.save(generic_event).await + } + + #[instrument(skip(self))] + pub async fn save_device_creds(&self, device_creds: &DeviceCredentials) -> anyhow::Result<()> { + let creds_obj = CredentialsObject::unit(device_creds.clone()); + let generic_event = creds_obj.to_generic(); + self.p_obj.repo.save(generic_event).await?; + Ok(()) + } + + #[instrument(skip_all)] + pub async fn get_user_creds(&self) -> anyhow::Result> { + let maybe_creds_obj = self.find().await?; + + let Some(creds_obj) = maybe_creds_obj else { + return Ok(None); + }; + + match creds_obj { + CredentialsObject::Device { .. } => Ok(None), + CredentialsObject::DefaultUser(event) => Ok(Some(event.value)), + } + } + + #[instrument(skip_all)] + pub async fn find(&self) -> anyhow::Result> { + let maybe_creds = self.p_obj.find_tail_event(ObjectDescriptor::CredsIndex).await?; + + let Some(creds) = maybe_creds else { + return Ok(None); + }; + + let creds_obj = CredentialsObject::try_from(creds)?; + Ok(Some(creds_obj)) + } + + #[instrument(skip(self))] + async fn generate_device_creds(&self, device_name: DeviceName) -> anyhow::Result { + let device_creds = DeviceCredentials::generate(device_name); + info!("Device credentials has been generated: {:?}", &device_creds.device); + + self.save_device_creds(&device_creds).await?; + Ok(device_creds) + } + + #[instrument(skip_all)] + pub async fn get_or_generate_user_creds( + &self, + device_name: DeviceName, + vault_name: VaultName, + ) -> anyhow::Result { + let maybe_creds = self.find().await?; + + match maybe_creds { + None => { + let device_creds = self.get_or_generate_device_creds(device_name.clone()).await?; + let user_creds = UserCredentials::from(device_creds, vault_name); + self.save(CredentialsObject::default_user(user_creds.clone())).await?; + Ok(user_creds) + }, + Some(CredentialsObject::Device(KvLogEvent { value: creds, .. })) => { + let user_creds = UserCredentials::from(creds, vault_name); + self.save(CredentialsObject::default_user(user_creds.clone())).await?; + Ok(user_creds) + } + Some(CredentialsObject::DefaultUser(event)) => Ok(event.value), + } + } +} + +#[cfg(test)] +pub mod fixture { + use std::sync::Arc; + use crate::meta_tests::fixture_util::fixture::states::EmptyState; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::repo::persistent_credentials::PersistentCredentials; + + pub struct PersistentCredentialsFixture { + pub client_p_creds: Arc>, + pub vd_p_creds: Arc>, + pub server_p_creds: Arc> + } + + impl PersistentCredentialsFixture { + pub async fn init(state: &EmptyState) -> anyhow::Result { + let client_p_creds = Arc::new(PersistentCredentials { + p_obj: state.p_obj.client.clone(), + }); + + let vd_p_creds = Arc::new(PersistentCredentials { + p_obj: state.p_obj.vd.clone(), + }); + + let server_p_creds = Arc::new(PersistentCredentials { + p_obj: state.p_obj.server.clone(), + }); + + let _ = client_p_creds.save_device_creds(&state.device_creds.client).await?; + let _ = client_p_creds.get_or_generate_user_creds( + state.device_creds.client.device.device_name.clone(), + state.user_creds.client.user().vault_name() + ).await?; + + let _ = vd_p_creds.save_device_creds(&state.device_creds.vd).await?; + let _ = vd_p_creds.get_or_generate_user_creds( + state.device_creds.vd.device.device_name.clone(), + state.user_creds.vd.user().vault_name() + ).await?; + + let _ = server_p_creds.save_device_creds(&state.device_creds.server).await?; + + Ok(Self { client_p_creds, vd_p_creds, server_p_creds }) + } + } + +} + +#[cfg(test)] +pub mod spec { + use std::sync::Arc; + use crate::node::db::descriptors::object_descriptor::ObjectDescriptor; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::repo::generic_db::KvLogEventRepo; + + pub struct CredentialsRepoSpec { + pub p_obj: Arc>, + } + + impl CredentialsRepoSpec { + pub async fn verify(&self) -> anyhow::Result<()> { + let creds_desc = ObjectDescriptor::CredsIndex; + + let events = self.p_obj + .get_object_events_from_beginning(creds_desc) + .await?; + + assert_eq!(events.len(), 2); + + Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + use tracing_attributes::instrument; + use crate::meta_tests::setup_tracing; + use crate::node::common::model::device::common::DeviceName; + use crate::node::common::model::vault::VaultName; + use crate::node::db::objects::persistent_object::PersistentObject; + use crate::node::db::repo::persistent_credentials::PersistentCredentials; + use crate::node::db::repo::persistent_credentials::spec::CredentialsRepoSpec; + + #[tokio::test] + #[instrument] + async fn test_get_or_generate_user_creds() -> anyhow::Result<()> { + setup_tracing()?; + + let p_obj = Arc::new(PersistentObject::in_mem()); + + let creds_repo = PersistentCredentials {p_obj: p_obj.clone() }; + let vault_name = VaultName::from("test"); + let _ = creds_repo + .get_or_generate_user_creds(DeviceName::server(), vault_name) + .await?; + + let spec = CredentialsRepoSpec {p_obj: p_obj.clone()}; + spec.verify().await?; + + Ok(()) + } +} diff --git a/core/src/node/server/mod.rs b/core/src/node/server/mod.rs index 04c6da16..91c13709 100644 --- a/core/src/node/server/mod.rs +++ b/core/src/node/server/mod.rs @@ -1,3 +1,4 @@ pub mod request; pub mod server_app; pub mod server_data_sync; + diff --git a/core/src/node/server/request.rs b/core/src/node/server/request.rs index 7328847f..39f7bebf 100644 --- a/core/src/node/server/request.rs +++ b/core/src/node/server/request.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; - -use crate::node::common::model::device::DeviceData; -use crate::node::common::model::user::UserData; +use crate::node::common::model::device::common::DeviceData; +use crate::node::common::model::user::common::UserData; use crate::node::db::events::object_id::ObjectId; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/core/src/node/server/server_app.rs b/core/src/node/server/server_app.rs index 7dd58638..b74e478a 100644 --- a/core/src/node/server/server_app.rs +++ b/core/src/node/server/server_app.rs @@ -2,24 +2,26 @@ use std::sync::Arc; use anyhow::Result; use tracing::{error, info, instrument}; - use crate::node::common::data_transfer::MpscDataTransfer; -use crate::node::common::model::device::{DeviceCredentials, DeviceName}; +use crate::node::common::model::device::common::DeviceName; +use crate::node::common::model::device::device_creds::DeviceCredentials; use crate::node::db::events::generic_log_event::GenericKvLogEvent; -use crate::node::db::objects::global_index::PersistentGlobalIndex; +use crate::node::db::objects::global_index::ServerPersistentGlobalIndex; use crate::node::db::objects::persistent_device_log::PersistentDeviceLog; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::objects::persistent_shared_secret::PersistentSharedSecret; -use crate::node::db::repo::credentials_repo::CredentialsRepo; +use crate::node::db::repo::persistent_credentials::PersistentCredentials; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::node::server::request::SyncRequest; use crate::node::server::server_data_sync::{ - DataEventsResponse, DataSyncApi, DataSyncRequest, DataSyncResponse, ServerDataSync, ServerTailResponse, + DataEventsResponse, DataSyncApi, DataSyncRequest, DataSyncResponse, ServerSyncGateway, ServerTailResponse, }; pub struct ServerApp { - pub data_sync: ServerDataSync, - p_obj: Arc>, + pub data_sync: ServerSyncGateway, + pub p_obj: Arc>, + creds_repo: PersistentCredentials, + server_dt: Arc, } pub struct ServerDataTransfer { @@ -27,112 +29,279 @@ pub struct ServerDataTransfer { } impl ServerApp { - pub async fn init(repo: Arc) -> anyhow::Result { + pub fn new(repo: Arc, server_dt: Arc) -> Result { let p_obj = { let obj = PersistentObject::new(repo); Arc::new(obj) }; - let creds_repo = CredentialsRepo { p_obj: p_obj.clone() }; + let data_sync = ServerSyncGateway { + p_obj: p_obj.clone(), + }; - let device_creds = creds_repo - .get_or_generate_device_creds(DeviceName::from("server")) - .await?; + let creds_repo = PersistentCredentials { p_obj: p_obj.clone() }; - let data_sync = ServerDataSync { - persistent_obj: p_obj.clone(), - device_creds: device_creds.clone(), - }; + Ok(Self { data_sync, p_obj, creds_repo, server_dt }) + } - let gi_obj = PersistentGlobalIndex { - p_obj: data_sync.persistent_obj.clone(), + async fn init(&self) -> Result { + let device_creds = self.get_creds().await?; + + let gi_obj = ServerPersistentGlobalIndex { + p_obj: self.data_sync.p_obj.clone(), server_device: device_creds.device.clone(), }; gi_obj.init().await?; - Ok(ServerApp { data_sync, p_obj }) + Ok(device_creds) } - #[instrument(skip_all)] - pub async fn run(&self, data_transfer: Arc) -> anyhow::Result<()> { + #[instrument(skip(self))] + pub async fn run(&self) -> Result<()> { info!("Run server app"); - while let Ok(sync_message) = data_transfer.dt.service_receive().await { - self.handle_client_request(sync_message, data_transfer.clone()).await?; + let init_result = self.init().await; + if let Err(err) = &init_result { + error!("ServerApp failed to start: {:?}", err); + } + + let server_creds = init_result?; + + info!("Started ServerApp with: {:?}", &server_creds.device); + + while let Ok(sync_message) = self.server_dt.dt.service_receive().await { + let result = self.handle_client_request(server_creds.clone(), sync_message.clone()).await; + if let Err(err) = &result { + error!("Failed handling incoming request: {:?}, with error: {}", sync_message, err); + } + + result? } Ok(()) } - #[instrument(skip_all)] + #[instrument(skip(self, server_creds))] async fn handle_client_request( - &self, - sync_message: DataSyncRequest, - data_transfer: Arc, + &self, server_creds: DeviceCredentials, sync_message: DataSyncRequest ) -> Result<()> { match sync_message { DataSyncRequest::SyncRequest(request) => { - let new_events = self.handle_sync_request(request).await; + let new_events = self.handle_sync_request(request).await?; - data_transfer + self + .server_dt .dt .send_to_client(DataSyncResponse::Data(DataEventsResponse(new_events))) .await; } DataSyncRequest::Event(event) => { - self.handle_new_event(event).await?; + self.data_sync.handle(server_creds.device, event).await?; } - DataSyncRequest::ServerTailRequest(user_id) => { + DataSyncRequest::ServerTailRequest(user) => { let p_device_log = PersistentDeviceLog { p_obj: self.p_obj.clone(), }; - let device_log_tail = p_device_log.find_tail_id(&user_id).await?; + let device_log_tail = p_device_log + .find_tail_id(&user.user_id()) + .await?; let p_ss = PersistentSharedSecret { p_obj: self.p_obj.clone(), }; - let ss_device_log_tail = p_ss.find_device_tail_id(&user_id.device_id).await?; + let ss_device_log_tail = p_ss + .find_device_tail_id(&user.device.device_id) + .await?; - let response = ServerTailResponse { - device_log_tail, - ss_device_log_tail, - }; - let response = DataSyncResponse::ServerTailResponse(response); + let response = ServerTailResponse { device_log_tail, ss_device_log_tail }; - data_transfer.dt.send_to_client(response).await; + let data_sync_response = DataSyncResponse::ServerTailResponse(response); + + self + .server_dt + .dt + .send_to_client(data_sync_response) + .await; } } Ok(()) } - async fn handle_new_event(&self, event: GenericKvLogEvent) -> Result<()> { - self.data_sync.send(event).await?; - Ok(()) + pub async fn handle_sync_request(&self, request: SyncRequest) -> Result> { + self.data_sync.replication(request).await } - pub async fn handle_sync_request(&self, request: SyncRequest) -> Vec { - let new_events_result = self.data_sync.replication(request).await; + pub async fn get_creds(&self) -> Result { + self.creds_repo + .get_or_generate_device_creds(DeviceName::server()) + .await + } +} - match new_events_result { - Ok(data) => { - //debug!(format!("New events for a client: {:?}", data).as_str()); - data - } - Err(_) => { - error!("Server. Sync Error"); - vec![] - } +#[cfg(test)] +pub mod fixture { + use std::sync::Arc; + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::meta_tests::fixture_util::fixture::states::BaseState; + use crate::node::common::data_transfer::MpscDataTransfer; + use crate::node::db::in_mem_db::InMemKvLogEventRepo; + use crate::node::server::server_app::{ServerApp, ServerDataTransfer}; + + pub struct ServerAppFixture { + pub server_app: Arc>, + } + + impl ServerAppFixture { + pub fn try_from(registry: &FixtureRegistry) -> anyhow::Result { + let repo = registry.state.empty.p_obj.server.repo.clone(); + let server_dt = registry.state.server_dt.server_dt.clone(); + let server_app = Arc::new(ServerApp::new(repo, server_dt)?); + Ok(Self { server_app }) } } - pub async fn get_creds(&self) -> Result { - let creds_repo = CredentialsRepo { - p_obj: self.p_obj.clone(), + pub struct ServerDataTransferFixture { + pub server_dt: Arc, + } + + impl ServerDataTransferFixture { + pub fn generate() -> Self { + let server_dt = Arc::new(ServerDataTransfer { dt: MpscDataTransfer::new() }); + + Self { server_dt } + } + } +} + +#[cfg(test)] +mod test { + use std::thread; + use std::time::Duration; + use anyhow::bail; + use tracing::{info, Instrument}; + use crate::meta_tests::fixture_util::fixture::FixtureRegistry; + use crate::meta_tests::setup_tracing; + use crate::node::common::meta_tracing::{client_span, server_span}; + use crate::node::db::actions::sign_up::claim::spec::SignUpClaimSpec; + use crate::node::db::actions::sign_up::claim::test_action::SignUpClaimTestAction; + use crate::node::db::descriptors::object_descriptor::ToObjectDescriptor; + use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; + use tokio::runtime::{Builder}; + use crate::meta_tests::fixture_util::fixture::states::ExtendedState; + use crate::meta_tests::spec::test_spec::TestSpec; + use crate::node::common::model::user::common::UserData; + use crate::node::common::model::vault::VaultStatus; + use crate::node::db::objects::persistent_vault::PersistentVault; + + #[tokio::test] + #[ignore] + async fn test_sign_up_one_device() -> anyhow::Result<()> { + setup_tracing()?; + + let registry = FixtureRegistry::extended().await?; + + run_server(®istry).await?; + + info!("Executing 'sign up' claim"); + let client_p_obj = registry.state.base.empty.p_obj.client.clone(); + let client_user_creds = ®istry.state.base.empty.user_creds; + let _ = SignUpClaimTestAction::sign_up(client_p_obj.clone(), client_user_creds) + .instrument(client_span()) + .await?; + + sync_client(®istry).await?; + + info!("Verify SignUpClaim"); + let client_user = registry.state.base.empty.user_creds.client.user(); + let client_claim_spec = SignUpClaimSpec { + p_obj: client_p_obj.clone(), + user: client_user.clone(), + server_device: registry.state.base.empty.device_creds.server.device.clone(), }; - creds_repo - .get_or_generate_device_creds(DeviceName::from("server")) - .await + client_claim_spec + .verify() + .instrument(client_span()) + .await?; + + let client_db = client_p_obj.repo.get_db().await; + assert_eq!(11, client_db.len()); + + async_std::task::sleep(Duration::from_secs(3)).await; + + p_vault_check(®istry).await?; + + async_std::task::sleep(Duration::from_secs(1)).await; + + sync_client(®istry).await?; + + async_std::task::sleep(Duration::from_secs(1)).await; + + server_check(registry, client_user).await?; + + Ok(()) + } + + async fn server_check(registry: FixtureRegistry, client_user: UserData) -> anyhow::Result<()> { + let server_app = registry.state.server_app.server_app.clone(); + let server_ss_device_log_events = { + let ss_desc = SharedSecretDescriptor::SSDeviceLog(client_user.device.device_id.clone()) + .to_obj_desc(); + + server_app + .p_obj + .get_object_events_from_beginning(ss_desc) + .instrument(server_span()) + .await? + }; + info!("SERVER SS device log EVENTS: {:?}", server_ss_device_log_events.len()); + + let server_db = server_app.p_obj.repo.get_db().await; + + assert_eq!(server_db.len(), 18); + + let server_claim_spec = SignUpClaimSpec { + p_obj: server_app.p_obj.clone(), + user: client_user.clone(), + server_device: registry.state.base.empty.device_creds.server.device, + }; + + server_claim_spec.verify().await?; + Ok(()) + } + + async fn p_vault_check(registry: &FixtureRegistry) -> anyhow::Result<()> { + let p_vault = PersistentVault { + p_obj: registry.state.base.empty.p_obj.client.clone(), + }; + let vault_status = p_vault + .find(registry.state.base.empty.user_creds.client.user()) + .await?; + + let VaultStatus::Member { .. } = &vault_status else { + bail!("Client is not a vault member: {:?}", vault_status); + }; + Ok(()) + } + + async fn sync_client(registry: &FixtureRegistry) -> anyhow::Result<()> { + registry.state.meta_client_service.sync_gateway.client_gw.sync().await?; + Ok(()) + } + + async fn run_server(registry: &FixtureRegistry) -> anyhow::Result<()> { + let server_app = registry.state.server_app.server_app.clone(); + server_app.init().await?; + + thread::spawn(move || { + let rt = Builder::new_multi_thread().enable_all().build().unwrap(); + rt.block_on(async { + server_app.run().instrument(server_span()).await + }) + }); + async_std::task::sleep(Duration::from_secs(1)).await; + + Ok(()) } } diff --git a/core/src/node/server/server_data_sync.rs b/core/src/node/server/server_data_sync.rs index c10cb053..83172328 100644 --- a/core/src/node/server/server_data_sync.rs +++ b/core/src/node/server/server_data_sync.rs @@ -1,45 +1,39 @@ use std::sync::Arc; -use anyhow::{anyhow, bail, Ok}; -use async_trait::async_trait; -use tracing::{debug, info, instrument}; - -use crate::node::common::model::device::{DeviceCredentials, DeviceData}; -use crate::node::common::model::user::{UserData, UserDataMember, UserId}; -use crate::node::common::model::vault::{VaultData, VaultName}; -use crate::node::db::actions::sign_up::SignUpAction; -use crate::node::db::descriptors::global_index_descriptor::GlobalIndexDescriptor; -use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ToObjectDescriptor}; +use crate::node::common::model::user::common::{UserData}; +use crate::node::common::model::vault::{VaultStatus}; +use crate::node::db::descriptors::object_descriptor::{ToObjectDescriptor}; use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; -use crate::node::db::descriptors::vault_descriptor::VaultDescriptor; use crate::node::db::events::generic_log_event::{GenericKvLogEvent, ToGenericEvent}; -use crate::node::db::events::global_index_event::GlobalIndexObject; use crate::node::db::events::kv_log_event::{KvKey, KvLogEvent}; -use crate::node::db::events::object_id::{ArtifactId, Next, ObjectId, UnitId}; -use crate::node::db::events::shared_secret_event::SSLedgerObject; -use crate::node::db::events::vault_event::{ - DeviceLogObject, VaultAction, VaultLogObject, VaultMembershipObject, VaultObject, -}; +use crate::node::db::events::object_id::{Next, ObjectId}; +use crate::node::db::events::shared_secret_event::{SSDeviceLogObject, SSLedgerObject}; use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::node::server::request::{SyncRequest, VaultRequest}; +use anyhow::{anyhow, bail, Ok}; +use async_trait::async_trait; +use tracing::{info, instrument}; +use crate::node::common::model::device::common::{DeviceData, DeviceName}; +use crate::node::db::actions::vault::vault_action::VaultAction; +use crate::node::db::events::vault::device_log_event::DeviceLogObject; +use crate::node::db::objects::persistent_vault::PersistentVault; #[async_trait(? Send)] pub trait DataSyncApi { async fn replication(&self, request: SyncRequest) -> anyhow::Result>; - async fn send(&self, event: GenericKvLogEvent) -> anyhow::Result<()>; + async fn handle(&self, server_device: DeviceData, event: GenericKvLogEvent) -> anyhow::Result<()>; } -pub struct ServerDataSync { - pub persistent_obj: Arc>, - pub device_creds: DeviceCredentials, +pub struct ServerSyncGateway { + pub p_obj: Arc> } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum DataSyncRequest { SyncRequest(SyncRequest), - ServerTailRequest(UserId), + ServerTailRequest(UserData), Event(GenericKvLogEvent), } @@ -78,30 +72,45 @@ impl DataSyncResponse { } #[async_trait(? Send)] -impl DataSyncApi for ServerDataSync { +impl DataSyncApi for ServerSyncGateway { #[instrument(skip(self))] async fn replication(&self, request: SyncRequest) -> anyhow::Result> { let mut commit_log: Vec = vec![]; - match request { + match &request { SyncRequest::GlobalIndex(gi_request) => { - let gi_events = self.global_index_replication(gi_request.global_index.clone()).await?; + let gi_events = self + .global_index_replication(gi_request.global_index.clone()) + .await?; commit_log.extend(gi_events); } SyncRequest::Vault(vault_request) => { - let maybe_vault = self.find_vault(vault_request.vault.clone()).await?; - let Some(vault) = maybe_vault else { - return Ok(commit_log); + let p_vault = PersistentVault { + p_obj: self.p_obj.clone(), }; - if !vault.is_member(&vault_request.sender.device.id) { - return Ok(commit_log); + let vault_status = p_vault.find(vault_request.sender.clone()).await?; + match vault_status { + VaultStatus::NotExists(_) => { + if vault_request.sender.device.device_name.eq(&DeviceName::client()) { + info!("VaultStatus::NotExists!!!") + } + return Ok(commit_log); + } + VaultStatus::Outsider(outsider) => { + if vault_request.sender.device.device_name == DeviceName::client() { + info!("VaultStatus::Outsider: {:?}", outsider) + } + return Ok(commit_log); + } + VaultStatus::Member { .. } => { + let vault_events = self + .vault_replication(vault_request.clone()) + .await?; + commit_log.extend(vault_events); + } } - - let vault_events = self.vault_replication(vault_request).await?; - - commit_log.extend(vault_events); } } @@ -109,57 +118,67 @@ impl DataSyncApi for ServerDataSync { } /// Handle request: all types of requests will be handled and the actions will be executed accordingly - async fn send(&self, generic_event: GenericKvLogEvent) -> anyhow::Result<()> { - self.server_processing(generic_event).await + async fn handle( + &self, server_device: DeviceData, generic_event: GenericKvLogEvent + ) -> anyhow::Result<()> { + self.server_processing(server_device, generic_event).await } } -impl ServerDataSync { - #[instrument(skip_all)] - async fn server_processing(&self, generic_event: GenericKvLogEvent) -> anyhow::Result<()> { - debug!("DataSync::event_processing: {:?}", &generic_event); - +impl ServerSyncGateway { + #[instrument(skip(self))] + async fn server_processing( + &self, server_device: DeviceData, generic_event: GenericKvLogEvent + ) -> anyhow::Result<()> { match &generic_event { GenericKvLogEvent::DeviceLog(device_log_obj) => { - self.handle_device_log_request(device_log_obj).await?; + self.handle_device_log_request(server_device, device_log_obj).await?; } GenericKvLogEvent::SSDeviceLog(ss_device_log_obj) => { - self.persistent_obj + info!("Shared Secret Device Log message processing: {:?}", &ss_device_log_obj); + + self.p_obj .repo .save(ss_device_log_obj.clone().to_generic()) .await?; - let ss_claim = ss_device_log_obj.get_distribution_request()?; - - let ss_ledger_desc = SharedSecretDescriptor::SSLedger(ss_claim.vault_name.clone()).to_obj_desc(); - - let maybe_generic_ss_ledger = self.persistent_obj.find_tail_event(ss_ledger_desc.clone()).await?; - - match maybe_generic_ss_ledger { - Some(generic_ss_ledger) => { - let ss_ledger_obj = SSLedgerObject::try_from(generic_ss_ledger)?; - - let mut ss_ledger = ss_ledger_obj.to_ledger_data()?; - if ss_ledger.claims.contains_key(&ss_claim.id) { - //the claim is already in the ledger, no action needed - return Ok(()); - } else { - //add the claim to the ledger - ss_ledger.claims.insert(ss_claim.id.clone(), ss_claim.clone()); - - //update ss_ledger - let updated_ss_ledger = SSLedgerObject::Ledger(KvLogEvent { - key: KvKey { - obj_id: ss_ledger_obj.get_ledger_id()?.next(), - obj_desc: ss_ledger_desc, - }, - value: ss_ledger, - }); - - self.persistent_obj.repo.save(updated_ss_ledger.to_generic()).await?; + + if let SSDeviceLogObject::SSDeviceLog(event) = ss_device_log_obj { + let ss_claim = event.value.clone(); + + let ss_ledger_desc = SharedSecretDescriptor::SSLedger(ss_claim.vault_name.clone()) + .to_obj_desc(); + + let maybe_generic_ss_ledger = self.p_obj + .find_tail_event(ss_ledger_desc.clone()) + .await?; + + match maybe_generic_ss_ledger { + Some(generic_ss_ledger) => { + let ss_ledger_obj = SSLedgerObject::try_from(generic_ss_ledger)?; + + let mut ss_ledger = ss_ledger_obj.to_ledger_data()?; + if ss_ledger.claims.contains_key(&ss_claim.id) { + //the claim is already in the ledger, no action needed + return Ok(()); + } else { + //add the claim to the ledger + ss_ledger.claims.insert(ss_claim.id.clone(), ss_claim.clone()); + + //update ss_ledger + let updated_ss_ledger = SSLedgerObject::Ledger(KvLogEvent { + key: KvKey { + obj_id: ss_ledger_obj.get_ledger_id()?.next(), + obj_desc: ss_ledger_desc, + }, + value: ss_ledger, + }); + + self.p_obj.repo.save(updated_ss_ledger.to_generic()).await?; + } + } + None => { + unimplemented!("Not implemented yet") } - } - None => { - unimplemented!("Not implemented yet") } } } @@ -171,8 +190,11 @@ impl ServerDataSync { Ok(()) } - async fn handle_device_log_request(&self, device_log_obj: &DeviceLogObject) -> anyhow::Result<()> { - self.persistent_obj + #[instrument(skip(self))] + async fn handle_device_log_request( + &self, server_device: DeviceData, device_log_obj: &DeviceLogObject + ) -> anyhow::Result<()> { + self.p_obj .repo .save(device_log_obj.clone().to_generic()) .await?; @@ -188,174 +210,18 @@ impl ServerDataSync { }; let vault_action = vault_action_event.value.clone(); - let vault_name = vault_action.vault_name(); - - match vault_action { - VaultAction::CreateVault(user) => { - // create vault if not exists - let vault_desc = VaultDescriptor::vault(vault_name.clone()); - let maybe_vault = self.find_vault(ObjectId::unit(vault_desc.clone())).await?; - - if let Some(_vault) = maybe_vault { - // vault already exists, and the event have been saved into vault_log alredy, - // no action needed - return Ok(()); - }; - - //create vault_log, vault and vault status - self.create_vault(user.clone()).await - } - - VaultAction::JoinClusterRequest { .. } => { - let vault_log_desc = VaultDescriptor::VaultLog(vault_name).to_obj_desc(); - - let vault_log_free_id = self - .persistent_obj - .find_free_id_by_obj_desc(vault_log_desc.clone()) - .await?; - - if let ObjectId::Artifact(vault_log_artifact_id) = vault_log_free_id { - let vault_log_action_event = GenericKvLogEvent::VaultLog(VaultLogObject::Action(KvLogEvent { - key: KvKey::artifact(vault_log_desc, vault_log_artifact_id), - value: vault_action.clone(), - })); - - self.persistent_obj.repo.save(vault_log_action_event).await?; - Ok(()) - } else { - bail!( - "Invalid vault log id, must be ArtifactId, but it's: {:?}", - vault_log_free_id - ); - } - } - - VaultAction::UpdateMembership { - sender: UserDataMember(sender_user), - update, - } => { - //check if a sender is a member of the vault and update the vault then - let vault_log_desc = VaultDescriptor::VaultLog(vault_name).to_obj_desc(); - let vault_log_free_id = self - .persistent_obj - .find_free_id_by_obj_desc(vault_log_desc.clone()) - .await?; - - let ObjectId::Artifact(_) = vault_log_free_id else { - bail!( - "Invalid vault log id, must be ArtifactId, but it's: {:?}", - vault_log_free_id - ); - }; - - let vault_name = sender_user.vault_name.clone(); - let (vault_artifact_id, vault) = self.get_vault(vault_name.clone(), &sender_user.device).await?; - - let vault_event = { - let mut new_vault = vault.clone(); - new_vault.update_membership(update.clone()); - - VaultObject::Vault(KvLogEvent { - key: KvKey { - obj_id: vault_artifact_id, - obj_desc: VaultDescriptor::vault(vault_name.clone()), - }, - value: new_vault, - }) - .to_generic() - }; - - self.persistent_obj.repo.save(vault_event).await?; - - // Don't forget to update the vault status - - let vault_status_desc = { - let user_id = UserId { - device_id: update.device_id(), - vault_name, - }; - VaultDescriptor::VaultMembership(user_id).to_obj_desc() - }; - - let vault_status_free_id = self - .persistent_obj - .find_free_id_by_obj_desc(vault_status_desc.clone()) - .await?; - - let ObjectId::Artifact(vault_status_artifact_id) = vault_status_free_id else { - return Ok(()); - }; - - let vault_status_event = { - let event = KvLogEvent { - key: KvKey::artifact(vault_status_desc, vault_status_artifact_id), - value: update.clone(), - }; - VaultMembershipObject::Membership(event).to_generic() - }; - - self.persistent_obj.repo.save(vault_status_event).await?; - Ok(()) - } - VaultAction::AddMetaPassword { sender, meta_pass_id } => { - let user = sender.user(); - let vault_name = user.vault_name.clone(); - let (vault_artifact_id, vault) = self.get_vault(vault_name.clone(), &user.device).await?; - - let vault_event = { - let mut new_vault = vault.clone(); - new_vault.add_secret(meta_pass_id.clone()); - - let event = KvLogEvent { - key: KvKey { - obj_id: vault_artifact_id, - obj_desc: VaultDescriptor::vault(vault_name.clone()), - }, - value: new_vault, - }; - - VaultObject::Vault(event).to_generic() - }; - - self.persistent_obj.repo.save(vault_event).await?; - - Ok(()) - } - } - } - - async fn get_vault( - &self, - vault_name: VaultName, - sender_device: &DeviceData, - ) -> anyhow::Result<(ArtifactId, VaultData)> { - let vault_desc = VaultDescriptor::vault(vault_name.clone()); - let maybe_vault = self.find_vault(ObjectId::unit(vault_desc.clone())).await?; - let Some(vault) = maybe_vault else { - return Err(anyhow!("Vault not found")); + let action = VaultAction { + p_obj: self.p_obj.clone(), + server_device }; - - if !vault.is_member(&sender_device.id) { - return Err(anyhow!("Sender is not a member of the vault")); - } - - //save new vault state - let vault_free_id = self.persistent_obj.find_free_id_by_obj_desc(vault_desc.clone()).await?; - - let ObjectId::Artifact(vault_artifact_id) = vault_free_id else { - return Err(anyhow!( - "Invalid vault id, must be ArtifactId, but it's: {:?}", - vault_free_id - )); - }; - - Ok((vault_artifact_id, vault)) + action.do_processing(vault_action).await?; + Ok(()) } #[instrument(skip_all)] async fn global_index_replication(&self, gi_id: ObjectId) -> anyhow::Result> { - let events = self.persistent_obj.find_object_events(gi_id).await?; + let events = self.p_obj.find_object_events(gi_id).await?; Ok(events) } @@ -365,7 +231,7 @@ impl ServerDataSync { //sync VaultLog { let vault_log_events = self - .persistent_obj + .p_obj .find_object_events(request.vault_log.clone()) .await?; commit_log.extend(vault_log_events); @@ -373,15 +239,14 @@ impl ServerDataSync { //sync Vault { - let vault_events = self.persistent_obj.find_object_events(request.vault.clone()).await?; - + let vault_events = self.p_obj.find_object_events(request.vault.clone()).await?; commit_log.extend(vault_events); } //sync vault status { let vault_status_events = self - .persistent_obj + .p_obj .find_object_events(request.vault_status.clone()) .await?; @@ -390,170 +255,4 @@ impl ServerDataSync { Ok(commit_log) } - - async fn find_vault(&self, vault_id: ObjectId) -> anyhow::Result> { - let maybe_vault_event = self.persistent_obj.find_tail_event_by_obj_id(vault_id).await?; - - let Some(vault_event) = maybe_vault_event else { - return Ok(None); - }; - - let GenericKvLogEvent::Vault(VaultObject::Vault(event)) = vault_event else { - return Ok(None); - }; - - let vault = event.value; - Ok(Some(vault)) - } -} - -impl ServerDataSync { - async fn create_vault(&self, candidate: UserData) -> anyhow::Result<()> { - //vault not found, we can create our new vault - info!("Accept SignUp request, for the vault: {:?}", candidate.vault_name()); - - let server = self.device_creds.device.clone(); - - let sign_up_action = SignUpAction {}; - let sign_up_events = sign_up_action.accept(candidate.clone(), server.clone()); - - for sign_up_event in sign_up_events { - self.persistent_obj.repo.save(sign_up_event).await?; - } - - self.update_global_index(candidate.vault_name()).await?; - - Ok(()) - } - - async fn update_global_index(&self, vault_name: VaultName) -> anyhow::Result<()> { - //find the latest global_index_id??? - let gi_free_id = self - .persistent_obj - .find_free_id_by_obj_desc(ObjectDescriptor::GlobalIndex(GlobalIndexDescriptor::Index)) - .await?; - - let ObjectId::Artifact(gi_artifact_id) = gi_free_id else { - return Err(anyhow!("Invalid global index state")); - }; - - let vault_id = UnitId::vault_unit(vault_name.clone()); - - let gi_update_event = { - GlobalIndexObject::Update(KvLogEvent { - key: KvKey { - obj_id: gi_artifact_id, - obj_desc: GlobalIndexDescriptor::Index.to_obj_desc(), - }, - value: vault_id.clone(), - }) - .to_generic() - }; - - let gi_events = vec![gi_update_event]; - - for gi_event in gi_events { - self.persistent_obj.repo.save(gi_event).await?; - } - - let vault_idx_evt = GlobalIndexObject::index_from_vault_id(vault_id).to_generic(); - - self.persistent_obj.repo.save(vault_idx_evt).await?; - - Ok(()) - } -} - -#[cfg(test)] -mod test { - - use std::sync::Arc; - - use anyhow::Result; - use tracing::Level; - - use crate::{ - meta_tests::{ - action::{ - global_index_action::GlobalIndexSyncRequestTestAction, sign_up_claim_action::SignUpClaimTestAction, - }, - spec::{sign_up_claim_spec::SignUpClaimSpec, test_spec::TestSpec}, - }, - node::{ - common::model::vault::VaultStatus, - db::{ - descriptors::{ - object_descriptor::ToObjectDescriptor, shared_secret_descriptor::SharedSecretDescriptor, - vault_descriptor::VaultDescriptor, - }, - in_mem_db::InMemKvLogEventRepo, - objects::persistent_object::PersistentObject, - }, - }, - }; - - // temporary disabled - //#[tokio::test] - async fn test_sign_up() -> Result<()> { - let subscriber = tracing_subscriber::fmt().with_max_level(Level::DEBUG).finish(); - tracing::subscriber::set_global_default(subscriber)?; - - let gi_action = GlobalIndexSyncRequestTestAction::init().await?; - - let client_p_obj = { - let client_repo = Arc::new(InMemKvLogEventRepo::default()); - Arc::new(PersistentObject::new(client_repo.clone())) - }; - - let claim_action = SignUpClaimTestAction::new(client_p_obj.clone()); - let vault_status = claim_action.sign_up().await?; - let VaultStatus::Outsider(outsider) = vault_status else { - panic!("Invalid state, the user can't be already registerred"); - }; - - let server_data_sync = gi_action.server_node.app.data_sync; - - let client_device_log_events = client_p_obj - .get_object_events_from_beginning(VaultDescriptor::device_log(outsider.user_data.user_id())) - .await?; - - for cd_log_event in client_device_log_events { - server_data_sync.server_processing(cd_log_event).await?; - } - - let client_ss_device_log_events = { - let ss_desc = SharedSecretDescriptor::SSDeviceLog(outsider.user_data.device.id.clone()).to_obj_desc(); - client_p_obj.get_object_events_from_beginning(ss_desc).await? - }; - println!("CLIENT SS device log EVENTS: {:?}", client_ss_device_log_events.len()); - for cd_ss_log_event in client_ss_device_log_events { - server_data_sync.server_processing(cd_ss_log_event).await?; - } - - let server_ss_device_log_events = { - let ss_desc = SharedSecretDescriptor::SSDeviceLog(outsider.user_data.device.id.clone()).to_obj_desc(); - gi_action - .server_node - .p_obj - .get_object_events_from_beginning(ss_desc) - .await? - }; - println!("SERVER SS device log EVENTS: {:?}", server_ss_device_log_events.len()); - - let db = gi_action.server_node.p_obj.repo.get_db().await; - assert_eq!(db.len(), 16); - - db.values().for_each(|event| { - println!("Event: {}\n", serde_json::to_string_pretty(event).unwrap()); - }); - - let server_claim_spec = SignUpClaimSpec { - p_obj: gi_action.server_node.p_obj, - user: outsider.user_data, - }; - - server_claim_spec.verify().await?; - - Ok(()) - } } diff --git a/core/src/secret/mod.rs b/core/src/secret/mod.rs index 6af96854..26ee3b20 100644 --- a/core/src/secret/mod.rs +++ b/core/src/secret/mod.rs @@ -2,11 +2,10 @@ use std::sync::Arc; use crate::crypto::keys::KeyManager; use crate::node::common::model::crypto::EncryptedMessage; -use crate::node::common::model::device::{DeviceLink, DeviceLinkBuilder}; use crate::node::common::model::secret::{ MetaPasswordId, SSDistributionClaimId, SSDistributionId, SecretDistributionData, SecretDistributionType, }; -use crate::node::common::model::user::UserCredentials; +use crate::node::common::model::user::user_creds::UserCredentials; use crate::node::common::model::vault::VaultData; use crate::node::db::descriptors::object_descriptor::{ObjectDescriptor, ToObjectDescriptor}; use crate::node::db::descriptors::shared_secret_descriptor::SharedSecretDescriptor; @@ -17,6 +16,7 @@ use crate::node::db::objects::persistent_object::PersistentObject; use crate::node::db::repo::generic_db::KvLogEventRepo; use crate::CoreResult; use crate::{PlainText, SharedSecretConfig, SharedSecretEncryption, UserShareDto}; +use crate::node::common::model::device::device_link::{DeviceLink, DeviceLinkBuilder}; pub mod data_block; pub mod shared_secret; @@ -63,8 +63,8 @@ impl MetaEncryptor { let encrypted_share = key_manager.transport.encrypt_string(share_str, receiver_pk)?; let device_link = DeviceLinkBuilder::builder() - .sender(self.user.device_creds.device.id.clone()) - .receiver(receiver.clone().user().device.id.clone()) + .sender(self.user.device_creds.device.device_id.clone()) + .receiver(receiver.clone().user().device.device_id.clone()) .build()?; let cipher_share = EncryptedMessage::CipherShare { diff --git a/core/tests/server_test.rs b/core/tests/server_test.rs deleted file mode 100644 index 05fee74e..00000000 --- a/core/tests/server_test.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[cfg(test)] -mod test { - use std::sync::Arc; - - use meta_secret_core::{ - meta_tests::{ - action::sign_up_claim_action::SignUpClaimTestAction, - spec::{sign_up_claim_spec::SignUpClaimSpec, test_spec::TestSpec}, - }, - node::{ - common::model::vault::VaultStatus, - db::{in_mem_db::InMemKvLogEventRepo, objects::persistent_object::PersistentObject}, - }, - }; - - #[tokio::test] - pub async fn test_server_app_initial_state() -> anyhow::Result<()> { - let client_p_obj = { - let client_repo = Arc::new(InMemKvLogEventRepo::default()); - Arc::new(PersistentObject::new(client_repo.clone())) - }; - - let sign_up_claim_action = SignUpClaimTestAction::new(client_p_obj.clone()); - let vault_status = sign_up_claim_action.sign_up().await?; - - let VaultStatus::Outsider(outsider) = vault_status else { - panic!("Invalid state"); - }; - - let claim_spec = SignUpClaimSpec { - p_obj: client_p_obj, - user: outsider.user_data, - }; - - claim_spec.verify().await?; - - Ok(()) - } -} diff --git a/recipe.json b/recipe.json index 366b159f..17b1d142 100644 --- a/recipe.json +++ b/recipe.json @@ -1 +1 @@ -{"skeleton":{"manifests":[{"relative_path":"Cargo.toml","contents":"[workspace]\nmembers = [\"core\", \"meta-server-emulator\", \"core-swift-lib\", \"wasm\", \"cli\"]\nexclude = [\"meta-server-serverless\"]\nresolver = \"2\"\n\n[workspace.dependencies]\nanyhow = \"1.0.75\"\nasync-mutex = \"1.4\"\nasync-trait = \"0.1\"\nbase64 = \"0.20.0\"\ned25519-dalek = \"1.0.1\"\nflume = \"0.11\"\nhex = \"0.4\"\nrand = \"0.8.5\"\nserde_derive = \"1.0.188\"\nserde_json = \"1.0.107\"\nshamirsecretsharing = \"0.1\"\nthiserror = \"1.0.49\"\ntracing = \"0.1\"\n\n[workspace.dependencies.async-std]\nversion = \"1.12.0\"\n\n[workspace.dependencies.crypto_box]\nversion = \"0.8.2\"\nfeatures = [\"std\"]\n\n[workspace.dependencies.diesel]\nversion = \"2.0.0\"\n\n[workspace.dependencies.diesel_migrations]\nversion = \"2.0.0\"\n\n[workspace.dependencies.getrandom]\nversion = \"0.2.8\"\nfeatures = [\"js\"]\n\n[workspace.dependencies.serde]\nversion = \"1.0.188\"\nfeatures = [\"derive\"]\n\n[workspace.dependencies.sha2]\nversion = \"0.10.6\"\nfeatures = [\"oid\"]\n\n[workspace.dependencies.tracing-subscriber]\nversion = \"0.3\"\n","targets":[]},{"relative_path":"cli/Cargo.toml","contents":"bench = []\ntest = []\nexample = []\n\n[[bin]]\npath = \"src/main.rs\"\nname = \"meta-secret-cli\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-cli\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies]\nanyhow = \"1.0\"\nmeta-secret-core = \"1.2.6\"\nserde = \"1.0\"\nserde_json = \"1.0\"\nserde_yaml = \"0.9\"\nthiserror = \"1.0.33\"\n\n[dependencies.clap]\nversion = \"3.2\"\nfeatures = [\"derive\"]\n","targets":[{"path":"src/main.rs","kind":"Bin","name":"meta-secret-cli"}]},{"relative_path":"core/Cargo.toml","contents":"bin = []\nbench = []\nexample = []\n\n[[test]]\npath = \"tests/mod.rs\"\nname = \"mod\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\n\n[[test]]\npath = \"tests/server_test.rs\"\nname = \"server_test\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-core\"\nedition = \"2021\"\nversion = \"0.0.1\"\ndescription = \"Meta Secret Core Module\"\nhomepage = \"https://github.com/meta-secret/meta-secret-core\"\ndocumentation = \"https://github.com/meta-secret/meta-secret-core/blob/main/core/README.md\"\nreadme = \"README.md\"\nlicense = \"Apache-2.0\"\nrepository = \"https://github.com/meta-secret/meta-secret-core\"\n\n[dependencies]\nimage = \"0.24\"\nqrcode-generator = \"4.1.6\"\nrqrr = \"0.5\"\nserde-big-array = \"0.4\"\nserde_bytes = \"0.11\"\ntracing-attributes = \"0.1.27\"\nwasm-bindgen = \"0.2.93\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-mutex]\nworkspace = true\n\n[dependencies.async-std]\nworkspace = true\nfeatures = [\"unstable\"]\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.base64]\nworkspace = true\n\n[dependencies.crypto_box]\nworkspace = true\n\n[dependencies.ed25519-dalek]\nworkspace = true\n\n[dependencies.flume]\nworkspace = true\n\n[dependencies.getrandom]\nworkspace = true\n\n[dependencies.hex]\nworkspace = true\n\n[dependencies.rand]\nworkspace = true\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.sha2]\nworkspace = true\n\n[dependencies.shamirsecretsharing]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[dependencies.tracing]\nworkspace = true\n\n[dependencies.tracing-subscriber]\nworkspace = true\nfeatures = [\"json\", \"env-filter\"]\n\n[dependencies.uuid]\nversion = \"1.3.0\"\nfeatures = [\"v4\", \"fast-rng\", \"macro-diagnostics\"]\n\n[dev-dependencies]\npretty_assertions = \"1\"\n\n[dev-dependencies.tokio]\nversion = \"1.20.1\"\nfeatures = [\"full\"]\n\n[features]\ntest_utils = []\n\n[lib]\nname = \"meta_secret_core\"\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"lib\", \"staticlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_core"},{"path":"tests/mod.rs","kind":"Test","name":"mod"},{"path":"tests/server_test.rs","kind":"Test","name":"server_test"}]},{"relative_path":"core-swift-lib/Cargo.toml","contents":"bin = []\nbench = []\ntest = []\nexample = []\n\n[package]\nname = \"meta-secret-core-swift\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.base64]\nworkspace = true\n\n[dependencies.crypto_box]\nworkspace = true\n\n[dependencies.ed25519-dalek]\nworkspace = true\n\n[dependencies.hex]\nworkspace = true\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.sha2]\nworkspace = true\n\n[lib]\nname = \"meta_secret_core_swift\"\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"lib\", \"staticlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_core_swift"}]},{"relative_path":"meta-server-emulator/Cargo.toml","contents":"bin = []\nbench = []\ntest = []\nexample = []\n\n[package]\nname = \"meta-server-emulator\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.diesel]\nworkspace = true\nfeatures = [\"sqlite\"]\n\n[dependencies.diesel_migrations]\nworkspace = true\nfeatures = [\"sqlite\"]\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[lib]\npath = \"src/lib.rs\"\nname = \"meta_server_emulator\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\ncrate-type = [\"rlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_server_emulator"}]},{"relative_path":"wasm/Cargo.toml","contents":"bin = []\nbench = []\nexample = []\n\n[[test]]\npath = \"tests/web.rs\"\nname = \"web\"\nplugin = false\nproc-macro = false\nedition = \"2018\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-wasm\"\nedition = \"2018\"\nversion = \"0.0.1\"\nauthors = [\"cypherkitty \"]\n\n[dependencies]\nrexie = \"0.6.2\"\nserde-wasm-bindgen = \"0.6.0\"\ntracing-web = \"0.1.2\"\nwasm-bindgen-futures = \"0.4.37\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-mutex]\nworkspace = true\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.console_error_panic_hook]\nversion = \"0.1.7\"\noptional = true\n\n[dependencies.flume]\nworkspace = true\n\n[dependencies.getrandom]\nworkspace = true\n\n[dependencies.js-sys]\nversion = \"0.3.63\"\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[dependencies.tracing]\nworkspace = true\n\n[dependencies.tracing-subscriber]\nworkspace = true\nfeatures = [\"fmt\", \"time\"]\n\n[dependencies.wasm-bindgen]\nversion = \"0.2.87\"\nfeatures = [\"serde-serialize\"]\n\n[dependencies.web-sys]\nversion = \"0.3.60\"\nfeatures = [\"DomException\", \"DomStringList\", \"Event\", \"StorageType\", \"Window\"]\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.37\"\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".dependencies.meta-secret-core]\npath = \"../core\"\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".dev-dependencies]\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".build-dependencies]\n\n[features]\ndefault = [\"console_error_panic_hook\"]\n\n[lib]\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"rlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_wasm"},{"path":"tests/web.rs","kind":"Test","name":"web"}]}],"config_file":null,"lock_file":"version = 3\n\n[[package]]\nname = \"addr2line\"\nversion = \"0.22.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678\"\ndependencies = [\"gimli\"]\n\n[[package]]\nname = \"adler\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe\"\n\n[[package]]\nname = \"adler2\"\nversion = \"2.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627\"\n\n[[package]]\nname = \"aead\"\nversion = \"0.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"aead\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0\"\ndependencies = [\"crypto-common\", \"generic-array\"]\n\n[[package]]\nname = \"ahash\"\nversion = \"0.7.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9\"\ndependencies = [\"getrandom 0.2.15\", \"once_cell\", \"version_check\"]\n\n[[package]]\nname = \"aho-corasick\"\nversion = \"1.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"anyhow\"\nversion = \"1.0.86\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da\"\n\n[[package]]\nname = \"async-channel\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35\"\ndependencies = [\"concurrent-queue\", \"event-listener 2.5.3\", \"futures-core\"]\n\n[[package]]\nname = \"async-channel\"\nversion = \"2.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a\"\ndependencies = [\"concurrent-queue\", \"event-listener-strategy\", \"futures-core\", \"pin-project-lite\"]\n\n[[package]]\nname = \"async-executor\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7\"\ndependencies = [\"async-task\", \"concurrent-queue\", \"fastrand 2.1.1\", \"futures-lite 2.3.0\", \"slab\"]\n\n[[package]]\nname = \"async-global-executor\"\nversion = \"2.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c\"\ndependencies = [\"async-channel 2.3.1\", \"async-executor\", \"async-io 2.3.4\", \"async-lock 3.4.0\", \"blocking\", \"futures-lite 2.3.0\", \"once_cell\"]\n\n[[package]]\nname = \"async-io\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af\"\ndependencies = [\"async-lock 2.8.0\", \"autocfg\", \"cfg-if\", \"concurrent-queue\", \"futures-lite 1.13.0\", \"log\", \"parking\", \"polling 2.8.0\", \"rustix 0.37.27\", \"slab\", \"socket2 0.4.10\", \"waker-fn\"]\n\n[[package]]\nname = \"async-io\"\nversion = \"2.3.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8\"\ndependencies = [\"async-lock 3.4.0\", \"cfg-if\", \"concurrent-queue\", \"futures-io\", \"futures-lite 2.3.0\", \"parking\", \"polling 3.7.3\", \"rustix 0.38.35\", \"slab\", \"tracing\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"async-lock\"\nversion = \"2.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b\"\ndependencies = [\"event-listener 2.5.3\"]\n\n[[package]]\nname = \"async-lock\"\nversion = \"3.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18\"\ndependencies = [\"event-listener 5.3.1\", \"event-listener-strategy\", \"pin-project-lite\"]\n\n[[package]]\nname = \"async-mutex\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e\"\ndependencies = [\"event-listener 2.5.3\"]\n\n[[package]]\nname = \"async-process\"\nversion = \"1.8.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88\"\ndependencies = [\"async-io 1.13.0\", \"async-lock 2.8.0\", \"async-signal\", \"blocking\", \"cfg-if\", \"event-listener 3.1.0\", \"futures-lite 1.13.0\", \"rustix 0.38.35\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"async-signal\"\nversion = \"0.2.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3\"\ndependencies = [\"async-io 2.3.4\", \"async-lock 3.4.0\", \"atomic-waker\", \"cfg-if\", \"futures-core\", \"futures-io\", \"rustix 0.38.35\", \"signal-hook-registry\", \"slab\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"async-std\"\nversion = \"1.12.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d\"\ndependencies = [\"async-channel 1.9.0\", \"async-global-executor\", \"async-io 1.13.0\", \"async-lock 2.8.0\", \"async-process\", \"crossbeam-utils\", \"futures-channel\", \"futures-core\", \"futures-io\", \"futures-lite 1.13.0\", \"gloo-timers\", \"kv-log-macro\", \"log\", \"memchr\", \"once_cell\", \"pin-project-lite\", \"pin-utils\", \"slab\", \"wasm-bindgen-futures\"]\n\n[[package]]\nname = \"async-task\"\nversion = \"4.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de\"\n\n[[package]]\nname = \"async-trait\"\nversion = \"0.1.82\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"atomic-waker\"\nversion = \"1.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0\"\n\n[[package]]\nname = \"atty\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8\"\ndependencies = [\"hermit-abi 0.1.19\", \"libc\", \"winapi\"]\n\n[[package]]\nname = \"autocfg\"\nversion = \"1.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0\"\n\n[[package]]\nname = \"backtrace\"\nversion = \"0.3.73\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a\"\ndependencies = [\"addr2line\", \"cc\", \"cfg-if\", \"libc\", \"miniz_oxide 0.7.4\", \"object\", \"rustc-demangle\"]\n\n[[package]]\nname = \"base64\"\nversion = \"0.20.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5\"\n\n[[package]]\nname = \"base64\"\nversion = \"0.21.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567\"\n\n[[package]]\nname = \"bit_field\"\nversion = \"0.10.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"1.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"2.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de\"\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.10.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"blocking\"\nversion = \"1.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea\"\ndependencies = [\"async-channel 2.3.1\", \"async-task\", \"futures-io\", \"futures-lite 2.3.0\", \"piper\"]\n\n[[package]]\nname = \"bumpalo\"\nversion = \"3.16.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c\"\n\n[[package]]\nname = \"bytemuck\"\nversion = \"1.17.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2\"\n\n[[package]]\nname = \"byteorder\"\nversion = \"1.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b\"\n\n[[package]]\nname = \"bytes\"\nversion = \"1.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50\"\n\n[[package]]\nname = \"cc\"\nversion = \"1.1.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\ndependencies = [\"shlex\"]\n\n[[package]]\nname = \"cfg-if\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd\"\n\n[[package]]\nname = \"chacha20\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818\"\ndependencies = [\"cfg-if\", \"cipher 0.4.4\", \"cpufeatures\"]\n\n[[package]]\nname = \"chacha20poly1305\"\nversion = \"0.10.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35\"\ndependencies = [\"aead 0.5.2\", \"chacha20\", \"cipher 0.4.4\", \"poly1305 0.8.0\", \"zeroize\"]\n\n[[package]]\nname = \"cipher\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"cipher\"\nversion = \"0.4.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad\"\ndependencies = [\"crypto-common\", \"inout\", \"zeroize\"]\n\n[[package]]\nname = \"clap\"\nversion = \"3.2.25\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123\"\ndependencies = [\"atty\", \"bitflags 1.3.2\", \"clap_derive\", \"clap_lex\", \"indexmap 1.9.3\", \"once_cell\", \"strsim 0.10.0\", \"termcolor\", \"textwrap\"]\n\n[[package]]\nname = \"clap_derive\"\nversion = \"3.2.25\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008\"\ndependencies = [\"heck 0.4.1\", \"proc-macro-error\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 1.0.109\"]\n\n[[package]]\nname = \"clap_lex\"\nversion = \"0.2.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5\"\ndependencies = [\"os_str_bytes\"]\n\n[[package]]\nname = \"color_quant\"\nversion = \"1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b\"\n\n[[package]]\nname = \"concurrent-queue\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973\"\ndependencies = [\"crossbeam-utils\"]\n\n[[package]]\nname = \"console_error_panic_hook\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc\"\ndependencies = [\"cfg-if\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"const-oid\"\nversion = \"0.9.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8\"\n\n[[package]]\nname = \"core-foundation\"\nversion = \"0.9.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"core-foundation-sys\"\nversion = \"0.8.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b\"\n\n[[package]]\nname = \"cpufeatures\"\nversion = \"0.2.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"cpuid-bool\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba\"\n\n[[package]]\nname = \"crc32fast\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3\"\ndependencies = [\"cfg-if\"]\n\n[[package]]\nname = \"crossbeam-deque\"\nversion = \"0.8.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d\"\ndependencies = [\"crossbeam-epoch\", \"crossbeam-utils\"]\n\n[[package]]\nname = \"crossbeam-epoch\"\nversion = \"0.9.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e\"\ndependencies = [\"crossbeam-utils\"]\n\n[[package]]\nname = \"crossbeam-utils\"\nversion = \"0.8.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80\"\n\n[[package]]\nname = \"crunchy\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7\"\n\n[[package]]\nname = \"crypto-common\"\nversion = \"0.1.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3\"\ndependencies = [\"generic-array\", \"rand_core 0.6.4\", \"typenum\"]\n\n[[package]]\nname = \"crypto_box\"\nversion = \"0.8.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fd26c32de5307fd08aac445a75c43472b14559d5dccdfba8022dbcd075838ebc\"\ndependencies = [\"aead 0.5.2\", \"chacha20\", \"chacha20poly1305\", \"salsa20 0.10.2\", \"x25519-dalek\", \"xsalsa20poly1305 0.9.1\", \"zeroize\"]\n\n[[package]]\nname = \"curve25519-dalek\"\nversion = \"3.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61\"\ndependencies = [\"byteorder\", \"digest 0.9.0\", \"rand_core 0.5.1\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"darling\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989\"\ndependencies = [\"darling_core\", \"darling_macro\"]\n\n[[package]]\nname = \"darling_core\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5\"\ndependencies = [\"fnv\", \"ident_case\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"strsim 0.11.1\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"darling_macro\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806\"\ndependencies = [\"darling_core\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"deranged\"\nversion = \"0.3.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4\"\ndependencies = [\"powerfmt\"]\n\n[[package]]\nname = \"diesel\"\nversion = \"2.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71\"\ndependencies = [\"diesel_derives\", \"libsqlite3-sys\", \"time\"]\n\n[[package]]\nname = \"diesel_derives\"\nversion = \"2.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4\"\ndependencies = [\"diesel_table_macro_syntax\", \"dsl_auto_type\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"diesel_migrations\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6\"\ndependencies = [\"diesel\", \"migrations_internals\", \"migrations_macros\"]\n\n[[package]]\nname = \"diesel_table_macro_syntax\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25\"\ndependencies = [\"syn 2.0.77\"]\n\n[[package]]\nname = \"diff\"\nversion = \"0.1.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8\"\n\n[[package]]\nname = \"digest\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"digest\"\nversion = \"0.10.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292\"\ndependencies = [\"block-buffer 0.10.4\", \"const-oid\", \"crypto-common\"]\n\n[[package]]\nname = \"dsl_auto_type\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607\"\ndependencies = [\"darling\", \"either\", \"heck 0.5.0\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"ed25519\"\nversion = \"1.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7\"\ndependencies = [\"signature\"]\n\n[[package]]\nname = \"ed25519-dalek\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d\"\ndependencies = [\"curve25519-dalek\", \"ed25519\", \"rand 0.7.3\", \"serde\", \"sha2 0.9.9\", \"zeroize\"]\n\n[[package]]\nname = \"either\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0\"\n\n[[package]]\nname = \"encoding_rs\"\nversion = \"0.8.34\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59\"\ndependencies = [\"cfg-if\"]\n\n[[package]]\nname = \"equivalent\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5\"\n\n[[package]]\nname = \"errno\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba\"\ndependencies = [\"libc\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"event-listener\"\nversion = \"2.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0\"\n\n[[package]]\nname = \"event-listener\"\nversion = \"3.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2\"\ndependencies = [\"concurrent-queue\", \"parking\", \"pin-project-lite\"]\n\n[[package]]\nname = \"event-listener\"\nversion = \"5.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba\"\ndependencies = [\"concurrent-queue\", \"parking\", \"pin-project-lite\"]\n\n[[package]]\nname = \"event-listener-strategy\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1\"\ndependencies = [\"event-listener 5.3.1\", \"pin-project-lite\"]\n\n[[package]]\nname = \"exr\"\nversion = \"1.72.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4\"\ndependencies = [\"bit_field\", \"flume\", \"half\", \"lebe\", \"miniz_oxide 0.7.4\", \"rayon-core\", \"smallvec\", \"zune-inflate\"]\n\n[[package]]\nname = \"fastrand\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be\"\ndependencies = [\"instant\"]\n\n[[package]]\nname = \"fastrand\"\nversion = \"2.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6\"\n\n[[package]]\nname = \"fdeflate\"\nversion = \"0.3.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645\"\ndependencies = [\"simd-adler32\"]\n\n[[package]]\nname = \"flate2\"\nversion = \"1.0.33\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253\"\ndependencies = [\"crc32fast\", \"miniz_oxide 0.8.0\"]\n\n[[package]]\nname = \"flume\"\nversion = \"0.11.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181\"\ndependencies = [\"futures-core\", \"futures-sink\", \"nanorand\", \"spin\"]\n\n[[package]]\nname = \"fnv\"\nversion = \"1.0.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1\"\n\n[[package]]\nname = \"foreign-types\"\nversion = \"0.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1\"\ndependencies = [\"foreign-types-shared\"]\n\n[[package]]\nname = \"foreign-types-shared\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b\"\n\n[[package]]\nname = \"form_urlencoded\"\nversion = \"1.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456\"\ndependencies = [\"percent-encoding\"]\n\n[[package]]\nname = \"futures-channel\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78\"\ndependencies = [\"futures-core\"]\n\n[[package]]\nname = \"futures-core\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d\"\n\n[[package]]\nname = \"futures-io\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1\"\n\n[[package]]\nname = \"futures-lite\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce\"\ndependencies = [\"fastrand 1.9.0\", \"futures-core\", \"futures-io\", \"memchr\", \"parking\", \"pin-project-lite\", \"waker-fn\"]\n\n[[package]]\nname = \"futures-lite\"\nversion = \"2.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5\"\ndependencies = [\"fastrand 2.1.1\", \"futures-core\", \"futures-io\", \"parking\", \"pin-project-lite\"]\n\n[[package]]\nname = \"futures-sink\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5\"\n\n[[package]]\nname = \"futures-task\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004\"\n\n[[package]]\nname = \"futures-util\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48\"\ndependencies = [\"futures-core\", \"futures-task\", \"pin-project-lite\", \"pin-utils\"]\n\n[[package]]\nname = \"g2gen\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2fc100b16c63808c5c388cd23ff94c5a35cf28ea459f336323f7948a39480555\"\ndependencies = [\"g2poly\", \"proc-macro2 0.4.30\", \"quote 0.6.13\", \"syn 0.15.44\"]\n\n[[package]]\nname = \"g2p\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bf09bc632629cbe5420b330e45bcc8f80403e74ba1027d213258914fd5c62755\"\ndependencies = [\"g2gen\", \"g2poly\"]\n\n[[package]]\nname = \"g2poly\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e837767888fca507f07e89c90e0b350da7bbb89170f67a4655dc9bdc4cca457b\"\n\n[[package]]\nname = \"generic-array\"\nversion = \"0.14.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a\"\ndependencies = [\"typenum\", \"version_check\"]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.1.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce\"\ndependencies = [\"cfg-if\", \"libc\", \"wasi 0.9.0+wasi-snapshot-preview1\"]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.2.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7\"\ndependencies = [\"cfg-if\", \"js-sys\", \"libc\", \"wasi 0.11.0+wasi-snapshot-preview1\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"gif\"\nversion = \"0.13.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2\"\ndependencies = [\"color_quant\", \"weezl\"]\n\n[[package]]\nname = \"gimli\"\nversion = \"0.29.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd\"\n\n[[package]]\nname = \"gloo-timers\"\nversion = \"0.2.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c\"\ndependencies = [\"futures-channel\", \"futures-core\", \"js-sys\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"h2\"\nversion = \"0.3.26\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8\"\ndependencies = [\"bytes\", \"fnv\", \"futures-core\", \"futures-sink\", \"futures-util\", \"http\", \"indexmap 2.5.0\", \"slab\", \"tokio\", \"tokio-util\", \"tracing\"]\n\n[[package]]\nname = \"half\"\nversion = \"2.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888\"\ndependencies = [\"cfg-if\", \"crunchy\"]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.12.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888\"\ndependencies = [\"ahash\"]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.14.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1\"\n\n[[package]]\nname = \"heck\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8\"\n\n[[package]]\nname = \"heck\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\"\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.1.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024\"\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc\"\n\n[[package]]\nname = \"hex\"\nversion = \"0.4.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70\"\n\n[[package]]\nname = \"html-escape\"\nversion = \"0.2.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476\"\ndependencies = [\"utf8-width\"]\n\n[[package]]\nname = \"http\"\nversion = \"0.2.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1\"\ndependencies = [\"bytes\", \"fnv\", \"itoa\"]\n\n[[package]]\nname = \"http-body\"\nversion = \"0.4.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2\"\ndependencies = [\"bytes\", \"http\", \"pin-project-lite\"]\n\n[[package]]\nname = \"httparse\"\nversion = \"1.9.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9\"\n\n[[package]]\nname = \"httpdate\"\nversion = \"1.0.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9\"\n\n[[package]]\nname = \"hyper\"\nversion = \"0.14.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9\"\ndependencies = [\"bytes\", \"futures-channel\", \"futures-core\", \"futures-util\", \"h2\", \"http\", \"http-body\", \"httparse\", \"httpdate\", \"itoa\", \"pin-project-lite\", \"socket2 0.5.7\", \"tokio\", \"tower-service\", \"tracing\", \"want\"]\n\n[[package]]\nname = \"hyper-tls\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905\"\ndependencies = [\"bytes\", \"hyper\", \"native-tls\", \"tokio\", \"tokio-native-tls\"]\n\n[[package]]\nname = \"idb\"\nversion = \"0.6.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2764bba4203538c2ef2d733d5bad8fdb4622b1ebc5560831279db7b3be1332e8\"\ndependencies = [\"js-sys\", \"num-traits\", \"thiserror\", \"tokio\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"ident_case\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39\"\n\n[[package]]\nname = \"idna\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6\"\ndependencies = [\"unicode-bidi\", \"unicode-normalization\"]\n\n[[package]]\nname = \"image\"\nversion = \"0.24.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d\"\ndependencies = [\"bytemuck\", \"byteorder\", \"color_quant\", \"exr\", \"gif\", \"jpeg-decoder\", \"num-traits\", \"png\", \"qoi\", \"tiff\"]\n\n[[package]]\nname = \"indexmap\"\nversion = \"1.9.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99\"\ndependencies = [\"autocfg\", \"hashbrown 0.12.3\"]\n\n[[package]]\nname = \"indexmap\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5\"\ndependencies = [\"equivalent\", \"hashbrown 0.14.5\"]\n\n[[package]]\nname = \"inout\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"instant\"\nversion = \"0.1.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222\"\ndependencies = [\"cfg-if\"]\n\n[[package]]\nname = \"io-lifetimes\"\nversion = \"1.0.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2\"\ndependencies = [\"hermit-abi 0.3.9\", \"libc\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"ipnet\"\nversion = \"2.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3\"\n\n[[package]]\nname = \"itoa\"\nversion = \"1.0.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b\"\n\n[[package]]\nname = \"jpeg-decoder\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0\"\ndependencies = [\"rayon\"]\n\n[[package]]\nname = \"js-sys\"\nversion = \"0.3.70\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a\"\ndependencies = [\"wasm-bindgen\"]\n\n[[package]]\nname = \"kv-log-macro\"\nversion = \"1.0.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f\"\ndependencies = [\"log\"]\n\n[[package]]\nname = \"lazy_static\"\nversion = \"1.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe\"\n\n[[package]]\nname = \"lebe\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8\"\n\n[[package]]\nname = \"libc\"\nversion = \"0.2.158\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n\n[[package]]\nname = \"libsqlite3-sys\"\nversion = \"0.30.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149\"\ndependencies = [\"pkg-config\", \"vcpkg\"]\n\n[[package]]\nname = \"linux-raw-sys\"\nversion = \"0.3.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519\"\n\n[[package]]\nname = \"linux-raw-sys\"\nversion = \"0.4.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89\"\n\n[[package]]\nname = \"lock_api\"\nversion = \"0.4.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17\"\ndependencies = [\"autocfg\", \"scopeguard\"]\n\n[[package]]\nname = \"log\"\nversion = \"0.4.22\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24\"\ndependencies = [\"value-bag\"]\n\n[[package]]\nname = \"lru\"\nversion = \"0.7.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a\"\ndependencies = [\"hashbrown 0.12.3\"]\n\n[[package]]\nname = \"matchers\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558\"\ndependencies = [\"regex-automata 0.1.10\"]\n\n[[package]]\nname = \"memchr\"\nversion = \"2.7.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3\"\n\n[[package]]\nname = \"meta-secret-cli\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"clap\", \"meta-secret-core 1.10.8\", \"serde\", \"serde_json\", \"serde_yaml\", \"thiserror\"]\n\n[[package]]\nname = \"meta-secret-core\"\nversion = \"1.10.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"60891c16c01e54fb1885eb42056fb8c4f202593ec8f8dca0159775acab6860e5\"\ndependencies = [\"anyhow\", \"async-std\", \"async-trait\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"getrandom 0.2.15\", \"hex\", \"image\", \"qrcode-generator\", \"rand 0.8.5\", \"reqwest\", \"rqrr\", \"serde\", \"serde-big-array\", \"serde_bytes\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\", \"shamirsecretsharing\", \"thiserror\"]\n\n[[package]]\nname = \"meta-secret-core\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-mutex\", \"async-std\", \"async-trait\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"flume\", \"getrandom 0.2.15\", \"hex\", \"image\", \"pretty_assertions\", \"qrcode-generator\", \"rand 0.8.5\", \"rqrr\", \"serde\", \"serde-big-array\", \"serde_bytes\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\", \"shamirsecretsharing\", \"thiserror\", \"tokio\", \"tracing\", \"tracing-attributes\", \"tracing-subscriber\", \"uuid\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"meta-secret-core-swift\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"hex\", \"meta-secret-core 2.0.0\", \"serde\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\"]\n\n[[package]]\nname = \"meta-secret-wasm\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-mutex\", \"async-trait\", \"console_error_panic_hook\", \"flume\", \"getrandom 0.2.15\", \"js-sys\", \"meta-secret-core 2.0.0\", \"rexie\", \"serde\", \"serde-wasm-bindgen\", \"serde_derive\", \"serde_json\", \"thiserror\", \"tracing\", \"tracing-subscriber\", \"tracing-web\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"wasm-bindgen-test\", \"web-sys\"]\n\n[[package]]\nname = \"meta-server-emulator\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-trait\", \"diesel\", \"diesel_migrations\", \"meta-secret-core 2.0.0\", \"serde\", \"serde_derive\", \"serde_json\", \"thiserror\"]\n\n[[package]]\nname = \"migrations_internals\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff\"\ndependencies = [\"serde\", \"toml\"]\n\n[[package]]\nname = \"migrations_macros\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd\"\ndependencies = [\"migrations_internals\", \"proc-macro2 1.0.86\", \"quote 1.0.37\"]\n\n[[package]]\nname = \"mime\"\nversion = \"0.3.17\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a\"\n\n[[package]]\nname = \"minicov\"\nversion = \"0.3.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169\"\ndependencies = [\"cc\", \"walkdir\"]\n\n[[package]]\nname = \"miniz_oxide\"\nversion = \"0.7.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08\"\ndependencies = [\"adler\", \"simd-adler32\"]\n\n[[package]]\nname = \"miniz_oxide\"\nversion = \"0.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1\"\ndependencies = [\"adler2\"]\n\n[[package]]\nname = \"mio\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec\"\ndependencies = [\"hermit-abi 0.3.9\", \"libc\", \"wasi 0.11.0+wasi-snapshot-preview1\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"nanorand\"\nversion = \"0.7.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3\"\ndependencies = [\"getrandom 0.2.15\"]\n\n[[package]]\nname = \"native-tls\"\nversion = \"0.2.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466\"\ndependencies = [\"libc\", \"log\", \"openssl\", \"openssl-probe\", \"openssl-sys\", \"schannel\", \"security-framework\", \"security-framework-sys\", \"tempfile\"]\n\n[[package]]\nname = \"nu-ansi-term\"\nversion = \"0.46.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84\"\ndependencies = [\"overload\", \"winapi\"]\n\n[[package]]\nname = \"num-conv\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9\"\n\n[[package]]\nname = \"num-traits\"\nversion = \"0.2.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\"\ndependencies = [\"autocfg\"]\n\n[[package]]\nname = \"object\"\nversion = \"0.36.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"once_cell\"\nversion = \"1.19.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92\"\n\n[[package]]\nname = \"opaque-debug\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381\"\n\n[[package]]\nname = \"openssl\"\nversion = \"0.10.66\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1\"\ndependencies = [\"bitflags 2.6.0\", \"cfg-if\", \"foreign-types\", \"libc\", \"once_cell\", \"openssl-macros\", \"openssl-sys\"]\n\n[[package]]\nname = \"openssl-macros\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"openssl-probe\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf\"\n\n[[package]]\nname = \"openssl-sys\"\nversion = \"0.9.103\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6\"\ndependencies = [\"cc\", \"libc\", \"pkg-config\", \"vcpkg\"]\n\n[[package]]\nname = \"os_str_bytes\"\nversion = \"6.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1\"\n\n[[package]]\nname = \"overload\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39\"\n\n[[package]]\nname = \"parking\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae\"\n\n[[package]]\nname = \"parking_lot\"\nversion = \"0.12.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27\"\ndependencies = [\"lock_api\", \"parking_lot_core\"]\n\n[[package]]\nname = \"parking_lot_core\"\nversion = \"0.9.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8\"\ndependencies = [\"cfg-if\", \"libc\", \"redox_syscall\", \"smallvec\", \"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"percent-encoding\"\nversion = \"2.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e\"\n\n[[package]]\nname = \"pin-project-lite\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02\"\n\n[[package]]\nname = \"pin-utils\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184\"\n\n[[package]]\nname = \"piper\"\nversion = \"0.2.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066\"\ndependencies = [\"atomic-waker\", \"fastrand 2.1.1\", \"futures-io\"]\n\n[[package]]\nname = \"pkg-config\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n\n[[package]]\nname = \"png\"\nversion = \"0.17.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1\"\ndependencies = [\"bitflags 1.3.2\", \"crc32fast\", \"fdeflate\", \"flate2\", \"miniz_oxide 0.7.4\"]\n\n[[package]]\nname = \"polling\"\nversion = \"2.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce\"\ndependencies = [\"autocfg\", \"bitflags 1.3.2\", \"cfg-if\", \"concurrent-queue\", \"libc\", \"log\", \"pin-project-lite\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"polling\"\nversion = \"3.7.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511\"\ndependencies = [\"cfg-if\", \"concurrent-queue\", \"hermit-abi 0.4.0\", \"pin-project-lite\", \"rustix 0.38.35\", \"tracing\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"poly1305\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4b7456bc1ad2d4cf82b3a016be4c2ac48daf11bf990c1603ebd447fe6f30fca8\"\ndependencies = [\"cpuid-bool\", \"universal-hash 0.4.0\"]\n\n[[package]]\nname = \"poly1305\"\nversion = \"0.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf\"\ndependencies = [\"cpufeatures\", \"opaque-debug\", \"universal-hash 0.5.1\"]\n\n[[package]]\nname = \"powerfmt\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391\"\n\n[[package]]\nname = \"ppv-lite86\"\nversion = \"0.2.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04\"\ndependencies = [\"zerocopy\"]\n\n[[package]]\nname = \"pretty_assertions\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66\"\ndependencies = [\"diff\", \"yansi\"]\n\n[[package]]\nname = \"proc-macro-error\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c\"\ndependencies = [\"proc-macro-error-attr\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 1.0.109\", \"version_check\"]\n\n[[package]]\nname = \"proc-macro-error-attr\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"version_check\"]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"0.4.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759\"\ndependencies = [\"unicode-xid\"]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"1.0.86\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77\"\ndependencies = [\"unicode-ident\"]\n\n[[package]]\nname = \"qoi\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001\"\ndependencies = [\"bytemuck\"]\n\n[[package]]\nname = \"qrcode-generator\"\nversion = \"4.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1d06cb9646c7a14096231a2474d7f21e5e8c13de090c68d13bde6157cfe7f159\"\ndependencies = [\"html-escape\", \"image\", \"qrcodegen\"]\n\n[[package]]\nname = \"qrcodegen\"\nversion = \"1.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142\"\n\n[[package]]\nname = \"quote\"\nversion = \"0.6.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1\"\ndependencies = [\"proc-macro2 0.4.30\"]\n\n[[package]]\nname = \"quote\"\nversion = \"1.0.37\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af\"\ndependencies = [\"proc-macro2 1.0.86\"]\n\n[[package]]\nname = \"rand\"\nversion = \"0.7.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03\"\ndependencies = [\"getrandom 0.1.16\", \"libc\", \"rand_chacha 0.2.2\", \"rand_core 0.5.1\", \"rand_hc\"]\n\n[[package]]\nname = \"rand\"\nversion = \"0.8.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404\"\ndependencies = [\"libc\", \"rand_chacha 0.3.1\", \"rand_core 0.6.4\"]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402\"\ndependencies = [\"ppv-lite86\", \"rand_core 0.5.1\"]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88\"\ndependencies = [\"ppv-lite86\", \"rand_core 0.6.4\"]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19\"\ndependencies = [\"getrandom 0.1.16\"]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.6.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c\"\ndependencies = [\"getrandom 0.2.15\"]\n\n[[package]]\nname = \"rand_hc\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c\"\ndependencies = [\"rand_core 0.5.1\"]\n\n[[package]]\nname = \"rayon\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa\"\ndependencies = [\"either\", \"rayon-core\"]\n\n[[package]]\nname = \"rayon-core\"\nversion = \"1.12.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2\"\ndependencies = [\"crossbeam-deque\", \"crossbeam-utils\"]\n\n[[package]]\nname = \"redox_syscall\"\nversion = \"0.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4\"\ndependencies = [\"bitflags 2.6.0\"]\n\n[[package]]\nname = \"regex\"\nversion = \"1.10.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619\"\ndependencies = [\"aho-corasick\", \"memchr\", \"regex-automata 0.4.7\", \"regex-syntax 0.8.4\"]\n\n[[package]]\nname = \"regex-automata\"\nversion = \"0.1.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132\"\ndependencies = [\"regex-syntax 0.6.29\"]\n\n[[package]]\nname = \"regex-automata\"\nversion = \"0.4.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df\"\ndependencies = [\"aho-corasick\", \"memchr\", \"regex-syntax 0.8.4\"]\n\n[[package]]\nname = \"regex-syntax\"\nversion = \"0.6.29\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1\"\n\n[[package]]\nname = \"regex-syntax\"\nversion = \"0.8.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b\"\n\n[[package]]\nname = \"reqwest\"\nversion = \"0.11.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62\"\ndependencies = [\"base64 0.21.7\", \"bytes\", \"encoding_rs\", \"futures-core\", \"futures-util\", \"h2\", \"http\", \"http-body\", \"hyper\", \"hyper-tls\", \"ipnet\", \"js-sys\", \"log\", \"mime\", \"native-tls\", \"once_cell\", \"percent-encoding\", \"pin-project-lite\", \"rustls-pemfile\", \"serde\", \"serde_json\", \"serde_urlencoded\", \"sync_wrapper\", \"system-configuration\", \"tokio\", \"tokio-native-tls\", \"tower-service\", \"url\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"web-sys\", \"winreg\"]\n\n[[package]]\nname = \"rexie\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"887466cfa8a12c08ee4b174998135cea8ff0fd84858627cd793e56535a045bc9\"\ndependencies = [\"idb\", \"thiserror\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"rqrr\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"096fe5b00d2859cd111094b3bd41da44aee2a9b14e9bacd673e53349f461b129\"\ndependencies = [\"g2p\", \"image\", \"lru\"]\n\n[[package]]\nname = \"rustc-demangle\"\nversion = \"0.1.24\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f\"\n\n[[package]]\nname = \"rustix\"\nversion = \"0.37.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2\"\ndependencies = [\"bitflags 1.3.2\", \"errno\", \"io-lifetimes\", \"libc\", \"linux-raw-sys 0.3.8\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"rustix\"\nversion = \"0.38.35\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f\"\ndependencies = [\"bitflags 2.6.0\", \"errno\", \"libc\", \"linux-raw-sys 0.4.14\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"rustls-pemfile\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c\"\ndependencies = [\"base64 0.21.7\"]\n\n[[package]]\nname = \"ryu\"\nversion = \"1.0.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f\"\n\n[[package]]\nname = \"salsa20\"\nversion = \"0.7.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"399f290ffc409596022fce5ea5d4138184be4784f2b28c62c59f0d8389059a15\"\ndependencies = [\"cipher 0.2.5\", \"zeroize\"]\n\n[[package]]\nname = \"salsa20\"\nversion = \"0.10.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213\"\ndependencies = [\"cipher 0.4.4\"]\n\n[[package]]\nname = \"same-file\"\nversion = \"1.0.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502\"\ndependencies = [\"winapi-util\"]\n\n[[package]]\nname = \"schannel\"\nversion = \"0.1.23\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534\"\ndependencies = [\"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"scoped-tls\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294\"\n\n[[package]]\nname = \"scopeguard\"\nversion = \"1.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49\"\n\n[[package]]\nname = \"security-framework\"\nversion = \"2.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02\"\ndependencies = [\"bitflags 2.6.0\", \"core-foundation\", \"core-foundation-sys\", \"libc\", \"security-framework-sys\"]\n\n[[package]]\nname = \"security-framework-sys\"\nversion = \"2.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.209\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09\"\ndependencies = [\"serde_derive\"]\n\n[[package]]\nname = \"serde-big-array\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3323f09a748af288c3dc2474ea6803ee81f118321775bffa3ac8f7e65c5e90e7\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde-wasm-bindgen\"\nversion = \"0.6.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b\"\ndependencies = [\"js-sys\", \"serde\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"serde_bytes\"\nversion = \"0.11.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde_derive\"\nversion = \"1.0.209\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"serde_json\"\nversion = \"1.0.127\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad\"\ndependencies = [\"itoa\", \"memchr\", \"ryu\", \"serde\"]\n\n[[package]]\nname = \"serde_spanned\"\nversion = \"0.6.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde_urlencoded\"\nversion = \"0.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd\"\ndependencies = [\"form_urlencoded\", \"itoa\", \"ryu\", \"serde\"]\n\n[[package]]\nname = \"serde_yaml\"\nversion = \"0.9.34+deprecated\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47\"\ndependencies = [\"indexmap 2.5.0\", \"itoa\", \"ryu\", \"serde\", \"unsafe-libyaml\"]\n\n[[package]]\nname = \"sha2\"\nversion = \"0.9.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800\"\ndependencies = [\"block-buffer 0.9.0\", \"cfg-if\", \"cpufeatures\", \"digest 0.9.0\", \"opaque-debug\"]\n\n[[package]]\nname = \"sha2\"\nversion = \"0.10.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8\"\ndependencies = [\"cfg-if\", \"cpufeatures\", \"digest 0.10.7\"]\n\n[[package]]\nname = \"shamirsecretsharing\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6c0df585cf14446dc081c77e9f3a56a9c5d79755e875b165d2d15e4cdf357972\"\ndependencies = [\"cc\", \"rand 0.8.5\", \"xsalsa20poly1305 0.6.0\"]\n\n[[package]]\nname = \"sharded-slab\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6\"\ndependencies = [\"lazy_static\"]\n\n[[package]]\nname = \"shlex\"\nversion = \"1.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n\n[[package]]\nname = \"signal-hook-registry\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"signature\"\nversion = \"1.6.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c\"\n\n[[package]]\nname = \"simd-adler32\"\nversion = \"0.3.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe\"\n\n[[package]]\nname = \"slab\"\nversion = \"0.4.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67\"\ndependencies = [\"autocfg\"]\n\n[[package]]\nname = \"smallvec\"\nversion = \"1.13.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67\"\n\n[[package]]\nname = \"socket2\"\nversion = \"0.4.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d\"\ndependencies = [\"libc\", \"winapi\"]\n\n[[package]]\nname = \"socket2\"\nversion = \"0.5.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c\"\ndependencies = [\"libc\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"spin\"\nversion = \"0.9.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67\"\ndependencies = [\"lock_api\"]\n\n[[package]]\nname = \"strsim\"\nversion = \"0.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623\"\n\n[[package]]\nname = \"strsim\"\nversion = \"0.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\"\n\n[[package]]\nname = \"subtle\"\nversion = \"2.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292\"\n\n[[package]]\nname = \"syn\"\nversion = \"0.15.44\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5\"\ndependencies = [\"proc-macro2 0.4.30\", \"quote 0.6.13\", \"unicode-xid\"]\n\n[[package]]\nname = \"syn\"\nversion = \"1.0.109\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"unicode-ident\"]\n\n[[package]]\nname = \"syn\"\nversion = \"2.0.77\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"unicode-ident\"]\n\n[[package]]\nname = \"sync_wrapper\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160\"\n\n[[package]]\nname = \"system-configuration\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7\"\ndependencies = [\"bitflags 1.3.2\", \"core-foundation\", \"system-configuration-sys\"]\n\n[[package]]\nname = \"system-configuration-sys\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"tempfile\"\nversion = \"3.12.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64\"\ndependencies = [\"cfg-if\", \"fastrand 2.1.1\", \"once_cell\", \"rustix 0.38.35\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"termcolor\"\nversion = \"1.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755\"\ndependencies = [\"winapi-util\"]\n\n[[package]]\nname = \"textwrap\"\nversion = \"0.16.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9\"\n\n[[package]]\nname = \"thiserror\"\nversion = \"1.0.63\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724\"\ndependencies = [\"thiserror-impl\"]\n\n[[package]]\nname = \"thiserror-impl\"\nversion = \"1.0.63\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"thread_local\"\nversion = \"1.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c\"\ndependencies = [\"cfg-if\", \"once_cell\"]\n\n[[package]]\nname = \"tiff\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e\"\ndependencies = [\"flate2\", \"jpeg-decoder\", \"weezl\"]\n\n[[package]]\nname = \"time\"\nversion = \"0.3.36\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885\"\ndependencies = [\"deranged\", \"itoa\", \"num-conv\", \"powerfmt\", \"serde\", \"time-core\", \"time-macros\"]\n\n[[package]]\nname = \"time-core\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3\"\n\n[[package]]\nname = \"time-macros\"\nversion = \"0.2.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf\"\ndependencies = [\"num-conv\", \"time-core\"]\n\n[[package]]\nname = \"tinyvec\"\nversion = \"1.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938\"\ndependencies = [\"tinyvec_macros\"]\n\n[[package]]\nname = \"tinyvec_macros\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20\"\n\n[[package]]\nname = \"tokio\"\nversion = \"1.40.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998\"\ndependencies = [\"backtrace\", \"bytes\", \"libc\", \"mio\", \"parking_lot\", \"pin-project-lite\", \"signal-hook-registry\", \"socket2 0.5.7\", \"tokio-macros\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"tokio-macros\"\nversion = \"2.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"tokio-native-tls\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2\"\ndependencies = [\"native-tls\", \"tokio\"]\n\n[[package]]\nname = \"tokio-util\"\nversion = \"0.7.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1\"\ndependencies = [\"bytes\", \"futures-core\", \"futures-sink\", \"pin-project-lite\", \"tokio\"]\n\n[[package]]\nname = \"toml\"\nversion = \"0.8.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e\"\ndependencies = [\"serde\", \"serde_spanned\", \"toml_datetime\", \"toml_edit\"]\n\n[[package]]\nname = \"toml_datetime\"\nversion = \"0.6.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"toml_edit\"\nversion = \"0.22.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d\"\ndependencies = [\"indexmap 2.5.0\", \"serde\", \"serde_spanned\", \"toml_datetime\", \"winnow\"]\n\n[[package]]\nname = \"tower-service\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3\"\n\n[[package]]\nname = \"tracing\"\nversion = \"0.1.40\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef\"\ndependencies = [\"pin-project-lite\", \"tracing-attributes\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-attributes\"\nversion = \"0.1.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"tracing-core\"\nversion = \"0.1.32\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54\"\ndependencies = [\"once_cell\", \"valuable\"]\n\n[[package]]\nname = \"tracing-log\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3\"\ndependencies = [\"log\", \"once_cell\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-serde\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1\"\ndependencies = [\"serde\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-subscriber\"\nversion = \"0.3.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b\"\ndependencies = [\"matchers\", \"nu-ansi-term\", \"once_cell\", \"regex\", \"serde\", \"serde_json\", \"sharded-slab\", \"smallvec\", \"thread_local\", \"time\", \"tracing\", \"tracing-core\", \"tracing-log\", \"tracing-serde\"]\n\n[[package]]\nname = \"tracing-web\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9e6a141feebd51f8d91ebfd785af50fca223c570b86852166caa3b141defe7c\"\ndependencies = [\"js-sys\", \"tracing-core\", \"tracing-subscriber\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"try-lock\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b\"\n\n[[package]]\nname = \"typenum\"\nversion = \"1.17.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825\"\n\n[[package]]\nname = \"unicode-bidi\"\nversion = \"0.3.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75\"\n\n[[package]]\nname = \"unicode-ident\"\nversion = \"1.0.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b\"\n\n[[package]]\nname = \"unicode-normalization\"\nversion = \"0.1.23\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5\"\ndependencies = [\"tinyvec\"]\n\n[[package]]\nname = \"unicode-xid\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc\"\n\n[[package]]\nname = \"universal-hash\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402\"\ndependencies = [\"generic-array\", \"subtle\"]\n\n[[package]]\nname = \"universal-hash\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea\"\ndependencies = [\"crypto-common\", \"subtle\"]\n\n[[package]]\nname = \"unsafe-libyaml\"\nversion = \"0.2.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861\"\n\n[[package]]\nname = \"url\"\nversion = \"2.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c\"\ndependencies = [\"form_urlencoded\", \"idna\", \"percent-encoding\"]\n\n[[package]]\nname = \"utf8-width\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3\"\n\n[[package]]\nname = \"uuid\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314\"\ndependencies = [\"getrandom 0.2.15\", \"rand 0.8.5\", \"uuid-macro-internal\"]\n\n[[package]]\nname = \"uuid-macro-internal\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"valuable\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d\"\n\n[[package]]\nname = \"value-bag\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101\"\n\n[[package]]\nname = \"vcpkg\"\nversion = \"0.2.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n\n[[package]]\nname = \"version_check\"\nversion = \"0.9.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a\"\n\n[[package]]\nname = \"waker-fn\"\nversion = \"1.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7\"\n\n[[package]]\nname = \"walkdir\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b\"\ndependencies = [\"same-file\", \"winapi-util\"]\n\n[[package]]\nname = \"want\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e\"\ndependencies = [\"try-lock\"]\n\n[[package]]\nname = \"wasi\"\nversion = \"0.9.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519\"\n\n[[package]]\nname = \"wasi\"\nversion = \"0.11.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423\"\n\n[[package]]\nname = \"wasm-bindgen\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5\"\ndependencies = [\"cfg-if\", \"once_cell\", \"serde\", \"serde_json\", \"wasm-bindgen-macro\"]\n\n[[package]]\nname = \"wasm-bindgen-backend\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b\"\ndependencies = [\"bumpalo\", \"log\", \"once_cell\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\", \"wasm-bindgen-shared\"]\n\n[[package]]\nname = \"wasm-bindgen-futures\"\nversion = \"0.4.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed\"\ndependencies = [\"cfg-if\", \"js-sys\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"wasm-bindgen-macro\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf\"\ndependencies = [\"quote 1.0.37\", \"wasm-bindgen-macro-support\"]\n\n[[package]]\nname = \"wasm-bindgen-macro-support\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\", \"wasm-bindgen-backend\", \"wasm-bindgen-shared\"]\n\n[[package]]\nname = \"wasm-bindgen-shared\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484\"\n\n[[package]]\nname = \"wasm-bindgen-test\"\nversion = \"0.3.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9\"\ndependencies = [\"console_error_panic_hook\", \"js-sys\", \"minicov\", \"scoped-tls\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"wasm-bindgen-test-macro\"]\n\n[[package]]\nname = \"wasm-bindgen-test-macro\"\nversion = \"0.3.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"web-sys\"\nversion = \"0.3.70\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0\"\ndependencies = [\"js-sys\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"weezl\"\nversion = \"0.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082\"\n\n[[package]]\nname = \"winapi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419\"\ndependencies = [\"winapi-i686-pc-windows-gnu\", \"winapi-x86_64-pc-windows-gnu\"]\n\n[[package]]\nname = \"winapi-i686-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6\"\n\n[[package]]\nname = \"winapi-util\"\nversion = \"0.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb\"\ndependencies = [\"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"winapi-x86_64-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f\"\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.48.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9\"\ndependencies = [\"windows-targets 0.48.5\"]\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.52.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d\"\ndependencies = [\"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.59.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b\"\ndependencies = [\"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"windows-targets\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c\"\ndependencies = [\"windows_aarch64_gnullvm 0.48.5\", \"windows_aarch64_msvc 0.48.5\", \"windows_i686_gnu 0.48.5\", \"windows_i686_msvc 0.48.5\", \"windows_x86_64_gnu 0.48.5\", \"windows_x86_64_gnullvm 0.48.5\", \"windows_x86_64_msvc 0.48.5\"]\n\n[[package]]\nname = \"windows-targets\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973\"\ndependencies = [\"windows_aarch64_gnullvm 0.52.6\", \"windows_aarch64_msvc 0.52.6\", \"windows_i686_gnu 0.52.6\", \"windows_i686_gnullvm\", \"windows_i686_msvc 0.52.6\", \"windows_x86_64_gnu 0.52.6\", \"windows_x86_64_gnullvm 0.52.6\", \"windows_x86_64_msvc 0.52.6\"]\n\n[[package]]\nname = \"windows_aarch64_gnullvm\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8\"\n\n[[package]]\nname = \"windows_aarch64_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3\"\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc\"\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b\"\n\n[[package]]\nname = \"windows_i686_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78\"\n\n[[package]]\nname = \"windows_x86_64_gnullvm\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc\"\n\n[[package]]\nname = \"windows_x86_64_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec\"\n\n[[package]]\nname = \"winnow\"\nversion = \"0.6.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"winreg\"\nversion = \"0.50.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1\"\ndependencies = [\"cfg-if\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"x25519-dalek\"\nversion = \"1.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f\"\ndependencies = [\"curve25519-dalek\", \"rand_core 0.5.1\", \"zeroize\"]\n\n[[package]]\nname = \"xsalsa20poly1305\"\nversion = \"0.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0304c336e98d753428f7b3d8899d60b8a87a961ef50bdfc44af0c1bea2651ce5\"\ndependencies = [\"aead 0.3.2\", \"poly1305 0.6.2\", \"rand_core 0.5.1\", \"salsa20 0.7.2\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"xsalsa20poly1305\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7\"\ndependencies = [\"aead 0.5.2\", \"poly1305 0.8.0\", \"salsa20 0.10.2\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"yansi\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec\"\n\n[[package]]\nname = \"zerocopy\"\nversion = \"0.7.35\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0\"\ndependencies = [\"byteorder\", \"zerocopy-derive\"]\n\n[[package]]\nname = \"zerocopy-derive\"\nversion = \"0.7.35\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"zeroize\"\nversion = \"1.8.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde\"\ndependencies = [\"zeroize_derive\"]\n\n[[package]]\nname = \"zeroize_derive\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"zune-inflate\"\nversion = \"0.2.54\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02\"\ndependencies = [\"simd-adler32\"]\n","rust_toolchain_file":null}} \ No newline at end of file +{"skeleton":{"manifests":[{"relative_path":"Cargo.toml","contents":"[workspace]\nmembers = [\"core\", \"meta-server-emulator\", \"core-swift-lib\", \"wasm\", \"cli\"]\nexclude = [\"meta-server-serverless\"]\nresolver = \"2\"\n\n[workspace.dependencies]\nanyhow = \"1.0.89\"\nasync-mutex = \"1.4.0\"\nasync-trait = \"0.1.82\"\nbase64 = \"0.20.0\"\ned25519-dalek = \"1.0.1\"\nflume = \"0.11.0\"\nhex = \"0.4.3\"\nrand = \"0.8.5\"\nserde_derive = \"1.0.210\"\nserde_json = \"1.0.128\"\nshamirsecretsharing = \"0.1.5\"\nthiserror = \"1.0.63\"\ntracing = \"0.1.40\"\n\n[workspace.dependencies.async-std]\nversion = \"1.13.0\"\n\n[workspace.dependencies.crypto_box]\nversion = \"0.8.2\"\nfeatures = [\"std\"]\n\n[workspace.dependencies.diesel]\nversion = \"2.2.4\"\n\n[workspace.dependencies.diesel_migrations]\nversion = \"2.2.0\"\n\n[workspace.dependencies.getrandom]\nversion = \"0.2.15\"\nfeatures = [\"js\"]\n\n[workspace.dependencies.serde]\nversion = \"1.0.188\"\nfeatures = [\"derive\"]\n\n[workspace.dependencies.sha2]\nversion = \"0.10.8\"\nfeatures = [\"oid\"]\n\n[workspace.dependencies.tracing-subscriber]\nversion = \"0.3.18\"\n","targets":[]},{"relative_path":"cli/Cargo.toml","contents":"bench = []\ntest = []\nexample = []\n\n[[bin]]\npath = \"src/main.rs\"\nname = \"meta-secret-cli\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-cli\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies]\nanyhow = \"1.0\"\nmeta-secret-core = \"1.2.6\"\nserde = \"1.0\"\nserde_json = \"1.0\"\nserde_yaml = \"0.9\"\nthiserror = \"1.0.33\"\n\n[dependencies.clap]\nversion = \"3.2\"\nfeatures = [\"derive\"]\n","targets":[{"path":"src/main.rs","kind":"Bin","name":"meta-secret-cli"}]},{"relative_path":"core/Cargo.toml","contents":"bin = []\nbench = []\nexample = []\n\n[[test]]\npath = \"tests/mod.rs\"\nname = \"mod\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-core\"\nedition = \"2021\"\nversion = \"0.0.1\"\ndescription = \"Meta Secret Core Module\"\nhomepage = \"https://github.com/meta-secret/meta-secret-core\"\ndocumentation = \"https://github.com/meta-secret/meta-secret-core/blob/main/core/README.md\"\nreadme = \"README.md\"\nlicense = \"Apache-2.0\"\nrepository = \"https://github.com/meta-secret/meta-secret-core\"\n\n[dependencies]\nimage = \"0.24\"\nlog = \"0.4.22\"\nqrcode-generator = \"4.1.6\"\nrqrr = \"0.5\"\nserde-big-array = \"0.4\"\nserde_bytes = \"0.11\"\ntracing-attributes = \"0.1.27\"\nwasm-bindgen = \"0.2.93\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-mutex]\nworkspace = true\n\n[dependencies.async-std]\nworkspace = true\nfeatures = [\"unstable\"]\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.base64]\nworkspace = true\n\n[dependencies.crypto_box]\nworkspace = true\n\n[dependencies.ed25519-dalek]\nworkspace = true\n\n[dependencies.flume]\nworkspace = true\n\n[dependencies.getrandom]\nworkspace = true\n\n[dependencies.hex]\nworkspace = true\n\n[dependencies.rand]\nworkspace = true\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.sha2]\nworkspace = true\n\n[dependencies.shamirsecretsharing]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[dependencies.tracing]\nworkspace = true\n\n[dependencies.tracing-subscriber]\nworkspace = true\nfeatures = [\"json\", \"env-filter\"]\n\n[dependencies.uuid]\nversion = \"1.3.0\"\nfeatures = [\"v4\", \"fast-rng\", \"macro-diagnostics\"]\n\n[dev-dependencies]\npretty_assertions = \"1\"\n\n[dev-dependencies.tokio]\nversion = \"1.20.1\"\nfeatures = [\"full\"]\n\n[features]\ntest_utils = []\n\n[lib]\nname = \"meta_secret_core\"\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"lib\", \"staticlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_core"},{"path":"tests/mod.rs","kind":"Test","name":"mod"}]},{"relative_path":"core-swift-lib/Cargo.toml","contents":"bin = []\nbench = []\ntest = []\nexample = []\n\n[package]\nname = \"meta-secret-core-swift\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.base64]\nworkspace = true\n\n[dependencies.crypto_box]\nworkspace = true\n\n[dependencies.ed25519-dalek]\nworkspace = true\n\n[dependencies.hex]\nworkspace = true\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.sha2]\nworkspace = true\n\n[lib]\nname = \"meta_secret_core_swift\"\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"lib\", \"staticlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_core_swift"}]},{"relative_path":"meta-server-emulator/Cargo.toml","contents":"bin = []\nbench = []\ntest = []\nexample = []\n\n[package]\nname = \"meta-server-emulator\"\nedition = \"2021\"\nversion = \"0.0.1\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.diesel]\nworkspace = true\nfeatures = [\"sqlite\"]\n\n[dependencies.diesel_migrations]\nworkspace = true\nfeatures = [\"sqlite\"]\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[lib]\npath = \"src/lib.rs\"\nname = \"meta_server_emulator\"\nplugin = false\nproc-macro = false\nedition = \"2021\"\nrequired-features = []\ncrate-type = [\"rlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_server_emulator"}]},{"relative_path":"wasm/Cargo.toml","contents":"bin = []\nbench = []\nexample = []\n\n[[test]]\npath = \"tests/web.rs\"\nname = \"web\"\nplugin = false\nproc-macro = false\nedition = \"2018\"\nrequired-features = []\n\n[package]\nname = \"meta-secret-wasm\"\nedition = \"2018\"\nversion = \"0.0.1\"\nauthors = [\"cypherkitty \"]\n\n[dependencies]\nrexie = \"0.6.2\"\nserde-wasm-bindgen = \"0.6.5\"\ntracing-web = \"0.1.3\"\nwasm-bindgen-futures = \"0.4.43\"\n\n[dependencies.anyhow]\nworkspace = true\n\n[dependencies.async-mutex]\nworkspace = true\n\n[dependencies.async-trait]\nworkspace = true\n\n[dependencies.console_error_panic_hook]\nversion = \"0.1.7\"\noptional = true\n\n[dependencies.flume]\nworkspace = true\n\n[dependencies.getrandom]\nworkspace = true\n\n[dependencies.js-sys]\nversion = \"0.3.70\"\n\n[dependencies.meta-secret-core]\npath = \"../core\"\n\n[dependencies.serde]\nworkspace = true\n\n[dependencies.serde_derive]\nworkspace = true\n\n[dependencies.serde_json]\nworkspace = true\n\n[dependencies.thiserror]\nworkspace = true\n\n[dependencies.tracing]\nworkspace = true\n\n[dependencies.tracing-subscriber]\nworkspace = true\nfeatures = [\"fmt\", \"time\"]\n\n[dependencies.wasm-bindgen]\nversion = \"0.2.93\"\nfeatures = [\"serde-serialize\"]\n\n[dependencies.web-sys]\nversion = \"0.3.70\"\nfeatures = [\"DomException\", \"DomStringList\", \"Event\", \"StorageType\", \"Window\"]\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.43\"\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".dependencies.meta-secret-core]\npath = \"../core\"\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".dev-dependencies]\n\n[target.\"cfg(target_arch = \\\"wasm32\\\")\".build-dependencies]\n\n[features]\ndefault = [\"console_error_panic_hook\"]\n\n[lib]\nplugin = false\nproc-macro = false\nrequired-features = []\ncrate-type = [\"cdylib\", \"rlib\"]\n","targets":[{"path":"src/lib.rs","kind":{"Lib":{"is_proc_macro":false}},"name":"meta_secret_wasm"},{"path":"tests/web.rs","kind":"Test","name":"web"}]}],"config_file":null,"lock_file":"version = 3\n\n[[package]]\nname = \"addr2line\"\nversion = \"0.24.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375\"\ndependencies = [\"gimli\"]\n\n[[package]]\nname = \"adler\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe\"\n\n[[package]]\nname = \"adler2\"\nversion = \"2.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627\"\n\n[[package]]\nname = \"aead\"\nversion = \"0.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"aead\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0\"\ndependencies = [\"crypto-common\", \"generic-array\"]\n\n[[package]]\nname = \"ahash\"\nversion = \"0.7.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9\"\ndependencies = [\"getrandom 0.2.15\", \"once_cell\", \"version_check\"]\n\n[[package]]\nname = \"aho-corasick\"\nversion = \"1.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"anyhow\"\nversion = \"1.0.89\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6\"\n\n[[package]]\nname = \"async-channel\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35\"\ndependencies = [\"concurrent-queue\", \"event-listener 2.5.3\", \"futures-core\"]\n\n[[package]]\nname = \"async-channel\"\nversion = \"2.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a\"\ndependencies = [\"concurrent-queue\", \"event-listener-strategy\", \"futures-core\", \"pin-project-lite\"]\n\n[[package]]\nname = \"async-executor\"\nversion = \"1.13.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec\"\ndependencies = [\"async-task\", \"concurrent-queue\", \"fastrand\", \"futures-lite\", \"slab\"]\n\n[[package]]\nname = \"async-global-executor\"\nversion = \"2.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c\"\ndependencies = [\"async-channel 2.3.1\", \"async-executor\", \"async-io\", \"async-lock\", \"blocking\", \"futures-lite\", \"once_cell\"]\n\n[[package]]\nname = \"async-io\"\nversion = \"2.3.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8\"\ndependencies = [\"async-lock\", \"cfg-if\", \"concurrent-queue\", \"futures-io\", \"futures-lite\", \"parking\", \"polling\", \"rustix\", \"slab\", \"tracing\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"async-lock\"\nversion = \"3.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18\"\ndependencies = [\"event-listener 5.3.1\", \"event-listener-strategy\", \"pin-project-lite\"]\n\n[[package]]\nname = \"async-mutex\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e\"\ndependencies = [\"event-listener 2.5.3\"]\n\n[[package]]\nname = \"async-process\"\nversion = \"2.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb\"\ndependencies = [\"async-channel 2.3.1\", \"async-io\", \"async-lock\", \"async-signal\", \"async-task\", \"blocking\", \"cfg-if\", \"event-listener 5.3.1\", \"futures-lite\", \"rustix\", \"tracing\"]\n\n[[package]]\nname = \"async-signal\"\nversion = \"0.2.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3\"\ndependencies = [\"async-io\", \"async-lock\", \"atomic-waker\", \"cfg-if\", \"futures-core\", \"futures-io\", \"rustix\", \"signal-hook-registry\", \"slab\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"async-std\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615\"\ndependencies = [\"async-channel 1.9.0\", \"async-global-executor\", \"async-io\", \"async-lock\", \"async-process\", \"crossbeam-utils\", \"futures-channel\", \"futures-core\", \"futures-io\", \"futures-lite\", \"gloo-timers\", \"kv-log-macro\", \"log\", \"memchr\", \"once_cell\", \"pin-project-lite\", \"pin-utils\", \"slab\", \"wasm-bindgen-futures\"]\n\n[[package]]\nname = \"async-task\"\nversion = \"4.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de\"\n\n[[package]]\nname = \"async-trait\"\nversion = \"0.1.82\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"atomic-waker\"\nversion = \"1.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0\"\n\n[[package]]\nname = \"atty\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8\"\ndependencies = [\"hermit-abi 0.1.19\", \"libc\", \"winapi\"]\n\n[[package]]\nname = \"autocfg\"\nversion = \"1.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0\"\n\n[[package]]\nname = \"backtrace\"\nversion = \"0.3.74\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a\"\ndependencies = [\"addr2line\", \"cfg-if\", \"libc\", \"miniz_oxide 0.8.0\", \"object\", \"rustc-demangle\", \"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"base64\"\nversion = \"0.20.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5\"\n\n[[package]]\nname = \"base64\"\nversion = \"0.21.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567\"\n\n[[package]]\nname = \"bit_field\"\nversion = \"0.10.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"1.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"2.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de\"\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"block-buffer\"\nversion = \"0.10.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"blocking\"\nversion = \"1.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea\"\ndependencies = [\"async-channel 2.3.1\", \"async-task\", \"futures-io\", \"futures-lite\", \"piper\"]\n\n[[package]]\nname = \"bumpalo\"\nversion = \"3.16.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c\"\n\n[[package]]\nname = \"bytemuck\"\nversion = \"1.18.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae\"\n\n[[package]]\nname = \"byteorder\"\nversion = \"1.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b\"\n\n[[package]]\nname = \"bytes\"\nversion = \"1.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50\"\n\n[[package]]\nname = \"cc\"\nversion = \"1.1.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476\"\ndependencies = [\"shlex\"]\n\n[[package]]\nname = \"cfg-if\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd\"\n\n[[package]]\nname = \"chacha20\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818\"\ndependencies = [\"cfg-if\", \"cipher 0.4.4\", \"cpufeatures\"]\n\n[[package]]\nname = \"chacha20poly1305\"\nversion = \"0.10.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35\"\ndependencies = [\"aead 0.5.2\", \"chacha20\", \"cipher 0.4.4\", \"poly1305 0.8.0\", \"zeroize\"]\n\n[[package]]\nname = \"cipher\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"cipher\"\nversion = \"0.4.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad\"\ndependencies = [\"crypto-common\", \"inout\", \"zeroize\"]\n\n[[package]]\nname = \"clap\"\nversion = \"3.2.25\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123\"\ndependencies = [\"atty\", \"bitflags 1.3.2\", \"clap_derive\", \"clap_lex\", \"indexmap 1.9.3\", \"once_cell\", \"strsim 0.10.0\", \"termcolor\", \"textwrap\"]\n\n[[package]]\nname = \"clap_derive\"\nversion = \"3.2.25\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008\"\ndependencies = [\"heck 0.4.1\", \"proc-macro-error\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 1.0.109\"]\n\n[[package]]\nname = \"clap_lex\"\nversion = \"0.2.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5\"\ndependencies = [\"os_str_bytes\"]\n\n[[package]]\nname = \"color_quant\"\nversion = \"1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b\"\n\n[[package]]\nname = \"concurrent-queue\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973\"\ndependencies = [\"crossbeam-utils\"]\n\n[[package]]\nname = \"console_error_panic_hook\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc\"\ndependencies = [\"cfg-if\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"const-oid\"\nversion = \"0.9.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8\"\n\n[[package]]\nname = \"core-foundation\"\nversion = \"0.9.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"core-foundation-sys\"\nversion = \"0.8.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b\"\n\n[[package]]\nname = \"cpufeatures\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"cpuid-bool\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba\"\n\n[[package]]\nname = \"crc32fast\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3\"\ndependencies = [\"cfg-if\"]\n\n[[package]]\nname = \"crossbeam-deque\"\nversion = \"0.8.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d\"\ndependencies = [\"crossbeam-epoch\", \"crossbeam-utils\"]\n\n[[package]]\nname = \"crossbeam-epoch\"\nversion = \"0.9.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e\"\ndependencies = [\"crossbeam-utils\"]\n\n[[package]]\nname = \"crossbeam-utils\"\nversion = \"0.8.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80\"\n\n[[package]]\nname = \"crunchy\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7\"\n\n[[package]]\nname = \"crypto-common\"\nversion = \"0.1.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3\"\ndependencies = [\"generic-array\", \"rand_core 0.6.4\", \"typenum\"]\n\n[[package]]\nname = \"crypto_box\"\nversion = \"0.8.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fd26c32de5307fd08aac445a75c43472b14559d5dccdfba8022dbcd075838ebc\"\ndependencies = [\"aead 0.5.2\", \"chacha20\", \"chacha20poly1305\", \"salsa20 0.10.2\", \"x25519-dalek\", \"xsalsa20poly1305 0.9.1\", \"zeroize\"]\n\n[[package]]\nname = \"curve25519-dalek\"\nversion = \"3.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61\"\ndependencies = [\"byteorder\", \"digest 0.9.0\", \"rand_core 0.5.1\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"darling\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989\"\ndependencies = [\"darling_core\", \"darling_macro\"]\n\n[[package]]\nname = \"darling_core\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5\"\ndependencies = [\"fnv\", \"ident_case\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"strsim 0.11.1\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"darling_macro\"\nversion = \"0.20.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806\"\ndependencies = [\"darling_core\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"deranged\"\nversion = \"0.3.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4\"\ndependencies = [\"powerfmt\"]\n\n[[package]]\nname = \"diesel\"\nversion = \"2.2.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e\"\ndependencies = [\"diesel_derives\", \"libsqlite3-sys\", \"time\"]\n\n[[package]]\nname = \"diesel_derives\"\nversion = \"2.2.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4\"\ndependencies = [\"diesel_table_macro_syntax\", \"dsl_auto_type\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"diesel_migrations\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6\"\ndependencies = [\"diesel\", \"migrations_internals\", \"migrations_macros\"]\n\n[[package]]\nname = \"diesel_table_macro_syntax\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25\"\ndependencies = [\"syn 2.0.77\"]\n\n[[package]]\nname = \"diff\"\nversion = \"0.1.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8\"\n\n[[package]]\nname = \"digest\"\nversion = \"0.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"digest\"\nversion = \"0.10.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292\"\ndependencies = [\"block-buffer 0.10.4\", \"const-oid\", \"crypto-common\"]\n\n[[package]]\nname = \"dsl_auto_type\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607\"\ndependencies = [\"darling\", \"either\", \"heck 0.5.0\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"ed25519\"\nversion = \"1.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7\"\ndependencies = [\"signature\"]\n\n[[package]]\nname = \"ed25519-dalek\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d\"\ndependencies = [\"curve25519-dalek\", \"ed25519\", \"rand 0.7.3\", \"serde\", \"sha2 0.9.9\", \"zeroize\"]\n\n[[package]]\nname = \"either\"\nversion = \"1.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0\"\n\n[[package]]\nname = \"encoding_rs\"\nversion = \"0.8.34\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59\"\ndependencies = [\"cfg-if\"]\n\n[[package]]\nname = \"equivalent\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5\"\n\n[[package]]\nname = \"errno\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba\"\ndependencies = [\"libc\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"event-listener\"\nversion = \"2.5.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0\"\n\n[[package]]\nname = \"event-listener\"\nversion = \"5.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba\"\ndependencies = [\"concurrent-queue\", \"parking\", \"pin-project-lite\"]\n\n[[package]]\nname = \"event-listener-strategy\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1\"\ndependencies = [\"event-listener 5.3.1\", \"pin-project-lite\"]\n\n[[package]]\nname = \"exr\"\nversion = \"1.72.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4\"\ndependencies = [\"bit_field\", \"flume\", \"half\", \"lebe\", \"miniz_oxide 0.7.4\", \"rayon-core\", \"smallvec\", \"zune-inflate\"]\n\n[[package]]\nname = \"fastrand\"\nversion = \"2.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6\"\n\n[[package]]\nname = \"fdeflate\"\nversion = \"0.3.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645\"\ndependencies = [\"simd-adler32\"]\n\n[[package]]\nname = \"flate2\"\nversion = \"1.0.33\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253\"\ndependencies = [\"crc32fast\", \"miniz_oxide 0.8.0\"]\n\n[[package]]\nname = \"flume\"\nversion = \"0.11.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181\"\ndependencies = [\"futures-core\", \"futures-sink\", \"nanorand\", \"spin\"]\n\n[[package]]\nname = \"fnv\"\nversion = \"1.0.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1\"\n\n[[package]]\nname = \"foreign-types\"\nversion = \"0.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1\"\ndependencies = [\"foreign-types-shared\"]\n\n[[package]]\nname = \"foreign-types-shared\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b\"\n\n[[package]]\nname = \"form_urlencoded\"\nversion = \"1.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456\"\ndependencies = [\"percent-encoding\"]\n\n[[package]]\nname = \"futures-channel\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78\"\ndependencies = [\"futures-core\"]\n\n[[package]]\nname = \"futures-core\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d\"\n\n[[package]]\nname = \"futures-io\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1\"\n\n[[package]]\nname = \"futures-lite\"\nversion = \"2.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5\"\ndependencies = [\"fastrand\", \"futures-core\", \"futures-io\", \"parking\", \"pin-project-lite\"]\n\n[[package]]\nname = \"futures-sink\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5\"\n\n[[package]]\nname = \"futures-task\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004\"\n\n[[package]]\nname = \"futures-util\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48\"\ndependencies = [\"futures-core\", \"futures-task\", \"pin-project-lite\", \"pin-utils\"]\n\n[[package]]\nname = \"g2gen\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2fc100b16c63808c5c388cd23ff94c5a35cf28ea459f336323f7948a39480555\"\ndependencies = [\"g2poly\", \"proc-macro2 0.4.30\", \"quote 0.6.13\", \"syn 0.15.44\"]\n\n[[package]]\nname = \"g2p\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bf09bc632629cbe5420b330e45bcc8f80403e74ba1027d213258914fd5c62755\"\ndependencies = [\"g2gen\", \"g2poly\"]\n\n[[package]]\nname = \"g2poly\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e837767888fca507f07e89c90e0b350da7bbb89170f67a4655dc9bdc4cca457b\"\n\n[[package]]\nname = \"generic-array\"\nversion = \"0.14.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a\"\ndependencies = [\"typenum\", \"version_check\"]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.1.16\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce\"\ndependencies = [\"cfg-if\", \"libc\", \"wasi 0.9.0+wasi-snapshot-preview1\"]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.2.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7\"\ndependencies = [\"cfg-if\", \"js-sys\", \"libc\", \"wasi 0.11.0+wasi-snapshot-preview1\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"gif\"\nversion = \"0.13.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2\"\ndependencies = [\"color_quant\", \"weezl\"]\n\n[[package]]\nname = \"gimli\"\nversion = \"0.31.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64\"\n\n[[package]]\nname = \"gloo-timers\"\nversion = \"0.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994\"\ndependencies = [\"futures-channel\", \"futures-core\", \"js-sys\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"h2\"\nversion = \"0.3.26\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8\"\ndependencies = [\"bytes\", \"fnv\", \"futures-core\", \"futures-sink\", \"futures-util\", \"http\", \"indexmap 2.5.0\", \"slab\", \"tokio\", \"tokio-util\", \"tracing\"]\n\n[[package]]\nname = \"half\"\nversion = \"2.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888\"\ndependencies = [\"cfg-if\", \"crunchy\"]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.12.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888\"\ndependencies = [\"ahash\"]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.14.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1\"\n\n[[package]]\nname = \"heck\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8\"\n\n[[package]]\nname = \"heck\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\"\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.1.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024\"\n\n[[package]]\nname = \"hermit-abi\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc\"\n\n[[package]]\nname = \"hex\"\nversion = \"0.4.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70\"\n\n[[package]]\nname = \"html-escape\"\nversion = \"0.2.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476\"\ndependencies = [\"utf8-width\"]\n\n[[package]]\nname = \"http\"\nversion = \"0.2.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1\"\ndependencies = [\"bytes\", \"fnv\", \"itoa\"]\n\n[[package]]\nname = \"http-body\"\nversion = \"0.4.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2\"\ndependencies = [\"bytes\", \"http\", \"pin-project-lite\"]\n\n[[package]]\nname = \"httparse\"\nversion = \"1.9.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9\"\n\n[[package]]\nname = \"httpdate\"\nversion = \"1.0.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9\"\n\n[[package]]\nname = \"hyper\"\nversion = \"0.14.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9\"\ndependencies = [\"bytes\", \"futures-channel\", \"futures-core\", \"futures-util\", \"h2\", \"http\", \"http-body\", \"httparse\", \"httpdate\", \"itoa\", \"pin-project-lite\", \"socket2\", \"tokio\", \"tower-service\", \"tracing\", \"want\"]\n\n[[package]]\nname = \"hyper-tls\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905\"\ndependencies = [\"bytes\", \"hyper\", \"native-tls\", \"tokio\", \"tokio-native-tls\"]\n\n[[package]]\nname = \"idb\"\nversion = \"0.6.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2764bba4203538c2ef2d733d5bad8fdb4622b1ebc5560831279db7b3be1332e8\"\ndependencies = [\"js-sys\", \"num-traits\", \"thiserror\", \"tokio\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"ident_case\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39\"\n\n[[package]]\nname = \"idna\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6\"\ndependencies = [\"unicode-bidi\", \"unicode-normalization\"]\n\n[[package]]\nname = \"image\"\nversion = \"0.24.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d\"\ndependencies = [\"bytemuck\", \"byteorder\", \"color_quant\", \"exr\", \"gif\", \"jpeg-decoder\", \"num-traits\", \"png\", \"qoi\", \"tiff\"]\n\n[[package]]\nname = \"indexmap\"\nversion = \"1.9.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99\"\ndependencies = [\"autocfg\", \"hashbrown 0.12.3\"]\n\n[[package]]\nname = \"indexmap\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5\"\ndependencies = [\"equivalent\", \"hashbrown 0.14.5\"]\n\n[[package]]\nname = \"inout\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5\"\ndependencies = [\"generic-array\"]\n\n[[package]]\nname = \"ipnet\"\nversion = \"2.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4\"\n\n[[package]]\nname = \"itoa\"\nversion = \"1.0.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b\"\n\n[[package]]\nname = \"jpeg-decoder\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0\"\ndependencies = [\"rayon\"]\n\n[[package]]\nname = \"js-sys\"\nversion = \"0.3.70\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a\"\ndependencies = [\"wasm-bindgen\"]\n\n[[package]]\nname = \"kv-log-macro\"\nversion = \"1.0.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f\"\ndependencies = [\"log\"]\n\n[[package]]\nname = \"lazy_static\"\nversion = \"1.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe\"\n\n[[package]]\nname = \"lebe\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8\"\n\n[[package]]\nname = \"libc\"\nversion = \"0.2.158\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n\n[[package]]\nname = \"libsqlite3-sys\"\nversion = \"0.30.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149\"\ndependencies = [\"pkg-config\", \"vcpkg\"]\n\n[[package]]\nname = \"linux-raw-sys\"\nversion = \"0.4.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89\"\n\n[[package]]\nname = \"lock_api\"\nversion = \"0.4.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17\"\ndependencies = [\"autocfg\", \"scopeguard\"]\n\n[[package]]\nname = \"log\"\nversion = \"0.4.22\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24\"\ndependencies = [\"value-bag\"]\n\n[[package]]\nname = \"lru\"\nversion = \"0.7.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a\"\ndependencies = [\"hashbrown 0.12.3\"]\n\n[[package]]\nname = \"matchers\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558\"\ndependencies = [\"regex-automata 0.1.10\"]\n\n[[package]]\nname = \"memchr\"\nversion = \"2.7.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3\"\n\n[[package]]\nname = \"meta-secret-cli\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"clap\", \"meta-secret-core 1.10.8\", \"serde\", \"serde_json\", \"serde_yaml\", \"thiserror\"]\n\n[[package]]\nname = \"meta-secret-core\"\nversion = \"1.10.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"60891c16c01e54fb1885eb42056fb8c4f202593ec8f8dca0159775acab6860e5\"\ndependencies = [\"anyhow\", \"async-std\", \"async-trait\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"getrandom 0.2.15\", \"hex\", \"image\", \"qrcode-generator\", \"rand 0.8.5\", \"reqwest\", \"rqrr\", \"serde\", \"serde-big-array\", \"serde_bytes\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\", \"shamirsecretsharing\", \"thiserror\"]\n\n[[package]]\nname = \"meta-secret-core\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-mutex\", \"async-std\", \"async-trait\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"flume\", \"getrandom 0.2.15\", \"hex\", \"image\", \"log\", \"pretty_assertions\", \"qrcode-generator\", \"rand 0.8.5\", \"rqrr\", \"serde\", \"serde-big-array\", \"serde_bytes\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\", \"shamirsecretsharing\", \"thiserror\", \"tokio\", \"tracing\", \"tracing-attributes\", \"tracing-subscriber\", \"uuid\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"meta-secret-core-swift\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"base64 0.20.0\", \"crypto_box\", \"ed25519-dalek\", \"hex\", \"meta-secret-core 2.0.0\", \"serde\", \"serde_derive\", \"serde_json\", \"sha2 0.10.8\"]\n\n[[package]]\nname = \"meta-secret-wasm\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-mutex\", \"async-trait\", \"console_error_panic_hook\", \"flume\", \"getrandom 0.2.15\", \"js-sys\", \"meta-secret-core 2.0.0\", \"rexie\", \"serde\", \"serde-wasm-bindgen\", \"serde_derive\", \"serde_json\", \"thiserror\", \"tracing\", \"tracing-subscriber\", \"tracing-web\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"wasm-bindgen-test\", \"web-sys\"]\n\n[[package]]\nname = \"meta-server-emulator\"\nversion = \"0.0.1\"\ndependencies = [\"anyhow\", \"async-trait\", \"diesel\", \"diesel_migrations\", \"meta-secret-core 2.0.0\", \"serde\", \"serde_derive\", \"serde_json\", \"thiserror\"]\n\n[[package]]\nname = \"migrations_internals\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff\"\ndependencies = [\"serde\", \"toml\"]\n\n[[package]]\nname = \"migrations_macros\"\nversion = \"2.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd\"\ndependencies = [\"migrations_internals\", \"proc-macro2 1.0.86\", \"quote 1.0.37\"]\n\n[[package]]\nname = \"mime\"\nversion = \"0.3.17\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a\"\n\n[[package]]\nname = \"minicov\"\nversion = \"0.3.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169\"\ndependencies = [\"cc\", \"walkdir\"]\n\n[[package]]\nname = \"miniz_oxide\"\nversion = \"0.7.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08\"\ndependencies = [\"adler\", \"simd-adler32\"]\n\n[[package]]\nname = \"miniz_oxide\"\nversion = \"0.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1\"\ndependencies = [\"adler2\"]\n\n[[package]]\nname = \"mio\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec\"\ndependencies = [\"hermit-abi 0.3.9\", \"libc\", \"wasi 0.11.0+wasi-snapshot-preview1\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"nanorand\"\nversion = \"0.7.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3\"\ndependencies = [\"getrandom 0.2.15\"]\n\n[[package]]\nname = \"native-tls\"\nversion = \"0.2.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466\"\ndependencies = [\"libc\", \"log\", \"openssl\", \"openssl-probe\", \"openssl-sys\", \"schannel\", \"security-framework\", \"security-framework-sys\", \"tempfile\"]\n\n[[package]]\nname = \"nu-ansi-term\"\nversion = \"0.46.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84\"\ndependencies = [\"overload\", \"winapi\"]\n\n[[package]]\nname = \"num-conv\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9\"\n\n[[package]]\nname = \"num-traits\"\nversion = \"0.2.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\"\ndependencies = [\"autocfg\"]\n\n[[package]]\nname = \"object\"\nversion = \"0.36.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"once_cell\"\nversion = \"1.20.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe\"\n\n[[package]]\nname = \"opaque-debug\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381\"\n\n[[package]]\nname = \"openssl\"\nversion = \"0.10.66\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1\"\ndependencies = [\"bitflags 2.6.0\", \"cfg-if\", \"foreign-types\", \"libc\", \"once_cell\", \"openssl-macros\", \"openssl-sys\"]\n\n[[package]]\nname = \"openssl-macros\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"openssl-probe\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf\"\n\n[[package]]\nname = \"openssl-sys\"\nversion = \"0.9.103\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6\"\ndependencies = [\"cc\", \"libc\", \"pkg-config\", \"vcpkg\"]\n\n[[package]]\nname = \"os_str_bytes\"\nversion = \"6.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1\"\n\n[[package]]\nname = \"overload\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39\"\n\n[[package]]\nname = \"parking\"\nversion = \"2.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba\"\n\n[[package]]\nname = \"parking_lot\"\nversion = \"0.12.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27\"\ndependencies = [\"lock_api\", \"parking_lot_core\"]\n\n[[package]]\nname = \"parking_lot_core\"\nversion = \"0.9.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8\"\ndependencies = [\"cfg-if\", \"libc\", \"redox_syscall\", \"smallvec\", \"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"percent-encoding\"\nversion = \"2.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e\"\n\n[[package]]\nname = \"pin-project-lite\"\nversion = \"0.2.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02\"\n\n[[package]]\nname = \"pin-utils\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184\"\n\n[[package]]\nname = \"piper\"\nversion = \"0.2.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066\"\ndependencies = [\"atomic-waker\", \"fastrand\", \"futures-io\"]\n\n[[package]]\nname = \"pkg-config\"\nversion = \"0.3.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n\n[[package]]\nname = \"png\"\nversion = \"0.17.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1\"\ndependencies = [\"bitflags 1.3.2\", \"crc32fast\", \"fdeflate\", \"flate2\", \"miniz_oxide 0.7.4\"]\n\n[[package]]\nname = \"polling\"\nversion = \"3.7.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511\"\ndependencies = [\"cfg-if\", \"concurrent-queue\", \"hermit-abi 0.4.0\", \"pin-project-lite\", \"rustix\", \"tracing\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"poly1305\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4b7456bc1ad2d4cf82b3a016be4c2ac48daf11bf990c1603ebd447fe6f30fca8\"\ndependencies = [\"cpuid-bool\", \"universal-hash 0.4.0\"]\n\n[[package]]\nname = \"poly1305\"\nversion = \"0.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf\"\ndependencies = [\"cpufeatures\", \"opaque-debug\", \"universal-hash 0.5.1\"]\n\n[[package]]\nname = \"powerfmt\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391\"\n\n[[package]]\nname = \"ppv-lite86\"\nversion = \"0.2.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04\"\ndependencies = [\"zerocopy\"]\n\n[[package]]\nname = \"pretty_assertions\"\nversion = \"1.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66\"\ndependencies = [\"diff\", \"yansi\"]\n\n[[package]]\nname = \"proc-macro-error\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c\"\ndependencies = [\"proc-macro-error-attr\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 1.0.109\", \"version_check\"]\n\n[[package]]\nname = \"proc-macro-error-attr\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"version_check\"]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"0.4.30\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759\"\ndependencies = [\"unicode-xid\"]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"1.0.86\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77\"\ndependencies = [\"unicode-ident\"]\n\n[[package]]\nname = \"qoi\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001\"\ndependencies = [\"bytemuck\"]\n\n[[package]]\nname = \"qrcode-generator\"\nversion = \"4.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1d06cb9646c7a14096231a2474d7f21e5e8c13de090c68d13bde6157cfe7f159\"\ndependencies = [\"html-escape\", \"image\", \"qrcodegen\"]\n\n[[package]]\nname = \"qrcodegen\"\nversion = \"1.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142\"\n\n[[package]]\nname = \"quote\"\nversion = \"0.6.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1\"\ndependencies = [\"proc-macro2 0.4.30\"]\n\n[[package]]\nname = \"quote\"\nversion = \"1.0.37\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af\"\ndependencies = [\"proc-macro2 1.0.86\"]\n\n[[package]]\nname = \"rand\"\nversion = \"0.7.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03\"\ndependencies = [\"getrandom 0.1.16\", \"libc\", \"rand_chacha 0.2.2\", \"rand_core 0.5.1\", \"rand_hc\"]\n\n[[package]]\nname = \"rand\"\nversion = \"0.8.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404\"\ndependencies = [\"libc\", \"rand_chacha 0.3.1\", \"rand_core 0.6.4\"]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402\"\ndependencies = [\"ppv-lite86\", \"rand_core 0.5.1\"]\n\n[[package]]\nname = \"rand_chacha\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88\"\ndependencies = [\"ppv-lite86\", \"rand_core 0.6.4\"]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19\"\ndependencies = [\"getrandom 0.1.16\"]\n\n[[package]]\nname = \"rand_core\"\nversion = \"0.6.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c\"\ndependencies = [\"getrandom 0.2.15\"]\n\n[[package]]\nname = \"rand_hc\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c\"\ndependencies = [\"rand_core 0.5.1\"]\n\n[[package]]\nname = \"rayon\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa\"\ndependencies = [\"either\", \"rayon-core\"]\n\n[[package]]\nname = \"rayon-core\"\nversion = \"1.12.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2\"\ndependencies = [\"crossbeam-deque\", \"crossbeam-utils\"]\n\n[[package]]\nname = \"redox_syscall\"\nversion = \"0.5.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853\"\ndependencies = [\"bitflags 2.6.0\"]\n\n[[package]]\nname = \"regex\"\nversion = \"1.10.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619\"\ndependencies = [\"aho-corasick\", \"memchr\", \"regex-automata 0.4.7\", \"regex-syntax 0.8.4\"]\n\n[[package]]\nname = \"regex-automata\"\nversion = \"0.1.10\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132\"\ndependencies = [\"regex-syntax 0.6.29\"]\n\n[[package]]\nname = \"regex-automata\"\nversion = \"0.4.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df\"\ndependencies = [\"aho-corasick\", \"memchr\", \"regex-syntax 0.8.4\"]\n\n[[package]]\nname = \"regex-syntax\"\nversion = \"0.6.29\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1\"\n\n[[package]]\nname = \"regex-syntax\"\nversion = \"0.8.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b\"\n\n[[package]]\nname = \"reqwest\"\nversion = \"0.11.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62\"\ndependencies = [\"base64 0.21.7\", \"bytes\", \"encoding_rs\", \"futures-core\", \"futures-util\", \"h2\", \"http\", \"http-body\", \"hyper\", \"hyper-tls\", \"ipnet\", \"js-sys\", \"log\", \"mime\", \"native-tls\", \"once_cell\", \"percent-encoding\", \"pin-project-lite\", \"rustls-pemfile\", \"serde\", \"serde_json\", \"serde_urlencoded\", \"sync_wrapper\", \"system-configuration\", \"tokio\", \"tokio-native-tls\", \"tower-service\", \"url\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"web-sys\", \"winreg\"]\n\n[[package]]\nname = \"rexie\"\nversion = \"0.6.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"887466cfa8a12c08ee4b174998135cea8ff0fd84858627cd793e56535a045bc9\"\ndependencies = [\"idb\", \"thiserror\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"rqrr\"\nversion = \"0.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"096fe5b00d2859cd111094b3bd41da44aee2a9b14e9bacd673e53349f461b129\"\ndependencies = [\"g2p\", \"image\", \"lru\"]\n\n[[package]]\nname = \"rustc-demangle\"\nversion = \"0.1.24\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f\"\n\n[[package]]\nname = \"rustix\"\nversion = \"0.38.37\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811\"\ndependencies = [\"bitflags 2.6.0\", \"errno\", \"libc\", \"linux-raw-sys\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"rustls-pemfile\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c\"\ndependencies = [\"base64 0.21.7\"]\n\n[[package]]\nname = \"ryu\"\nversion = \"1.0.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f\"\n\n[[package]]\nname = \"salsa20\"\nversion = \"0.7.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"399f290ffc409596022fce5ea5d4138184be4784f2b28c62c59f0d8389059a15\"\ndependencies = [\"cipher 0.2.5\", \"zeroize\"]\n\n[[package]]\nname = \"salsa20\"\nversion = \"0.10.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213\"\ndependencies = [\"cipher 0.4.4\"]\n\n[[package]]\nname = \"same-file\"\nversion = \"1.0.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502\"\ndependencies = [\"winapi-util\"]\n\n[[package]]\nname = \"schannel\"\nversion = \"0.1.24\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b\"\ndependencies = [\"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"scoped-tls\"\nversion = \"1.0.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294\"\n\n[[package]]\nname = \"scopeguard\"\nversion = \"1.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49\"\n\n[[package]]\nname = \"security-framework\"\nversion = \"2.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02\"\ndependencies = [\"bitflags 2.6.0\", \"core-foundation\", \"core-foundation-sys\", \"libc\", \"security-framework-sys\"]\n\n[[package]]\nname = \"security-framework-sys\"\nversion = \"2.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.210\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a\"\ndependencies = [\"serde_derive\"]\n\n[[package]]\nname = \"serde-big-array\"\nversion = \"0.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3323f09a748af288c3dc2474ea6803ee81f118321775bffa3ac8f7e65c5e90e7\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde-wasm-bindgen\"\nversion = \"0.6.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b\"\ndependencies = [\"js-sys\", \"serde\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"serde_bytes\"\nversion = \"0.11.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde_derive\"\nversion = \"1.0.210\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"serde_json\"\nversion = \"1.0.128\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8\"\ndependencies = [\"itoa\", \"memchr\", \"ryu\", \"serde\"]\n\n[[package]]\nname = \"serde_spanned\"\nversion = \"0.6.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"serde_urlencoded\"\nversion = \"0.7.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd\"\ndependencies = [\"form_urlencoded\", \"itoa\", \"ryu\", \"serde\"]\n\n[[package]]\nname = \"serde_yaml\"\nversion = \"0.9.34+deprecated\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47\"\ndependencies = [\"indexmap 2.5.0\", \"itoa\", \"ryu\", \"serde\", \"unsafe-libyaml\"]\n\n[[package]]\nname = \"sha2\"\nversion = \"0.9.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800\"\ndependencies = [\"block-buffer 0.9.0\", \"cfg-if\", \"cpufeatures\", \"digest 0.9.0\", \"opaque-debug\"]\n\n[[package]]\nname = \"sha2\"\nversion = \"0.10.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8\"\ndependencies = [\"cfg-if\", \"cpufeatures\", \"digest 0.10.7\"]\n\n[[package]]\nname = \"shamirsecretsharing\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6c0df585cf14446dc081c77e9f3a56a9c5d79755e875b165d2d15e4cdf357972\"\ndependencies = [\"cc\", \"rand 0.8.5\", \"xsalsa20poly1305 0.6.0\"]\n\n[[package]]\nname = \"sharded-slab\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6\"\ndependencies = [\"lazy_static\"]\n\n[[package]]\nname = \"shlex\"\nversion = \"1.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n\n[[package]]\nname = \"signal-hook-registry\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1\"\ndependencies = [\"libc\"]\n\n[[package]]\nname = \"signature\"\nversion = \"1.6.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c\"\n\n[[package]]\nname = \"simd-adler32\"\nversion = \"0.3.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe\"\n\n[[package]]\nname = \"slab\"\nversion = \"0.4.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67\"\ndependencies = [\"autocfg\"]\n\n[[package]]\nname = \"smallvec\"\nversion = \"1.13.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67\"\n\n[[package]]\nname = \"socket2\"\nversion = \"0.5.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c\"\ndependencies = [\"libc\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"spin\"\nversion = \"0.9.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67\"\ndependencies = [\"lock_api\"]\n\n[[package]]\nname = \"strsim\"\nversion = \"0.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623\"\n\n[[package]]\nname = \"strsim\"\nversion = \"0.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\"\n\n[[package]]\nname = \"subtle\"\nversion = \"2.6.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292\"\n\n[[package]]\nname = \"syn\"\nversion = \"0.15.44\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5\"\ndependencies = [\"proc-macro2 0.4.30\", \"quote 0.6.13\", \"unicode-xid\"]\n\n[[package]]\nname = \"syn\"\nversion = \"1.0.109\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"unicode-ident\"]\n\n[[package]]\nname = \"syn\"\nversion = \"2.0.77\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"unicode-ident\"]\n\n[[package]]\nname = \"sync_wrapper\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160\"\n\n[[package]]\nname = \"system-configuration\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7\"\ndependencies = [\"bitflags 1.3.2\", \"core-foundation\", \"system-configuration-sys\"]\n\n[[package]]\nname = \"system-configuration-sys\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9\"\ndependencies = [\"core-foundation-sys\", \"libc\"]\n\n[[package]]\nname = \"tempfile\"\nversion = \"3.12.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64\"\ndependencies = [\"cfg-if\", \"fastrand\", \"once_cell\", \"rustix\", \"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"termcolor\"\nversion = \"1.4.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755\"\ndependencies = [\"winapi-util\"]\n\n[[package]]\nname = \"textwrap\"\nversion = \"0.16.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9\"\n\n[[package]]\nname = \"thiserror\"\nversion = \"1.0.63\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724\"\ndependencies = [\"thiserror-impl\"]\n\n[[package]]\nname = \"thiserror-impl\"\nversion = \"1.0.63\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"thread_local\"\nversion = \"1.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c\"\ndependencies = [\"cfg-if\", \"once_cell\"]\n\n[[package]]\nname = \"tiff\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e\"\ndependencies = [\"flate2\", \"jpeg-decoder\", \"weezl\"]\n\n[[package]]\nname = \"time\"\nversion = \"0.3.36\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885\"\ndependencies = [\"deranged\", \"itoa\", \"num-conv\", \"powerfmt\", \"serde\", \"time-core\", \"time-macros\"]\n\n[[package]]\nname = \"time-core\"\nversion = \"0.1.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3\"\n\n[[package]]\nname = \"time-macros\"\nversion = \"0.2.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf\"\ndependencies = [\"num-conv\", \"time-core\"]\n\n[[package]]\nname = \"tinyvec\"\nversion = \"1.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938\"\ndependencies = [\"tinyvec_macros\"]\n\n[[package]]\nname = \"tinyvec_macros\"\nversion = \"0.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20\"\n\n[[package]]\nname = \"tokio\"\nversion = \"1.40.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998\"\ndependencies = [\"backtrace\", \"bytes\", \"libc\", \"mio\", \"parking_lot\", \"pin-project-lite\", \"signal-hook-registry\", \"socket2\", \"tokio-macros\", \"windows-sys 0.52.0\"]\n\n[[package]]\nname = \"tokio-macros\"\nversion = \"2.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"tokio-native-tls\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2\"\ndependencies = [\"native-tls\", \"tokio\"]\n\n[[package]]\nname = \"tokio-util\"\nversion = \"0.7.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a\"\ndependencies = [\"bytes\", \"futures-core\", \"futures-sink\", \"pin-project-lite\", \"tokio\"]\n\n[[package]]\nname = \"toml\"\nversion = \"0.8.19\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e\"\ndependencies = [\"serde\", \"serde_spanned\", \"toml_datetime\", \"toml_edit\"]\n\n[[package]]\nname = \"toml_datetime\"\nversion = \"0.6.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41\"\ndependencies = [\"serde\"]\n\n[[package]]\nname = \"toml_edit\"\nversion = \"0.22.20\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d\"\ndependencies = [\"indexmap 2.5.0\", \"serde\", \"serde_spanned\", \"toml_datetime\", \"winnow\"]\n\n[[package]]\nname = \"tower-service\"\nversion = \"0.3.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3\"\n\n[[package]]\nname = \"tracing\"\nversion = \"0.1.40\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef\"\ndependencies = [\"pin-project-lite\", \"tracing-attributes\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-attributes\"\nversion = \"0.1.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"tracing-core\"\nversion = \"0.1.32\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54\"\ndependencies = [\"once_cell\", \"valuable\"]\n\n[[package]]\nname = \"tracing-log\"\nversion = \"0.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3\"\ndependencies = [\"log\", \"once_cell\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-serde\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1\"\ndependencies = [\"serde\", \"tracing-core\"]\n\n[[package]]\nname = \"tracing-subscriber\"\nversion = \"0.3.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b\"\ndependencies = [\"matchers\", \"nu-ansi-term\", \"once_cell\", \"regex\", \"serde\", \"serde_json\", \"sharded-slab\", \"smallvec\", \"thread_local\", \"time\", \"tracing\", \"tracing-core\", \"tracing-log\", \"tracing-serde\"]\n\n[[package]]\nname = \"tracing-web\"\nversion = \"0.1.3\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9e6a141feebd51f8d91ebfd785af50fca223c570b86852166caa3b141defe7c\"\ndependencies = [\"js-sys\", \"tracing-core\", \"tracing-subscriber\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"try-lock\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b\"\n\n[[package]]\nname = \"typenum\"\nversion = \"1.17.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825\"\n\n[[package]]\nname = \"unicode-bidi\"\nversion = \"0.3.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75\"\n\n[[package]]\nname = \"unicode-ident\"\nversion = \"1.0.13\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe\"\n\n[[package]]\nname = \"unicode-normalization\"\nversion = \"0.1.23\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5\"\ndependencies = [\"tinyvec\"]\n\n[[package]]\nname = \"unicode-xid\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc\"\n\n[[package]]\nname = \"universal-hash\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402\"\ndependencies = [\"generic-array\", \"subtle\"]\n\n[[package]]\nname = \"universal-hash\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea\"\ndependencies = [\"crypto-common\", \"subtle\"]\n\n[[package]]\nname = \"unsafe-libyaml\"\nversion = \"0.2.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861\"\n\n[[package]]\nname = \"url\"\nversion = \"2.5.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c\"\ndependencies = [\"form_urlencoded\", \"idna\", \"percent-encoding\"]\n\n[[package]]\nname = \"utf8-width\"\nversion = \"0.1.7\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3\"\n\n[[package]]\nname = \"uuid\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314\"\ndependencies = [\"getrandom 0.2.15\", \"rand 0.8.5\", \"uuid-macro-internal\"]\n\n[[package]]\nname = \"uuid-macro-internal\"\nversion = \"1.10.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"valuable\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d\"\n\n[[package]]\nname = \"value-bag\"\nversion = \"1.9.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101\"\n\n[[package]]\nname = \"vcpkg\"\nversion = \"0.2.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n\n[[package]]\nname = \"version_check\"\nversion = \"0.9.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a\"\n\n[[package]]\nname = \"walkdir\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b\"\ndependencies = [\"same-file\", \"winapi-util\"]\n\n[[package]]\nname = \"want\"\nversion = \"0.3.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e\"\ndependencies = [\"try-lock\"]\n\n[[package]]\nname = \"wasi\"\nversion = \"0.9.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519\"\n\n[[package]]\nname = \"wasi\"\nversion = \"0.11.0+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423\"\n\n[[package]]\nname = \"wasm-bindgen\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5\"\ndependencies = [\"cfg-if\", \"once_cell\", \"serde\", \"serde_json\", \"wasm-bindgen-macro\"]\n\n[[package]]\nname = \"wasm-bindgen-backend\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b\"\ndependencies = [\"bumpalo\", \"log\", \"once_cell\", \"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\", \"wasm-bindgen-shared\"]\n\n[[package]]\nname = \"wasm-bindgen-futures\"\nversion = \"0.4.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed\"\ndependencies = [\"cfg-if\", \"js-sys\", \"wasm-bindgen\", \"web-sys\"]\n\n[[package]]\nname = \"wasm-bindgen-macro\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf\"\ndependencies = [\"quote 1.0.37\", \"wasm-bindgen-macro-support\"]\n\n[[package]]\nname = \"wasm-bindgen-macro-support\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\", \"wasm-bindgen-backend\", \"wasm-bindgen-shared\"]\n\n[[package]]\nname = \"wasm-bindgen-shared\"\nversion = \"0.2.93\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484\"\n\n[[package]]\nname = \"wasm-bindgen-test\"\nversion = \"0.3.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9\"\ndependencies = [\"console_error_panic_hook\", \"js-sys\", \"minicov\", \"scoped-tls\", \"wasm-bindgen\", \"wasm-bindgen-futures\", \"wasm-bindgen-test-macro\"]\n\n[[package]]\nname = \"wasm-bindgen-test-macro\"\nversion = \"0.3.43\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"web-sys\"\nversion = \"0.3.70\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0\"\ndependencies = [\"js-sys\", \"wasm-bindgen\"]\n\n[[package]]\nname = \"weezl\"\nversion = \"0.1.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082\"\n\n[[package]]\nname = \"winapi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419\"\ndependencies = [\"winapi-i686-pc-windows-gnu\", \"winapi-x86_64-pc-windows-gnu\"]\n\n[[package]]\nname = \"winapi-i686-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6\"\n\n[[package]]\nname = \"winapi-util\"\nversion = \"0.1.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb\"\ndependencies = [\"windows-sys 0.59.0\"]\n\n[[package]]\nname = \"winapi-x86_64-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f\"\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.48.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9\"\ndependencies = [\"windows-targets 0.48.5\"]\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.52.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d\"\ndependencies = [\"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.59.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b\"\ndependencies = [\"windows-targets 0.52.6\"]\n\n[[package]]\nname = \"windows-targets\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c\"\ndependencies = [\"windows_aarch64_gnullvm 0.48.5\", \"windows_aarch64_msvc 0.48.5\", \"windows_i686_gnu 0.48.5\", \"windows_i686_msvc 0.48.5\", \"windows_x86_64_gnu 0.48.5\", \"windows_x86_64_gnullvm 0.48.5\", \"windows_x86_64_msvc 0.48.5\"]\n\n[[package]]\nname = \"windows-targets\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973\"\ndependencies = [\"windows_aarch64_gnullvm 0.52.6\", \"windows_aarch64_msvc 0.52.6\", \"windows_i686_gnu 0.52.6\", \"windows_i686_gnullvm\", \"windows_i686_msvc 0.52.6\", \"windows_x86_64_gnu 0.52.6\", \"windows_x86_64_gnullvm 0.52.6\", \"windows_x86_64_msvc 0.52.6\"]\n\n[[package]]\nname = \"windows_aarch64_gnullvm\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8\"\n\n[[package]]\nname = \"windows_aarch64_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3\"\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc\"\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b\"\n\n[[package]]\nname = \"windows_i686_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78\"\n\n[[package]]\nname = \"windows_x86_64_gnullvm\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc\"\n\n[[package]]\nname = \"windows_x86_64_gnullvm\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.48.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.52.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec\"\n\n[[package]]\nname = \"winnow\"\nversion = \"0.6.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f\"\ndependencies = [\"memchr\"]\n\n[[package]]\nname = \"winreg\"\nversion = \"0.50.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1\"\ndependencies = [\"cfg-if\", \"windows-sys 0.48.0\"]\n\n[[package]]\nname = \"x25519-dalek\"\nversion = \"1.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f\"\ndependencies = [\"curve25519-dalek\", \"rand_core 0.5.1\", \"zeroize\"]\n\n[[package]]\nname = \"xsalsa20poly1305\"\nversion = \"0.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0304c336e98d753428f7b3d8899d60b8a87a961ef50bdfc44af0c1bea2651ce5\"\ndependencies = [\"aead 0.3.2\", \"poly1305 0.6.2\", \"rand_core 0.5.1\", \"salsa20 0.7.2\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"xsalsa20poly1305\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7\"\ndependencies = [\"aead 0.5.2\", \"poly1305 0.8.0\", \"salsa20 0.10.2\", \"subtle\", \"zeroize\"]\n\n[[package]]\nname = \"yansi\"\nversion = \"0.5.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec\"\n\n[[package]]\nname = \"zerocopy\"\nversion = \"0.7.35\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0\"\ndependencies = [\"byteorder\", \"zerocopy-derive\"]\n\n[[package]]\nname = \"zerocopy-derive\"\nversion = \"0.7.35\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"zeroize\"\nversion = \"1.8.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde\"\ndependencies = [\"zeroize_derive\"]\n\n[[package]]\nname = \"zeroize_derive\"\nversion = \"1.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69\"\ndependencies = [\"proc-macro2 1.0.86\", \"quote 1.0.37\", \"syn 2.0.77\"]\n\n[[package]]\nname = \"zune-inflate\"\nversion = \"0.2.54\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02\"\ndependencies = [\"simd-adler32\"]\n","rust_toolchain_file":null}} \ No newline at end of file diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 18414a41..efcb8138 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -30,16 +30,16 @@ tracing-subscriber = { workspace = true, default-features = false, features = [" getrandom.workspace = true # Wasm dependencies -tracing-web = "0.1.2" +tracing-web = "0.1.3" -wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.93", features = ["serde-serialize"] } rexie = "0.6.2" -serde-wasm-bindgen = "0.6.0" -wasm-bindgen-futures = "0.4.37" +serde-wasm-bindgen = "0.6.5" +wasm-bindgen-futures = "0.4.43" -js-sys = { version = "0.3.63" } -web-sys = { version = "0.3.60", features = [ +js-sys = { version = "0.3.70" } +web-sys = { version = "0.3.70", features = [ "DomException", "DomStringList", "Event", @@ -54,7 +54,7 @@ web-sys = { version = "0.3.60", features = [ console_error_panic_hook = { version = "0.1.7", optional = true } [dev-dependencies] -wasm-bindgen-test = "0.3.37" +wasm-bindgen-test = "0.3.43" [target.'cfg(target_arch = "wasm32")'.dependencies] meta-secret-core = { path = "../core" } \ No newline at end of file diff --git a/wasm/src/app_manager.rs b/wasm/src/app_manager.rs index 4ed01c00..229a500b 100644 --- a/wasm/src/app_manager.rs +++ b/wasm/src/app_manager.rs @@ -1,56 +1,85 @@ +use std::cmp::PartialEq; +use anyhow::Context; use std::sync::Arc; - use tracing::{info, instrument, Instrument}; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen_futures::spawn_local; -use meta_secret_core::node::app::app_state_update_manager::{ - ApplicationManagerConfigurator -}; +use meta_secret_core::node::app::app_state_update_manager::ApplicationManagerConfigurator; -use meta_secret_core::node::app::meta_app::meta_client_service::{MetaClientAccessProxy, MetaClientDataTransfer, MetaClientService, MetaClientStateProvider}; +use meta_secret_core::node::app::meta_app::meta_client_service::{ + MetaClientAccessProxy, MetaClientDataTransfer, MetaClientService, MetaClientStateProvider, +}; use meta_secret_core::node::app::sync_gateway::SyncGateway; use meta_secret_core::node::app::virtual_device::VirtualDevice; use meta_secret_core::node::common::data_transfer::MpscDataTransfer; use meta_secret_core::node::common::meta_tracing::{client_span, server_span, vd_span}; -use meta_secret_core::node::common::model::ApplicationState; -use meta_secret_core::node::common::model::device::DeviceName; -use meta_secret_core::node::common::model::user::{UserDataOutsider, UserDataOutsiderStatus}; use meta_secret_core::node::common::model::vault::{VaultName, VaultStatus}; +use meta_secret_core::node::common::model::ApplicationState; +use meta_secret_core::node::common::model::device::common::DeviceName; +use meta_secret_core::node::common::model::user::common::UserDataOutsiderStatus; use meta_secret_core::node::db::objects::persistent_object::PersistentObject; -use meta_secret_core::node::db::repo::credentials_repo::CredentialsRepo; +use meta_secret_core::node::db::repo::persistent_credentials::PersistentCredentials; use meta_secret_core::node::db::repo::generic_db::KvLogEventRepo; use meta_secret_core::node::server::server_app::{ServerApp, ServerDataTransfer}; #[wasm_bindgen] pub struct WasmApplicationState { - inner: ApplicationState + inner: ApplicationState, } impl From for WasmApplicationState { fn from(state: ApplicationState) -> Self { - WasmApplicationState { - inner: state - } + WasmApplicationState { inner: state } } } #[wasm_bindgen] impl WasmApplicationState { - pub fn is_new_user(&self) -> bool { - let stt = match self.inner { - ApplicationState::Empty => "empty", - ApplicationState::Local { .. } => "local", - ApplicationState::User { .. } => "user", - ApplicationState::Vault { .. } => "vault", - }; - info!("Is new user: {:?}", stt); - return true; + + pub fn status(&self) -> WasmWebAppStatus { + WasmWebAppStatus::from(&self.inner) } - pub fn is_empty_env(&self) -> bool { - return true; + pub fn is_new_user(&self) -> bool { + let status = self.status(); + let is_local = matches!(status, WasmWebAppStatus::LocalEnv); + let vault_not_exists = matches!(status, WasmWebAppStatus::VaultNotExists); + + is_local || vault_not_exists + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum WasmWebAppStatus { + LocalEnv, + VaultNotExists, + NonMember, + Pending, + Declined, + Member +} + +impl WasmWebAppStatus { + fn from(app_state: &ApplicationState) -> WasmWebAppStatus { + match app_state { + ApplicationState::Local { .. } => WasmWebAppStatus::LocalEnv, + ApplicationState::Vault { vault } => { + match vault { + VaultStatus::NotExists(_) => WasmWebAppStatus::VaultNotExists, + VaultStatus::Outsider(outsider) => { + match outsider.status { + UserDataOutsiderStatus::NonMember => WasmWebAppStatus::NonMember, + UserDataOutsiderStatus::Pending => WasmWebAppStatus::Pending, + UserDataOutsiderStatus::Declined => WasmWebAppStatus::Declined + } + } + VaultStatus::Member { .. } => WasmWebAppStatus::Member + } + } + } } } @@ -85,13 +114,12 @@ impl ApplicationManager { }); Self::server_setup(cfg.server_repo, server_dt.clone()).await?; - - let state_provider = Arc::new(MetaClientStateProvider::new()); - Self::virtual_device_setup(cfg.device_repo, server_dt.clone(), state_provider.clone()).await?; + Self::virtual_device_setup(cfg.device_repo, server_dt.clone()) + .await?; let app_manager = - Self::client_setup(cfg.client_repo, server_dt.clone(), state_provider).await?; + Self::client_setup(cfg.client_repo, server_dt.clone()).await?; Ok(app_manager) } @@ -99,8 +127,7 @@ impl ApplicationManager { #[instrument(name = "MetaClient", skip_all)] pub async fn client_setup( client_repo: Arc, - dt: Arc, - app_state_provider: Arc + dt: Arc ) -> anyhow::Result> { let persistent_obj = { let obj = PersistentObject::new(client_repo.clone()); @@ -109,31 +136,30 @@ impl ApplicationManager { let sync_gateway = Arc::new(SyncGateway { id: String::from("client-gateway"), - persistent_object: persistent_obj.clone(), + p_obj: persistent_obj.clone(), server_dt: dt.clone(), }); + let state_provider = Arc::new(MetaClientStateProvider::new()); + let meta_client_service = { Arc::new(MetaClientService { data_transfer: Arc::new(MetaClientDataTransfer { dt: MpscDataTransfer::new(), }), sync_gateway: sync_gateway.clone(), - state_provider: app_state_provider + state_provider }) }; - let app_manager = ApplicationManager::new( - dt, - sync_gateway, - meta_client_service.clone(), - ); + let app_manager = ApplicationManager::new(dt, sync_gateway, meta_client_service.clone()); spawn_local(async move { meta_client_service .run() .instrument(client_span()) .await + .with_context(|| "Meta client error") .unwrap(); }); @@ -147,18 +173,17 @@ impl ApplicationManager { pub async fn virtual_device_setup( device_repo: Arc, dt: Arc, - app_state_provider: Arc ) -> anyhow::Result<()> { - info!("Device initialization"); + info!("virtual device initialization"); let persistent_object = Arc::new(PersistentObject::new(device_repo.clone())); - let creds_repo = CredentialsRepo { + let creds_repo = PersistentCredentials { p_obj: persistent_object.clone(), }; let _user_creds = creds_repo - .get_or_generate_user_creds(DeviceName::from("virtual-device"), VaultName::from("q")) + .get_or_generate_user_creds(DeviceName::virtual_device(), VaultName::test()) .await?; let dt_meta_client = Arc::new(MetaClientDataTransfer { @@ -167,16 +192,15 @@ impl ApplicationManager { let gateway = Arc::new(SyncGateway { id: String::from("vd-gateway"), - persistent_object: persistent_object.clone(), + p_obj: persistent_object.clone(), server_dt: dt.clone(), }); - let meta_client_service = { - MetaClientService { - data_transfer: dt_meta_client.clone(), - sync_gateway: gateway.clone(), - state_provider: app_state_provider - } + let state_provider = Arc::new(MetaClientStateProvider::new()); + let meta_client_service = MetaClientService { + data_transfer: dt_meta_client.clone(), + sync_gateway: gateway.clone(), + state_provider }; spawn_local(async move { @@ -188,8 +212,8 @@ impl ApplicationManager { }); let meta_client_access_proxy = Arc::new(MetaClientAccessProxy { dt: dt_meta_client }); - let vd = - VirtualDevice::init(persistent_object, meta_client_access_proxy, dt, gateway).await?; + let vd = VirtualDevice::init(persistent_object, meta_client_access_proxy, dt, gateway) + .await?; let vd = Arc::new(vd); spawn_local(async move { vd.run().instrument(vd_span()).await.unwrap() }); @@ -204,10 +228,9 @@ impl ApplicationManager { info!("Server initialization"); spawn_local(async move { - ServerApp::init(server_repo.clone()) - .await + ServerApp::new(server_repo.clone(), server_dt) .unwrap() - .run(server_dt) + .run() .instrument(server_span()) .await .unwrap() diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 7833f74e..29d33e13 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -4,7 +4,6 @@ use meta_secret_core::recover_from_shares; use meta_secret_core::secret::data_block::common::SharedSecretConfig; use meta_secret_core::secret::shared_secret::{PlainText, SharedSecretEncryption, UserShareDto}; use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; use wasm_bindgen::prelude::*; pub mod app_manager; @@ -15,6 +14,7 @@ pub mod wasm_repo; use tracing_subscriber::fmt::format::Pretty; use tracing_subscriber::fmt::time::UtcTime; +use tracing_subscriber::util::SubscriberInitExt; use tracing_web::{performance_layer, MakeConsoleWriter}; /// Json utilities https://github.com/rustwasm/wasm-bindgen/blob/main/crates/js-sys/tests/wasm/JSON.rs @@ -25,7 +25,7 @@ extern "C" { #[wasm_bindgen(js_namespace = console)] pub fn debug(s: &str); - + #[wasm_bindgen(js_namespace = console)] pub fn info(s: &str); @@ -41,9 +41,9 @@ pub fn configure() { utils::set_panic_hook(); let fmt_layer = tracing_subscriber::fmt::layer() + .json() .without_time() .with_ansi(false) - //.with_max_level(Level::INFO) .pretty() // Only partially supported across browsers .with_timer(UtcTime::rfc_3339()) // std::time is not available in browsers .with_writer(MakeConsoleWriter); // write events to the console diff --git a/wasm/src/wasm_app_manager.rs b/wasm/src/wasm_app_manager.rs index 9fd760c0..86acbe99 100644 --- a/wasm/src/wasm_app_manager.rs +++ b/wasm/src/wasm_app_manager.rs @@ -1,19 +1,17 @@ use std::sync::Arc; -use async_trait::async_trait; use tracing::info; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use crate::app_manager::{ApplicationManager, WasmApplicationState}; -use crate::wasm_repo::WasmRepo; use crate::configure; -use meta_secret_core::node::app::app_state_update_manager::{ - ApplicationManagerConfigurator, -}; +use crate::wasm_repo::WasmRepo; +use meta_secret_core::node::app::app_state_update_manager::ApplicationManagerConfigurator; use meta_secret_core::node::app::meta_app::messaging::{ ClusterDistributionRequest, GenericAppStateRequest, }; +use meta_secret_core::node::common::model::vault::VaultName; #[wasm_bindgen] pub struct WasmApplicationManager { @@ -48,7 +46,7 @@ impl WasmApplicationManager { let cfg = ApplicationManagerConfigurator { client_repo: Arc::new(WasmRepo::default().await), server_repo: Arc::new(WasmRepo::server().await), - device_repo: Arc::new(WasmRepo::virtual_device().await) + device_repo: Arc::new(WasmRepo::virtual_device().await), }; let app_manager = ApplicationManager::init(cfg) @@ -68,7 +66,7 @@ impl WasmApplicationManager { let cfg = ApplicationManagerConfigurator { client_repo: Arc::new(WasmRepo::default().await), server_repo: Arc::new(WasmRepo::server().await), - device_repo: Arc::new(WasmRepo::virtual_device().await) + device_repo: Arc::new(WasmRepo::virtual_device().await), }; let app_manager = ApplicationManager::init(cfg) @@ -79,9 +77,10 @@ impl WasmApplicationManager { app_manager: GenericApplicationManager::Wasm { app_manager }, } } - + pub async fn get_state(&self) -> WasmApplicationState { - let app_state = self.app_manager + let app_state = self + .app_manager .get_app_manager() .meta_client_service .state_provider @@ -90,23 +89,17 @@ impl WasmApplicationManager { WasmApplicationState::from(app_state) } - pub async fn sign_up(&self) { + pub async fn sign_up(&self, vault_name: String) { info!("Sign Up"); - let sign_up = GenericAppStateRequest::SignUp; + let sign_up = GenericAppStateRequest::SignUp(VaultName::from(vault_name.as_str())); match &self.app_manager { GenericApplicationManager::Wasm { app_manager } => { - app_manager - .meta_client_service - .send_request(sign_up) - .await; + app_manager.meta_client_service.send_request(sign_up).await; } GenericApplicationManager::InMem { app_manager } => { - app_manager - .meta_client_service - .send_request(sign_up) - .await; + app_manager.meta_client_service.send_request(sign_up).await; } }; } @@ -119,16 +112,10 @@ impl WasmApplicationManager { match &self.app_manager { GenericApplicationManager::Wasm { app_manager } => { - app_manager - .meta_client_service - .send_request(request) - .await; + app_manager.meta_client_service.send_request(request).await; } GenericApplicationManager::InMem { app_manager } => { - app_manager - .meta_client_service - .send_request(request) - .await; + app_manager.meta_client_service.send_request(request).await; } }; } @@ -140,16 +127,10 @@ impl WasmApplicationManager { match &self.app_manager { GenericApplicationManager::Wasm { app_manager } => { - app_manager - .meta_client_service - .send_request(request) - .await; + app_manager.meta_client_service.send_request(request).await; } GenericApplicationManager::InMem { app_manager } => { - app_manager - .meta_client_service - .send_request(request) - .await; + app_manager.meta_client_service.send_request(request).await; } } } diff --git a/wasm/src/wasm_repo.rs b/wasm/src/wasm_repo.rs index f1c7bc7a..6b65197b 100644 --- a/wasm/src/wasm_repo.rs +++ b/wasm/src/wasm_repo.rs @@ -1,5 +1,6 @@ +use anyhow::{bail}; use async_trait::async_trait; -use tracing::{instrument, Instrument}; +use tracing::{error, instrument}; use meta_secret_core::node::db::events::generic_log_event::{GenericKvLogEvent, ObjIdExtractor}; use meta_secret_core::node::db::events::object_id::ObjectId; @@ -33,7 +34,7 @@ impl WasmRepo { async fn build_rexie(db_name: &str, store_name: &str) -> Rexie { Rexie::builder(db_name) .version(1) - .add_object_store(ObjectStore::new(store_name).key_path("id")) + .add_object_store(ObjectStore::new(store_name)) .build() .await .expect("Failed to create REXie") @@ -85,6 +86,11 @@ impl WasmRepo { impl SaveCommand for WasmRepo { #[instrument(skip_all)] async fn save(&self, event: GenericKvLogEvent) -> anyhow::Result { + let maybe_key = self.get_key(event.obj_id()).await?; + if let Some(_) = maybe_key { + bail!("Wrong behaviour. Event already exists: {:?}", event); + }; + let store_name = self.store_name.as_str(); let tx = self @@ -97,8 +103,13 @@ impl SaveCommand for WasmRepo { let js_value = serde_wasm_bindgen::to_value(&event).unwrap(); let id_str = event.obj_id().id_str(); let obj_id_js = serde_wasm_bindgen::to_value(id_str.as_str()).unwrap(); + + let op_result = store.add(&js_value, Some(&obj_id_js)).await; + if let Err(_) = &op_result { + error!("Failed to save event: {:?}", &event); + } - store.add(&js_value, Some(&obj_id_js)).await.unwrap(); + op_result.unwrap(); // Waits for the transaction to complete tx.done().await.unwrap(); diff --git a/web-cli/ui/src/components/vault/Vault.vue b/web-cli/ui/src/components/vault/Vault.vue new file mode 100644 index 00000000..bcdb798b --- /dev/null +++ b/web-cli/ui/src/components/vault/Vault.vue @@ -0,0 +1,30 @@ + + + diff --git a/web-cli/ui/src/components/vault/auth/Join.vue b/web-cli/ui/src/components/vault/auth/Join.vue new file mode 100644 index 00000000..96c0baf2 --- /dev/null +++ b/web-cli/ui/src/components/vault/auth/Join.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/web-cli/ui/src/components/vault/Registration.vue b/web-cli/ui/src/components/vault/auth/Registration.vue similarity index 71% rename from web-cli/ui/src/components/vault/Registration.vue rename to web-cli/ui/src/components/vault/auth/Registration.vue index 24d1725d..1d5a3bb9 100644 --- a/web-cli/ui/src/components/vault/Registration.vue +++ b/web-cli/ui/src/components/vault/auth/Registration.vue @@ -1,6 +1,7 @@