From 27a8895835892b78bb45918663c559ea3da87895 Mon Sep 17 00:00:00 2001 From: adz Date: Tue, 2 Dec 2025 11:29:45 +0100 Subject: [PATCH 01/11] tokio: Use default number of worker threads for tokio runtime --- reflection-node/src/node_inner.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index 79ef91a..9a9e580 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -51,10 +51,7 @@ impl NodeInner { db_location: Option<&Path>, connection_mode: ConnectionMode, ) -> Result { - let runtime = Builder::new_multi_thread() - .worker_threads(1) - .enable_all() - .build()?; + let runtime = Builder::new_multi_thread().enable_all().build()?; let _guard = runtime.enter(); From 4e9085a61417b16e1a8690130dda22d5e9272e35 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 16 Dec 2025 18:24:19 +0100 Subject: [PATCH 02/11] node: Use exisiting tokio runtime Pickup the existing tokio runtime if the node is created inside an existing runtime. This is handy for tests or applications that use tokio already. --- reflection-node/src/document.rs | 34 ++++--------- reflection-node/src/node.rs | 60 ++++++++++++++++++----- reflection-node/src/node_inner.rs | 33 ++++--------- reflection-node/src/subscription_inner.rs | 6 +-- 4 files changed, 71 insertions(+), 62 deletions(-) diff --git a/reflection-node/src/document.rs b/reflection-node/src/document.rs index c18af59..9eda405 100644 --- a/reflection-node/src/document.rs +++ b/reflection-node/src/document.rs @@ -2,7 +2,6 @@ use std::fmt; use std::hash::Hash; use std::sync::Arc; -use crate::node_inner::NodeInner; use crate::operation_store::CreationError; use crate::subscription_inner::SubscriptionInner; @@ -75,6 +74,7 @@ pub trait SubscribableDocument: Sync + Send { pub struct Subscription { pub(crate) inner: Arc>, + pub(crate) runtime: tokio::runtime::Handle, network_monitor_task: AbortHandle, } @@ -85,49 +85,40 @@ impl Drop for Subscription { } impl Subscription { - pub(crate) async fn new(node: Arc, id: DocumentId, document: Arc) -> Self { - let inner = SubscriptionInner::new(node, id, document); + pub(crate) async fn new(runtime: tokio::runtime::Handle, inner: SubscriptionInner) -> Self { + let inner = Arc::new(inner); let inner_clone = inner.clone(); - let network_monitor_task = inner - .node - .runtime + let network_monitor_task = runtime .spawn(async move { inner_clone.spawn_network_monitor().await; }) .abort_handle(); - info!("Subscribed to document {}", id); - Subscription { inner, + runtime, network_monitor_task, } } pub async fn send_delta(&self, data: Vec) -> Result<(), DocumentError> { let inner = self.inner.clone(); - self.inner - .node - .runtime + self.runtime .spawn(async move { inner.send_delta(data).await }) .await? } pub async fn send_snapshot(&self, data: Vec) -> Result<(), DocumentError> { let inner = self.inner.clone(); - self.inner - .node - .runtime + self.runtime .spawn(async move { inner.send_snapshot(data).await }) .await? } pub async fn send_ephemeral(&self, data: Vec) -> Result<(), DocumentError> { let inner = self.inner.clone(); - self.inner - .node - .runtime + self.runtime .spawn(async move { inner.send_ephemeral(data).await }) .await? } @@ -137,10 +128,7 @@ impl Subscription { self.network_monitor_task.abort(); let inner = self.inner.clone(); - inner - .node - .clone() - .runtime + self.runtime .spawn(async move { inner.unsubscribe().await }) .await??; @@ -154,9 +142,7 @@ impl Subscription { /// This information will be written to the database pub async fn set_name(&self, name: Option) -> Result<(), DocumentError> { let inner = self.inner.clone(); - self.inner - .node - .runtime + self.runtime .spawn(async move { inner.set_name(name).await }) .await? } diff --git a/reflection-node/src/node.rs b/reflection-node/src/node.rs index 432ca99..dfb691f 100644 --- a/reflection-node/src/node.rs +++ b/reflection-node/src/node.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use chrono::{DateTime, Utc}; use p2panda_core::{Hash, PrivateKey}; use thiserror::Error; +use tracing::info; use crate::document::{DocumentError, DocumentId, SubscribableDocument, Subscription}; pub use crate::document_store::Author; @@ -38,9 +39,27 @@ pub struct Document { pub authors: Vec, } +#[derive(Debug)] +enum OwnedRuntimeOrHandle { + Handle(tokio::runtime::Handle), + OwnedRuntime(tokio::runtime::Runtime), +} + +impl std::ops::Deref for OwnedRuntimeOrHandle { + type Target = tokio::runtime::Handle; + + fn deref(&self) -> &Self::Target { + match self { + OwnedRuntimeOrHandle::Handle(handle) => handle, + OwnedRuntimeOrHandle::OwnedRuntime(runtime) => runtime.handle(), + } + } +} + #[derive(Debug)] pub struct Node { inner: Arc, + runtime: OwnedRuntimeOrHandle, } impl Node { @@ -50,10 +69,26 @@ impl Node { db_location: Option<&Path>, connection_mode: ConnectionMode, ) -> Result { + let runtime = if let Ok(handle) = tokio::runtime::Handle::try_current() { + OwnedRuntimeOrHandle::Handle(handle) + } else { + OwnedRuntimeOrHandle::OwnedRuntime( + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?, + ) + }; + + let db_file = db_location.map(|location| location.join("database.sqlite")); + let inner = runtime + .spawn(async move { + NodeInner::new(network_id, private_key, db_file, connection_mode).await + }) + .await??; + Ok(Self { - inner: Arc::new( - NodeInner::new(network_id, private_key, db_location, connection_mode).await?, - ), + inner: Arc::new(inner), + runtime, }) } @@ -62,8 +97,7 @@ impl Node { connection_mode: ConnectionMode, ) -> Result<(), NodeError> { let inner_clone = self.inner.clone(); - self.inner - .runtime + self.runtime .spawn(async move { inner_clone.set_connection_mode(connection_mode).await; }) @@ -74,8 +108,7 @@ impl Node { pub async fn shutdown(&self) -> Result<(), NodeError> { let inner_clone = self.inner.clone(); - self.inner - .runtime + self.runtime .spawn(async move { inner_clone.shutdown().await; }) @@ -87,7 +120,6 @@ impl Node { pub async fn documents>(&self) -> Result>, DocumentError> { let inner_clone = self.inner.clone(); let documents = self - .inner .runtime .spawn(async move { inner_clone.document_store.documents().await }) .await??; @@ -121,10 +153,15 @@ impl Node { let document_id: DocumentId = DocumentId::from(document_id.into()); let document_handle = Arc::new(document_handle); let inner_clone = self.inner.clone(); - self.inner + let inner_subscription = self .runtime .spawn(async move { inner_clone.subscribe(document_id, document_handle).await }) - .await? + .await??; + + let subscription = Subscription::new(self.runtime.clone(), inner_subscription).await; + info!("Subscribed to topic {}", document_id); + + Ok(subscription) } pub async fn delete_document>( @@ -133,8 +170,7 @@ impl Node { ) -> Result<(), DocumentError> { let document_id: DocumentId = DocumentId::from(document_id.into()); let inner_clone = self.inner.clone(); - self.inner - .runtime + self.runtime .spawn(async move { inner_clone.delete_document(document_id).await }) .await? } diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index 9a9e580..8e2a2f9 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -1,13 +1,14 @@ use std::ops::DerefMut; -use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; -use crate::document::{DocumentError, DocumentId, SubscribableDocument, Subscription}; +use crate::document::{DocumentError, DocumentId, SubscribableDocument}; use crate::document_store::DocumentStore; use crate::ephemerial_operation::EphemerialOperation; use crate::node::{ConnectionMode, NodeError}; use crate::operation_store::OperationStore; use crate::persistent_operation::PersistentOperation; +use crate::subscription_inner::SubscriptionInner; use crate::utils::CombinedMigrationSource; use p2panda_core::{Hash, PrivateKey}; @@ -17,10 +18,7 @@ use p2panda_net::{Network, NetworkBuilder, SyncConfiguration}; use p2panda_store::sqlite::store::migrations as operation_store_migrations; use p2panda_sync::log_sync::LogSyncProtocol; use sqlx::{migrate::Migrator, sqlite}; -use tokio::{ - runtime::{Builder, Runtime}, - sync::{Notify, RwLock}, -}; +use tokio::sync::{Notify, RwLock}; use tracing::{info, warn}; #[derive(Debug, serde::Serialize, serde::Deserialize)] @@ -32,7 +30,6 @@ pub(crate) enum MessageType { #[derive(Debug)] pub struct NodeInner { - pub(crate) runtime: Runtime, pub(crate) operation_store: OperationStore, pub(crate) document_store: DocumentStore, pub(crate) private_key: PrivateKey, @@ -48,27 +45,18 @@ impl NodeInner { pub async fn new( network_id: Hash, private_key: PrivateKey, - db_location: Option<&Path>, + db_file: Option, connection_mode: ConnectionMode, ) -> Result { - let runtime = Builder::new_multi_thread().enable_all().build()?; - - let _guard = runtime.enter(); - let connection_options = sqlx::sqlite::SqliteConnectOptions::new() .shared_cache(true) .create_if_missing(true); - let connection_options = if let Some(db_location) = db_location { - let db_file = db_location.join("database.sqlite"); + let pool = if let Some(db_file) = db_file { info!("Database file location: {db_file:?}"); - connection_options.filename(db_file) - } else { - connection_options.in_memory(true) - }; - - let pool = if db_location.is_some() { + let connection_options = connection_options.filename(db_file); sqlx::sqlite::SqlitePool::connect_with(connection_options).await? } else { + let connection_options = connection_options.in_memory(true); // FIXME: we need to set max connection to 1 for in memory sqlite DB. // Probably has to do something with this issue: https://github.com/launchbadge/sqlx/issues/2510 let pool_options = sqlite::SqlitePoolOptions::new().max_connections(1); @@ -98,7 +86,6 @@ impl NodeInner { }; Ok(Self { - runtime, operation_store, document_store, private_key, @@ -156,7 +143,7 @@ impl NodeInner { self: Arc, document_id: DocumentId, document: Arc, - ) -> Result, DocumentError> { + ) -> Result, DocumentError> { self.document_store.add_document(&document_id).await?; // Add ourselves as an author to the document store. self.document_store @@ -175,7 +162,7 @@ impl NodeInner { } } - Ok(Subscription::new(self, document_id, document).await) + Ok(SubscriptionInner::new(self.clone(), document_id, document)) } pub async fn delete_document( diff --git a/reflection-node/src/subscription_inner.rs b/reflection-node/src/subscription_inner.rs index f7020fc..20b19a5 100644 --- a/reflection-node/src/subscription_inner.rs +++ b/reflection-node/src/subscription_inner.rs @@ -42,16 +42,16 @@ impl Drop for SubscriptionInner { } impl SubscriptionInner { - pub fn new(node: Arc, id: DocumentId, document: Arc) -> Arc { + pub fn new(node: Arc, id: DocumentId, document: Arc) -> Self { let author_tracker = AuthorTracker::new(node.clone(), document.clone()); - Arc::new(SubscriptionInner { + SubscriptionInner { tx: RwLock::new(None), node, id, abort_handles: RwLock::new(Vec::new()), document, author_tracker, - }) + } } pub async fn spawn_network_monitor(&self) { From dc20459123d063b633b13b47ab19c54ff8c5ac76 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 16 Dec 2025 18:25:35 +0100 Subject: [PATCH 03/11] node: Use signle tokio runtime for tests Now that the node pickes up an already existing runtime, we can use tokio::test macro. --- reflection-node/src/lib.rs | 116 +++++++++++++++---------------------- 1 file changed, 47 insertions(+), 69 deletions(-) diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index 8e736cb..f4f0da4 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -21,36 +21,25 @@ mod tests { use p2panda_core::PrivateKey; use p2panda_core::PublicKey; use std::sync::Arc; - use test_log::test; use tokio::sync::{Mutex, mpsc}; - #[test] - fn create_document() { - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(1) - .build() + #[tokio::test] + #[test_log::test] + async fn create_document() { + let private_key = PrivateKey::new(); + let network_id = Hash::new(b"reflection"); + let node = Node::new(private_key, network_id, None, ConnectionMode::Network) + .await .unwrap(); - let node = runtime.block_on(async move { - let private_key = PrivateKey::new(); - let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None, ConnectionMode::Network) - .await - .unwrap(); + let document_id: [u8; 32] = [0; 32]; + let _sub = node.subscribe(document_id, TestDocument::new()).await; + let documents = node.documents::<[u8; 32]>().await.unwrap(); - let document_id: [u8; 32] = [0; 32]; - let _sub = node.subscribe(document_id, TestDocument::new()).await; - let documents = node.documents::<[u8; 32]>().await.unwrap(); + assert_eq!(documents.len(), 1); + assert_eq!(documents.first().unwrap().id, document_id); - assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, document_id); - - node.shutdown().await.unwrap(); - node - }); - - // Node can't be dropped inside an async context - drop(node); + node.shutdown().await.unwrap(); } #[derive(Clone)] @@ -83,61 +72,50 @@ mod tests { fn ephemeral_bytes_received(&self, _author: PublicKey, _data: Vec) {} } - #[test] - fn subscribe_document() { - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(1) - .build() + #[tokio::test] + #[test_log::test] + async fn subscribe_document() { + let private_key = PrivateKey::new(); + let network_id = Hash::new(b"reflection"); + let node = Node::new(private_key, network_id, None, ConnectionMode::Network) + .await .unwrap(); - let nodes = runtime.block_on(async move { - let private_key = PrivateKey::new(); - let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None, ConnectionMode::Network) - .await - .unwrap(); - - let test_document = TestDocument::new(); - - let document_id: [u8; 32] = [0; 32]; - let subscription = node.subscribe(document_id, test_document).await.unwrap(); + let test_document = TestDocument::new(); - let documents = node.documents::<[u8; 32]>().await.unwrap(); - assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, document_id); + let document_id: [u8; 32] = [0; 32]; + let subscription = node.subscribe(document_id, test_document).await.unwrap(); - let private_key2 = PrivateKey::new(); - let network_id2 = Hash::new(b"reflection"); - let node2 = Node::new(private_key2, network_id2, None, ConnectionMode::Network) - .await - .unwrap(); + let documents = node.documents::<[u8; 32]>().await.unwrap(); + assert_eq!(documents.len(), 1); + assert_eq!(documents.first().unwrap().id, document_id); - let test_document2 = TestDocument::new(); - - let _subscription2 = node2 - .subscribe(document_id, test_document2.clone()) - .await - .unwrap(); + let private_key2 = PrivateKey::new(); + let network_id2 = Hash::new(b"reflection"); + let node2 = Node::new(private_key2, network_id2, None, ConnectionMode::Network) + .await + .unwrap(); - let documents2 = node2.documents::<[u8; 32]>().await.unwrap(); - assert_eq!(documents2.len(), 1); - assert_eq!(documents2.first().unwrap().id, document_id); + let test_document2 = TestDocument::new(); - let test_snapshot = "test".as_bytes().to_vec(); - subscription - .send_snapshot(test_snapshot.clone()) - .await - .unwrap(); + let _subscription2 = node2 + .subscribe(document_id, test_document2.clone()) + .await + .unwrap(); - assert_eq!(test_document2.wait_for_bytes().await, test_snapshot); + let documents2 = node2.documents::<[u8; 32]>().await.unwrap(); + assert_eq!(documents2.len(), 1); + assert_eq!(documents2.first().unwrap().id, document_id); - node.shutdown().await.unwrap(); - node2.shutdown().await.unwrap(); + let test_snapshot = "test".as_bytes().to_vec(); + subscription + .send_snapshot(test_snapshot.clone()) + .await + .unwrap(); - (node, node2) - }); + assert_eq!(test_document2.wait_for_bytes().await, test_snapshot); - // Node can't be dropped inside a tokio async context - drop(nodes); + node.shutdown().await.unwrap(); + node2.shutdown().await.unwrap(); } } From 7193f56fcbfe756f9961ca366ea7898569a569c5 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Mon, 22 Dec 2025 18:49:39 +0100 Subject: [PATCH 04/11] doc: Use glib::async_test for tests --- reflection-doc/src/lib.rs | 214 ++++++++++++++------------------------ 1 file changed, 80 insertions(+), 134 deletions(-) diff --git a/reflection-doc/src/lib.rs b/reflection-doc/src/lib.rs index 9f98d4c..cc65342 100644 --- a/reflection-doc/src/lib.rs +++ b/reflection-doc/src/lib.rs @@ -80,142 +80,101 @@ pub mod identity { #[cfg(test)] mod tests { - use glib::clone; - use test_log::test; - use crate::document::DocumentId; use crate::identity::PrivateKey; use crate::service::Service; - #[test] - fn create_document() { + #[test_log::test(glib::async_test)] + async fn create_document() { let test_string = "Hello World"; - let context = glib::MainContext::new(); - let main_loop = glib::MainLoop::new(Some(&context), false); - - context.spawn_local(clone!( - #[strong] - context, - #[strong] - main_loop, - async move { - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); - service.startup().await.unwrap(); + let context = glib::MainContext::ref_thread_default(); + println!("Context: {context:?}"); - let document = - service.join_document_with_main_context(&DocumentId::new(), &context); - document.subscribe().await; + let private_key = PrivateKey::new(); + let service = Service::new(&private_key, None); + service.startup().await.unwrap(); - assert!(document.insert_text(0, test_string).is_ok()); - assert_eq!(document.text(), test_string); + let document = service.join_document_with_main_context(&DocumentId::new(), &context); + document.subscribe().await; - service.shutdown().await; - main_loop.quit(); - } - )); + assert!(document.insert_text(0, test_string).is_ok()); + assert_eq!(document.text(), test_string); - main_loop.run(); + service.shutdown().await; } - #[test] - fn basic_sync() { + #[test_log::test(glib::async_test)] + async fn basic_sync() { let test_string = "Hello World"; - let context = glib::MainContext::new(); - let main_loop = glib::MainLoop::new(Some(&context), false); - - context.spawn_local(clone!( - #[strong] - context, - #[strong] - main_loop, - async move { - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); - service.startup().await.unwrap(); - - let document = - service.join_document_with_main_context(&DocumentId::new(), &context); - document.subscribe().await; - let id = document.id(); + let context = glib::MainContext::ref_thread_default(); + println!("Context: {context:?}"); - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); - service2.startup().await.unwrap(); + let private_key = PrivateKey::new(); + let service = Service::new(&private_key, None); + service.startup().await.unwrap(); - let document2 = service2.join_document_with_main_context(&id, &context); - document2.subscribe().await; + let document = service.join_document_with_main_context(&DocumentId::new(), &context); + document.subscribe().await; + let id = document.id(); - assert_eq!(document.id(), document2.id()); + let private_key2 = PrivateKey::new(); + let service2 = Service::new(&private_key2, None); + service2.startup().await.unwrap(); - assert!(document.insert_text(0, test_string).is_ok()); - assert_eq!(document.text(), test_string); + let document2 = service2.join_document_with_main_context(&id, &context); + document2.subscribe().await; - service.shutdown().await; - service2.shutdown().await; + assert_eq!(document.id(), document2.id()); - assert_eq!(document2.text(), test_string); + assert!(document.insert_text(0, test_string).is_ok()); + assert_eq!(document.text(), test_string); - main_loop.quit(); - } - )); + service.shutdown().await; + service2.shutdown().await; - main_loop.run(); + assert_eq!(document2.text(), test_string); } - #[test] - fn sync_multiple_changes() { + #[test_log::test(glib::async_test)] + async fn sync_multiple_changes() { let expected_string = "Hello, World!"; - let context = glib::MainContext::new(); - let main_loop = glib::MainLoop::new(Some(&context), false); + let context = glib::MainContext::ref_thread_default(); + println!("Context: {context:?}"); - context.spawn_local(clone!( - #[strong] - context, - #[strong] - main_loop, - async move { - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); - service.startup().await.unwrap(); + let private_key = PrivateKey::new(); + let service = Service::new(&private_key, None); + service.startup().await.unwrap(); - let document = - service.join_document_with_main_context(&DocumentId::new(), &context); - document.subscribe().await; - let id = document.id(); + let document = service.join_document_with_main_context(&DocumentId::new(), &context); + document.subscribe().await; + let id = document.id(); - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); - service2.startup().await.unwrap(); + let private_key2 = PrivateKey::new(); + let service2 = Service::new(&private_key2, None); + service2.startup().await.unwrap(); - let document2 = service2.join_document_with_main_context(&id, &context); - document2.subscribe().await; + let document2 = service2.join_document_with_main_context(&id, &context); + document2.subscribe().await; - assert_eq!(document.id(), document2.id()); + assert_eq!(document.id(), document2.id()); - assert!(document.insert_text(0, "Hello,").is_ok()); - assert!(document.insert_text(6, " World!").is_ok()); - assert!(document.delete_range(7, 8).is_ok()); - assert!(document.insert_text(7, "W").is_ok()); - assert_eq!(document.text(), expected_string); + assert!(document.insert_text(0, "Hello,").is_ok()); + assert!(document.insert_text(6, " World!").is_ok()); + assert!(document.delete_range(7, 8).is_ok()); + assert!(document.insert_text(7, "W").is_ok()); + assert_eq!(document.text(), expected_string); - service.shutdown().await; - service2.shutdown().await; + service.shutdown().await; + service2.shutdown().await; - assert_eq!(document2.text(), expected_string); - - main_loop.quit(); - } - )); - - main_loop.run(); + assert_eq!(document2.text(), expected_string); } - #[test] - fn sync_longer_text() { + #[test_log::test(glib::async_test)] + async fn sync_longer_text() { let test_string = "Et aut omnis eos corporis ut. Qui est blanditiis blanditiis. Sit quia nam maxime accusantium ut voluptatem. Fuga consequuntur animi et et est. Unde voluptas consequatur mollitia id odit optio harum sint. Fugit quo aut et laborum aut cupiditate."; @@ -224,48 +183,35 @@ mod tests { test_string, test_string, test_string, test_string ); - let context = glib::MainContext::new(); - let main_loop = glib::MainLoop::new(Some(&context), false); - - context.spawn_local(clone!( - #[strong] - context, - #[strong] - main_loop, - async move { - let private_key = PrivateKey::new(); - let service = Service::new(&private_key, None); - service.startup().await.unwrap(); - - let document = - service.join_document_with_main_context(&DocumentId::new(), &context); - let id = document.id(); + let context = glib::MainContext::ref_thread_default(); + println!("Context: {context:?}"); - document.subscribe().await; + let private_key = PrivateKey::new(); + let service = Service::new(&private_key, None); + service.startup().await.unwrap(); - let private_key2 = PrivateKey::new(); - let service2 = Service::new(&private_key2, None); - service2.startup().await.unwrap(); + let document = service.join_document_with_main_context(&DocumentId::new(), &context); + let id = document.id(); - let document2 = service2.join_document_with_main_context(&id, &context); - document2.subscribe().await; + document.subscribe().await; - assert_eq!(document.id(), document2.id()); + let private_key2 = PrivateKey::new(); + let service2 = Service::new(&private_key2, None); + service2.startup().await.unwrap(); - assert!(document.insert_text(0, test_string).is_ok()); - assert!(document.insert_text(0, test_string).is_ok()); - assert!(document.insert_text(0, test_string).is_ok()); - assert!(document.insert_text(0, test_string).is_ok()); + let document2 = service2.join_document_with_main_context(&id, &context); + document2.subscribe().await; - service.shutdown().await; - service2.shutdown().await; + assert_eq!(document.id(), document2.id()); - assert_eq!(document2.text(), expected_string); + assert!(document.insert_text(0, test_string).is_ok()); + assert!(document.insert_text(0, test_string).is_ok()); + assert!(document.insert_text(0, test_string).is_ok()); + assert!(document.insert_text(0, test_string).is_ok()); - main_loop.quit(); - } - )); + service.shutdown().await; + service2.shutdown().await; - main_loop.run(); + assert_eq!(document2.text(), expected_string); } } From 9ea527a21d916230790715831b5f4f5c10107931 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 9 Dec 2025 18:09:28 +0100 Subject: [PATCH 05/11] node: Port to p2panda_net_next This should fix many of the networking issues we incountered. --- Cargo.lock | 1850 ++++++++++----------- reflection-node/Cargo.toml | 16 +- reflection-node/src/author_tracker.rs | 19 +- reflection-node/src/document.rs | 56 +- reflection-node/src/document_store.rs | 97 +- reflection-node/src/lib.rs | 1 - reflection-node/src/node.rs | 11 +- reflection-node/src/node_inner.rs | 81 +- reflection-node/src/operation.rs | 10 +- reflection-node/src/operation_store.rs | 4 +- reflection-node/src/subscription_inner.rs | 315 ++-- 11 files changed, 1175 insertions(+), 1285 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17ad293..f73c7b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,29 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "acto" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a026259da4f1a13b4af60cda453c392de64c58c12d239c560923e0382f42f2b9" +dependencies = [ + "parking_lot", + "pin-project-lite", + "rustc_version", + "smol_str", + "tokio", + "tracing", +] + [[package]] name = "aead" -version = "0.5.2" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "ac8202ab55fcbf46ca829833f347a82a2a4ce0596f0304ac322c2d100030cd56" dependencies = [ "bytes", - "crypto-common", - "generic-array", + "crypto-common 0.2.0-rc.4", + "inout", ] [[package]] @@ -158,45 +172,6 @@ dependencies = [ "zbus", ] -[[package]] -name = "asn1-rs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "async-broadcast" version = "0.7.2" @@ -221,6 +196,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compat" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" version = "1.13.3" @@ -350,6 +338,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + [[package]] name = "atoi" version = "2.0.0" @@ -376,11 +375,12 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.24.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ - "http 0.2.12", + "base64", + "http", "log", "url", ] @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" [[package]] name = "base32" @@ -422,15 +422,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bitflags" -version = "1.3.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bitflags" @@ -478,6 +472,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" +dependencies = [ + "hybrid-array", + "zeroize", +] + [[package]] name = "blocking" version = "1.6.2" @@ -492,10 +496,27 @@ dependencies = [ ] [[package]] -name = "bounded-integer" -version = "0.5.8" +name = "bon" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97493a391b4b18ee918675fb8663e53646fd09321c58b46afa04e8ce2499c869" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102dbef1187b1893e6dfe05a774e79fd52265f49f214f6879c8ff49f52c8188b" +checksum = "2a2af3eac944c12cdf4423eab70d310da0a8e5851a18ffb192c0a5e3f7ae1663" +dependencies = [ + "darling", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.111", +] [[package]] name = "bumpalo" @@ -520,11 +541,11 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe4354df4da648870e363387679081f8f9fc538ec8b55901e3740c6a0ef81b1" +checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4" dependencies = [ - "bitflags 2.10.0", + "bitflags", "cairo-sys-rs", "glib", "libc", @@ -532,9 +553,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d6c3300c7103eb8e4de07591003511aa25664438f8c6fc317a3a9902c103f8" +checksum = "06c28280c6b12055b5e39e4554271ae4e6630b27c0da9148c4cf6485fc6d245c" dependencies = [ "glib-sys", "libc", @@ -543,9 +564,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.48" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", @@ -581,13 +602,14 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.9.1" +version = "0.10.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +checksum = "9bd162f2b8af3e0639d83f28a637e4e55657b7a74508dba5a9bf4da523d5c9e9" dependencies = [ "cfg-if", "cipher", "cpufeatures", + "zeroize", ] [[package]] @@ -633,11 +655,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "1e12a13eb01ded5d32ee9658d94f553a19e804204f2dc811df69ab4d9e0cb8c7" dependencies = [ - "crypto-common", + "block-buffer 0.11.0", + "crypto-common 0.2.0-rc.4", "inout", "zeroize", ] @@ -682,12 +705,27 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + [[package]] name = "constant_time_eq" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cordyceps" version = "0.3.4" @@ -800,20 +838,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6" +dependencies = [ + "hybrid-array", + "rand_core 0.9.3", +] + [[package]] name = "crypto_box" -version = "0.9.1" +version = "0.10.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16182b4f39a82ec8a6851155cc4c0cda3065bb1db33651726a29e1951de0f009" +checksum = "2bda4de3e070830cf3a27a394de135b6709aefcc54d1e16f2f029271254a6ed9" dependencies = [ "aead", "chacha20", "crypto_secretbox", - "curve25519-dalek", + "curve25519-dalek 5.0.0-pre.1", "salsa20", "serdect", "subtle", @@ -822,14 +869,14 @@ dependencies = [ [[package]] name = "crypto_secretbox" -version = "0.1.1" +version = "0.2.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" +checksum = "54532aae6546084a52cef855593daf9555945719eeeda9974150e0def854873e" dependencies = [ "aead", "chacha20", "cipher", - "generic-array", + "hybrid-array", "poly1305", "salsa20", "subtle", @@ -845,9 +892,25 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rand_core 0.6.4", + "digest 0.10.7", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.11.0-rc.3", + "fiat-crypto 0.3.0", + "rand_core 0.9.3", "rustc_version", "serde", "subtle", @@ -900,6 +963,20 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -912,35 +989,20 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", - "der_derive", - "pem-rfc7468", + "const-oid 0.9.6", + "pem-rfc7468 0.7.0", "zeroize", ] [[package]] -name = "der-parser" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" +name = "der" +version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "const-oid 0.10.1", + "pem-rfc7468 1.0.0", + "zeroize", ] [[package]] @@ -969,7 +1031,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +dependencies = [ + "derive_more-impl 2.1.0", ] [[package]] @@ -984,6 +1055,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "derive_more-impl" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.111", + "unicode-xid", +] + [[package]] name = "diatomic-waker" version = "0.2.3" @@ -1002,12 +1087,23 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac89f8a64533a9b0eaa73a68e424db0fb1fd6271c74cc0125336a05f090568d" +dependencies = [ + "block-buffer 0.11.0", + "const-oid 0.10.1", + "crypto-common 0.2.0-rc.4", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1045,15 +1141,31 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", + "pkcs8 0.10.2", + "signature 2.2.0", +] + +[[package]] +name = "ed25519" +version = "3.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "594435fe09e345ee388e4e8422072ff7dfeca8729389fbd997b3f5504c44cd47" +dependencies = [ + "pkcs8 0.11.0-rc.8", "serde", - "signature", + "signature 3.0.0-rc.5", ] [[package]] @@ -1062,11 +1174,27 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "rand_core 0.6.4", "serde", - "sha2", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" +dependencies = [ + "curve25519-dalek 5.0.0-pre.1", + "ed25519 3.0.0-rc.2", + "rand_core 0.9.3", + "serde", + "sha2 0.11.0-rc.2", + "signature 3.0.0-rc.5", "subtle", "zeroize", ] @@ -1188,21 +1316,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" -dependencies = [ - "serde", -] - -[[package]] -name = "erased_set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a02a5d186d7bf1cb21f1f95e1a9cfa5c1f2dcd803a47aad454423ceec13525c5" - [[package]] name = "errno" version = "0.3.14" @@ -1245,12 +1358,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - [[package]] name = "fastrand" version = "2.3.0" @@ -1263,6 +1370,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "field-offset" version = "0.3.6" @@ -1293,7 +1406,6 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "nanorand", "spin 0.9.8", ] @@ -1309,6 +1421,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1482,9 +1600,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3c64459f569154f37616fc28923bfac490d4aaa134aaf5eca58a2c0c13050f" +checksum = "debb0d39e3cdd84626edfd54d6e4a6ba2da9a0ef2e796e691c4e9f8646fda00c" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -1494,9 +1612,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3854ef7a6a8b8f3b4013a01d5f9cb0d1794ec4e810c6cb4e2cc6d980f1baf724" +checksum = "bd95ad50b9a3d2551e25dd4f6892aff0b772fe5372d84514e9d0583af60a0ce7" dependencies = [ "gio-sys", "glib-sys", @@ -1507,9 +1625,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e292649dc26e3440c508a00f42ab39156008320dd6e962d63eaf626ba4d7f0" +checksum = "756564212bbe4a4ce05d88ffbd2582581ac6003832d0d32822d0825cca84bfbf" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -1522,9 +1640,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f3174fa4f1e0bf2a7e04469b65db8f4d1db89a6f5cdc57727b14e97ce438cf" +checksum = "a6d4e5b3ccf591826a4adcc83f5f57b4e59d1925cb4bf620b0d645f79498b034" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1559,7 +1677,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1625,9 +1742,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeff3dd716d1ba91850b976b76a1c2d28f99ef6c1602cd8fdaa8fab8017fd9c" +checksum = "c5ff48bf600c68b476e61dc6b7c762f2f4eb91deef66583ba8bb815c30b5811a" dependencies = [ "futures-channel", "futures-core", @@ -1642,9 +1759,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" +checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22" dependencies = [ "glib-sys", "gobject-sys", @@ -1655,11 +1772,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9dbecb1c33e483a98be4acfea2ab369e1c28f517c6eadb674537409c25c4b2" +checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.10.0", + "bitflags", "futures-channel", "futures-core", "futures-executor", @@ -1676,9 +1793,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "880e524e0085f3546cfb38532b2c202c0d64741d9977a6e4aa24704bfc9f19fb" +checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" dependencies = [ "heck 0.5.0", "proc-macro-crate", @@ -1689,20 +1806,14 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" +checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" dependencies = [ "libc", "system-deps", ] -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "gloo-timers" version = "0.3.0" @@ -1717,9 +1828,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" +checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" dependencies = [ "glib-sys", "libc", @@ -1728,9 +1839,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7749aaf5d3b955bf3bfce39e3423705878a666b561384134da0e7786a45ddc3" +checksum = "2730030ac9db663fd8bfe1e7093742c1cafb92db9c315c9417c29032341fe2f9" dependencies = [ "glib", "graphene-sys", @@ -1739,9 +1850,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250abaee850a90a276509890a78029c356173f9573412bded5f155b0e41fa568" +checksum = "915e32091ea9ad241e4b044af62b7351c2d68aeb24f489a0d7f37a0fc484fd93" dependencies = [ "glib-sys", "libc", @@ -1751,9 +1862,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6687e9f92ca89c000c376400cfaf7914d099413d72fdf4f84a25775a0b1fb2d" +checksum = "e755de9d8c5896c5beaa028b89e1969d067f1b9bf1511384ede971f5983aa153" dependencies = [ "cairo-rs", "gdk4", @@ -1766,9 +1877,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e76bcf64d9c4846f19651f45b400cc0c9c4c17b651849da520f3d77c6988c52" +checksum = "7ce91472391146f482065f1041876d8f869057b195b95399414caa163d72f4f7" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -1782,9 +1893,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ea71795b91a0725b0e926e72e3d209d920ce60166e3a8f9f4dd46f287fee87" +checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4" dependencies = [ "cairo-rs", "field-offset", @@ -1803,9 +1914,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "821160b4f17e7e4ed748818c23682d0a46bed04c287dbaac54dd4869d2c5e06a" +checksum = "3ccfb5a14a3d941244815d5f8101fa12d4577b59cc47245778d8d907b0003e42" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1815,9 +1926,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d274cbaf7d9aa55b7aff78cb21b43299d64e514e1300671469b66f691cc5a011" +checksum = "842577fe5a1ee15d166cd3afe804ce0cab6173bc789ca32e21308834f20088dd" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1843,7 +1954,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http", "indexmap", "slab", "tokio", @@ -1880,6 +1991,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1888,7 +2005,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1896,6 +2013,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1974,20 +2096,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", + "bytes", "cfg-if", "data-encoding", "enum-as-inner 0.6.1", "futures-channel", "futures-io", "futures-util", + "h2", + "http", "idna", "ipnet", "once_cell", "rand 0.9.2", "ring", + "rustls", "thiserror 2.0.17", "tinyvec", "tokio", + "tokio-rustls", "tracing", "url", ] @@ -2007,9 +2134,11 @@ dependencies = [ "parking_lot", "rand 0.9.2", "resolv-conf", + "rustls", "smallvec", "thiserror 2.0.17", "tokio", + "tokio-rustls", "tracing", ] @@ -2028,25 +2157,9 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", -] - -[[package]] -name = "hmac-sha1" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b05da5b9e5d4720bfb691eebb2b9d42da3570745da71eac8a1f5bb7e59aab88" -dependencies = [ - "hmac", - "sha1", + "digest 0.10.7", ] -[[package]] -name = "hmac-sha256" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6880c8d4a9ebf39c6e8b77007ce223f646a4d21ce29d99f70cb16420545425" - [[package]] name = "home" version = "0.5.12" @@ -2056,23 +2169,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "hostname-validator" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.4.0" @@ -2090,7 +2186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http", ] [[package]] @@ -2101,7 +2197,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http", "http-body", "pin-project-lite", ] @@ -2118,6 +2214,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +dependencies = [ + "typenum", + "zeroize", +] + [[package]] name = "hyper" version = "1.8.1" @@ -2129,7 +2235,7 @@ dependencies = [ "futures-channel", "futures-core", "h2", - "http 1.4.0", + "http", "http-body", "httparse", "httpdate", @@ -2147,7 +2253,7 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.4.0", + "http", "hyper", "hyper-util", "rustls", @@ -2155,21 +2261,21 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.4", + "webpki-roots", ] [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http", "http-body", "hyper", "ipnet", @@ -2254,9 +2360,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -2268,9 +2374,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -2316,20 +2422,20 @@ dependencies = [ [[package]] name = "igd-next" -version = "0.15.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b0d7d4541def58a37bf8efc559683f21edce7c82f0d866c93ac21f7e098f93" +checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" dependencies = [ "async-trait", "attohttpc", "bytes", "futures", - "http 1.4.0", + "http", "http-body-util", "hyper", "hyper-util", "log", - "rand 0.8.5", + "rand 0.9.2", "tokio", "url", "xmltree", @@ -2362,11 +2468,11 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "c7357b6e7aa75618c7864ebd0634b115a7218b0615f4cb1df33ac3eca23943d4" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] @@ -2411,25 +2517,22 @@ dependencies = [ [[package]] name = "iroh" -version = "0.34.1" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37432887a6836e7a832fccb121b5f0ee6cd953c506f99b0278bdbedf8dee0e88" +checksum = "2374ba3cdaac152dc6ada92d971f7328e6408286faab3b7350842b2ebbed4789" dependencies = [ "aead", - "anyhow", - "atomic-waker", "backon", "bytes", "cfg_aliases", - "concurrent-queue", "crypto_box", "data-encoding", - "der", - "derive_more", - "ed25519-dalek", + "derive_more 2.1.0", + "ed25519-dalek 3.0.0-pre.1", "futures-util", + "getrandom 0.3.4", "hickory-resolver", - "http 1.4.0", + "http", "igd-next", "instant", "iroh-base", @@ -2438,24 +2541,25 @@ dependencies = [ "iroh-quinn-proto", "iroh-quinn-udp", "iroh-relay", + "n0-error", "n0-future", + "n0-watcher", "netdev", "netwatch", "pin-project", "pkarr", + "pkcs8 0.11.0-rc.8", "portmapper", - "rand 0.8.5", - "rcgen", + "rand 0.9.2", "reqwest", - "ring", "rustls", - "rustls-webpki 0.102.8", + "rustls-pki-types", + "rustls-platform-verifier", + "rustls-webpki", "serde", "smallvec", - "strum", - "stun-rs", - "surge-ping", - "thiserror 2.0.17", + "strum 0.27.2", + "swarm-discovery", "time", "tokio", "tokio-stream", @@ -2463,67 +2567,52 @@ dependencies = [ "tracing", "url", "wasm-bindgen-futures", - "webpki-roots 0.26.11", - "x509-parser", + "webpki-roots", "z32", ] [[package]] name = "iroh-base" -version = "0.34.1" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd952d9e25e521d6aeb5b79f2fe32a0245da36aae3569e50f6010b38a5f0923" +checksum = "25a8c5fb1cc65589f0d7ab44269a76f615a8c4458356952c9b0ef1c93ea45ff8" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 5.0.0-pre.1", "data-encoding", - "derive_more", - "ed25519-dalek", - "postcard", - "rand_core 0.6.4", + "derive_more 2.1.0", + "ed25519-dalek 3.0.0-pre.1", + "n0-error", + "rand_core 0.9.3", "serde", - "thiserror 2.0.17", "url", -] - -[[package]] -name = "iroh-blake3" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbba31f40a650f58fa28dd585a8ca76d8ae3ba63aacab4c8269004a0c803930" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", + "zeroize", + "zeroize_derive", ] [[package]] name = "iroh-gossip" -version = "0.34.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a9d638618fb6a4dac68d59cb694774478ea039bc9afca4709e242726591be1" +version = "0.95.0" +source = "git+https://github.com/p2panda/iroh-gossip?rev=6fad0f740c031876dbb412c7b8237776314d763e#6fad0f740c031876dbb412c7b8237776314d763e" dependencies = [ - "anyhow", - "async-channel", + "blake3", "bytes", - "derive_more", - "ed25519-dalek", + "data-encoding", + "derive_more 2.1.0", + "ed25519-dalek 3.0.0-pre.1", "futures-concurrency", "futures-lite", "futures-util", "hex", "indexmap", "iroh", - "iroh-blake3", + "iroh-base", "iroh-metrics", + "irpc", + "n0-error", "n0-future", "postcard", - "rand 0.8.5", - "rand_core 0.6.4", + "rand 0.9.2", "serde", - "serde-error", - "thiserror 2.0.17", "tokio", "tokio-util", "tracing", @@ -2531,26 +2620,39 @@ dependencies = [ [[package]] name = "iroh-metrics" -version = "0.32.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f7cd1ffe3b152a5f4f4c1880e01e07d96001f20e02cc143cb7842987c616b3" +checksum = "79e3381da7c93c12d353230c74bba26131d1c8bf3a4d8af0fec041546454582e" dependencies = [ - "erased_set", + "iroh-metrics-derive", + "itoa", + "n0-error", + "postcard", + "ryu", "serde", - "struct_iterable", - "thiserror 2.0.17", "tracing", ] +[[package]] +name = "iroh-metrics-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e12bd0763fd16062f5cc5e8db15dd52d26e75a8af4c7fb57ccee3589b344b8" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "iroh-quinn" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c6245c9ed906506ab9185e8d7f64857129aee4f935e899f398a3bd3b70338d" +checksum = "0cde160ebee7aabede6ae887460cd303c8b809054224815addf1469d54a6fcf7" dependencies = [ "bytes", "cfg_aliases", - "futures-io", "iroh-quinn-proto", "iroh-quinn-udp", "pin-project-lite", @@ -2576,7 +2678,6 @@ dependencies = [ "rustc-hash", "rustls", "rustls-pki-types", - "rustls-platform-verifier", "slab", "thiserror 2.0.17", "tinyvec", @@ -2600,17 +2701,18 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "0.34.1" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d2d7b50d999922791c6c14c25e13f55711e182618cb387bafa0896ffe0b930" +checksum = "43fbdf2aeffa7d6ede1a31f6570866c2199b1cee96a0b563994623795d1bac2c" dependencies = [ - "anyhow", + "blake3", "bytes", "cfg_aliases", "data-encoding", - "derive_more", + "derive_more 2.1.0", + "getrandom 0.3.4", "hickory-resolver", - "http 1.4.0", + "http", "http-body-util", "hyper", "hyper-util", @@ -2618,30 +2720,59 @@ dependencies = [ "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", - "lru", + "lru 0.16.2", + "n0-error", "n0-future", "num_enum", "pin-project", "pkarr", "postcard", - "rand 0.8.5", + "rand 0.9.2", "reqwest", "rustls", - "rustls-webpki 0.102.8", + "rustls-pki-types", "serde", - "strum", - "stun-rs", - "thiserror 2.0.17", + "serde_bytes", + "sha1 0.11.0-rc.2", + "strum 0.27.2", "tokio", "tokio-rustls", - "tokio-tungstenite-wasm", "tokio-util", + "tokio-websockets", "tracing", "url", - "webpki-roots 0.26.11", + "webpki-roots", + "ws_stream_wasm", "z32", ] +[[package]] +name = "irpc" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bee97aaa18387c4f0aae61058195dc9f9dea3e41c0e272973fe3e9bf611563d" +dependencies = [ + "futures-util", + "irpc-derive", + "n0-error", + "n0-future", + "serde", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "irpc-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58148196d2230183c9679431ac99b57e172000326d664e8456fa2cd27af6505a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2752,9 +2883,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -2768,7 +2899,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.10.0", + "bitflags", "libc", "redox_syscall", ] @@ -2855,9 +2986,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -3005,11 +3136,17 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.5" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" + +[[package]] +name = "lru" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -3052,7 +3189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] @@ -3076,17 +3213,11 @@ dependencies = [ "autocfg", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi", @@ -3111,14 +3242,36 @@ dependencies = [ "uuid", ] +[[package]] +name = "n0-error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d5969a2f40e9d9ed121a789c415f4114ac2b28e5731c080bdefee217d3b3fb" +dependencies = [ + "anyhow", + "n0-error-macros", + "spez", +] + +[[package]] +name = "n0-error-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6908df844696d9af91c7c3950d50e52d67df327d02a95367f95bbf177d6556" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "n0-future" -version = "0.1.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" +checksum = "8c0709ac8235ce13b82bc4d180ee3c42364b90c1a8a628c3422d991d75a728b5" dependencies = [ "cfg_aliases", - "derive_more", + "derive_more 1.0.0", "futures-buffered", "futures-lite", "futures-util", @@ -3133,87 +3286,59 @@ dependencies = [ ] [[package]] -name = "nanorand" -version = "0.7.0" +name = "n0-watcher" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +checksum = "38acf13c1ddafc60eb7316d52213467f8ccb70b6f02b65e7d97f7799b1f50be4" dependencies = [ - "getrandom 0.2.16", + "derive_more 2.1.0", + "n0-error", + "n0-future", ] [[package]] name = "netdev" -version = "0.31.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" +checksum = "67ab878b4c90faf36dab10ea51d48c69ae9019bcca47c048a7c9b273d5d7a823" dependencies = [ "dlopen2", "ipnet", "libc", "netlink-packet-core", - "netlink-packet-route 0.17.1", + "netlink-packet-route", "netlink-sys", "once_cell", "system-configuration", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "netlink-packet-core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" -dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-route" -version = "0.17.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" dependencies = [ - "anyhow", - "bitflags 1.3.2", - "byteorder", - "libc", - "netlink-packet-core", - "netlink-packet-utils", + "paste", ] [[package]] name = "netlink-packet-route" -version = "0.19.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c171cd77b4ee8c7708da746ce392440cb7bcf618d122ec9ecc607b12938bf4" +checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" dependencies = [ - "anyhow", - "byteorder", + "bitflags", "libc", "log", "netlink-packet-core", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror 1.0.69", ] [[package]] name = "netlink-proto" -version = "0.11.5" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" +checksum = "b65d130ee111430e47eed7896ea43ca693c387f097dd97376bffafbf25812128" dependencies = [ "bytes", "futures", @@ -3238,66 +3363,45 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.4.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7879c2cfdf30d92f2be89efa3169b3d78107e3ab7f7b9a37157782569314e1" +checksum = "26f2acd376ef48b6c326abf3ba23c449e0cb8aa5c2511d189dd8a8a3bfac889b" dependencies = [ "atomic-waker", "bytes", "cfg_aliases", - "derive_more", + "derive_more 2.1.0", "iroh-quinn-udp", "js-sys", "libc", + "n0-error", "n0-future", + "n0-watcher", "netdev", "netlink-packet-core", - "netlink-packet-route 0.19.0", + "netlink-packet-route", + "netlink-proto", "netlink-sys", - "rtnetlink 0.13.1", - "rtnetlink 0.14.1", + "pin-project-lite", "serde", - "socket2 0.5.10", - "thiserror 2.0.17", + "socket2 0.6.1", "time", "tokio", "tokio-util", "tracing", "web-sys", - "windows 0.59.0", - "windows-result 0.3.4", + "windows 0.62.2", + "windows-result 0.4.1", "wmi", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "libc", -] - [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -3305,27 +3409,26 @@ dependencies = [ ] [[package]] -name = "no-std-net" -version = "0.6.0" +name = "nonmax" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" [[package]] -name = "nom" -version = "7.1.3" +name = "ntimestamp" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "c50f94c405726d3e0095e89e72f75ce7f6587b94a8bd8dc8054b73f65c0fd68c" dependencies = [ - "memchr", - "minimal-lexical", + "base32", + "document-features", + "getrandom 0.2.16", + "httpdate", + "js-sys", + "once_cell", + "serde", ] -[[package]] -name = "nonmax" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -3483,15 +3586,6 @@ dependencies = [ "objc", ] -[[package]] -name = "oid-registry" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" -dependencies = [ - "asn1-rs", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -3535,19 +3629,13 @@ dependencies = [ "zvariant", ] -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -3598,11 +3686,11 @@ dependencies = [ [[package]] name = "p2panda-core" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ "blake3", "ciborium", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "hex", "rand 0.8.5", "serde", @@ -3611,44 +3699,37 @@ dependencies = [ ] [[package]] -name = "p2panda-discovery" +name = "p2panda-discovery-next" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ - "anyhow", - "base32", - "flume", - "futures-buffered", - "futures-lite", - "hickory-proto", - "iroh", - "iroh-base", - "netwatch", - "socket2 0.5.10", + "blake3", + "futures-util", + "rand 0.9.2", + "rand_chacha 0.9.0", + "serde", + "thiserror 2.0.17", "tokio", - "tokio-util", - "tracing", ] [[package]] -name = "p2panda-net" +name = "p2panda-net-next" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ - "anyhow", - "async-trait", "ciborium", - "futures-lite", + "futures-channel", "futures-util", + "hex", "iroh", "iroh-base", "iroh-gossip", - "iroh-quinn", - "netwatch", "p2panda-core", - "p2panda-discovery", - "p2panda-sync", - "rand 0.8.5", + "p2panda-discovery-next", + "p2panda-sync-next", + "ractor", + "rand 0.9.2", + "rand_chacha 0.9.0", "serde", "thiserror 2.0.17", "tokio", @@ -3660,7 +3741,7 @@ dependencies = [ [[package]] name = "p2panda-store" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ "ciborium", "hex", @@ -3673,7 +3754,7 @@ dependencies = [ [[package]] name = "p2panda-stream" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ "ciborium", "futures-channel", @@ -3686,25 +3767,26 @@ dependencies = [ ] [[package]] -name = "p2panda-sync" +name = "p2panda-sync-next" version = "0.4.0" -source = "git+https://github.com/p2panda/p2panda#61ea7101eb4af6e672666e35c2b750d8d9621f4a" +source = "git+https://github.com/p2panda/p2panda?rev=a83a80b2ce0733c9437ceeaae33503d3b3742436#a83a80b2ce0733c9437ceeaae33503d3b3742436" dependencies = [ - "async-trait", "futures", + "futures-util", "p2panda-core", "p2panda-store", "serde", "thiserror 2.0.17", "tokio", - "tokio-util", + "tokio-stream", + "tracing", ] [[package]] name = "pango" -version = "0.21.3" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37b7a678e18c2e9f2485f7e39b7b2dac99590d5ddef08a7f56eae38a145402e" +checksum = "52d1d85e2078077a065bb7fc072783d5bcd4e51b379f22d67107d0a16937eb69" dependencies = [ "gio", "glib", @@ -3714,9 +3796,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f5daf21da43fba9f2a0092da0eebeb77637c23552bccaf58f791c518009c94" +checksum = "b4f06627d36ed5ff303d2df65211fc2e52ba5b17bf18dd80ff3d9628d6e06cfd" dependencies = [ "glib-sys", "gobject-sys", @@ -3760,20 +3842,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "pem" -version = "3.0.6" +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ - "base64", - "serde_core", + "base64ct", ] [[package]] name = "pem-rfc7468" -version = "0.7.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" dependencies = [ "base64ct", ] @@ -3785,46 +3866,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "pest" -version = "2.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "pest_meta" -version = "2.8.4" +name = "pharos" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ - "pest", - "sha2", + "futures", + "rustc_version", ] [[package]] @@ -3872,26 +3920,33 @@ dependencies = [ [[package]] name = "pkarr" -version = "2.3.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92eff194c72f00f3076855b413ad2d940e3a6e307fa697e5c7733e738341aed4" +checksum = "792c1328860f6874e90e3b387b4929819cc7783a6bd5a4728e918706eb436a48" dependencies = [ + "async-compat", + "base32", "bytes", + "cfg_aliases", "document-features", - "ed25519-dalek", - "flume", - "futures", - "js-sys", - "lru", + "dyn-clone", + "ed25519-dalek 3.0.0-pre.1", + "futures-buffered", + "futures-lite", + "getrandom 0.3.4", + "log", + "lru 0.13.0", + "ntimestamp", + "reqwest", "self_cell", + "serde", + "sha1_smol", "simple-dns", "thiserror 2.0.17", + "tokio", "tracing", - "ureq", - "wasm-bindgen", + "url", "wasm-bindgen-futures", - "web-sys", - "z32", ] [[package]] @@ -3900,9 +3955,9 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", ] [[package]] @@ -3911,57 +3966,25 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.10", + "spki 0.7.3", ] [[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "pnet_base" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" -dependencies = [ - "no-std-net", -] - -[[package]] -name = "pnet_macros" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.111", -] - -[[package]] -name = "pnet_macros_support" -version = "0.34.0" +name = "pkcs8" +version = "0.11.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56" +checksum = "77089aec8290d0b7bb01b671b091095cf1937670725af4fd73d47249f03b12c0" dependencies = [ - "pnet_base", + "der 0.8.0-rc.10", + "spki 0.8.0-rc.4", ] [[package]] -name = "pnet_packet" -version = "0.34.0" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba" -dependencies = [ - "glob", - "pnet_base", - "pnet_macros", - "pnet_macros_support", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" @@ -3979,12 +4002,11 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.8.0" +version = "0.9.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "fb78a635f75d76d856374961deecf61031c0b6f928c83dc9c0924ab6c019c298" dependencies = [ "cpufeatures", - "opaque-debug", "universal-hash", ] @@ -3996,28 +4018,30 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portmapper" -version = "0.4.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "247dcb75747c53cc433d6d8963a064187eec4a676ba13ea33143f1c9100e754f" +checksum = "7b575f975dcf03e258b0c7ab3f81497d7124f508884c37da66a7314aa2a8d467" dependencies = [ "base64", "bytes", - "derive_more", + "derive_more 2.1.0", "futures-lite", "futures-util", + "hyper-util", "igd-next", "iroh-metrics", "libc", + "n0-error", "netwatch", "num_enum", - "rand 0.8.5", + "rand 0.9.2", "serde", "smallvec", - "socket2 0.5.10", - "thiserror 2.0.17", + "socket2 0.6.1", "time", "tokio", "tokio-util", + "tower-layer", "tracing", "url", ] @@ -4071,40 +4095,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precis-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2e7b31f132e0c6f8682cfb7bf4a5340dbe925b7986618d0826a56dfe0c8e56" -dependencies = [ - "precis-tools", - "ucd-parse", - "unicode-normalization", -] - -[[package]] -name = "precis-profiles" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e2768890a47af73a032af9f0cedbddce3c9d06cf8de201d5b8f2436ded7674" -dependencies = [ - "lazy_static", - "precis-core", - "precis-tools", - "unicode-normalization", -] - -[[package]] -name = "precis-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc1eb2d5887ac7bfd2c0b745764db89edb84b856e4214e204ef48ef96d10c4a" -dependencies = [ - "lazy_static", - "regex", - "ucd-parse", -] - [[package]] name = "pretty_assertions" version = "1.4.1" @@ -4209,22 +4199,32 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "quoted-string-parser" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc75379cdb451d001f1cb667a9f74e8b355e9df84cc5193513cbe62b96fc5e9" -dependencies = [ - "pest", - "pest_derive", -] - [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "ractor" +version = "0.15.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9500e0be6f12a0539cb1154d654ef2e888bf8529164e54aff4a097baad5bb001" +dependencies = [ + "bon", + "dashmap", + "futures", + "js-sys", + "once_cell", + "strum 0.26.3", + "tokio", + "tokio_with_wasm", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-time", +] + [[package]] name = "rand" version = "0.8.5" @@ -4293,26 +4293,13 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rcgen" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" -dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "yasna", -] - [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags", ] [[package]] @@ -4357,16 +4344,16 @@ dependencies = [ name = "reflection-node" version = "0.1.0" dependencies = [ - "async-trait", "chrono", "ciborium", "hex", "p2panda-core", - "p2panda-discovery", - "p2panda-net", + "p2panda-discovery-next", + "p2panda-net-next", "p2panda-store", "p2panda-stream", - "p2panda-sync", + "p2panda-sync-next", + "rand_chacha 0.9.0", "serde", "serde_bytes", "sqlx", @@ -4400,12 +4387,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-lite" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" - [[package]] name = "regex-syntax" version = "0.8.8" @@ -4414,15 +4395,15 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" dependencies = [ "base64", "bytes", "futures-core", "futures-util", - "http 1.4.0", + "http", "http-body", "http-body-util", "hyper", @@ -4450,7 +4431,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.4", + "webpki-roots", ] [[package]] @@ -4479,56 +4460,20 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ - "const-oid", - "digest", + "const-oid 0.9.6", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] -[[package]] -name = "rtnetlink" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" -dependencies = [ - "futures", - "log", - "netlink-packet-core", - "netlink-packet-route 0.17.1", - "netlink-packet-utils", - "netlink-proto", - "netlink-sys", - "nix 0.26.4", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "rtnetlink" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b684475344d8df1859ddb2d395dd3dac4f8f3422a1aa0725993cb375fc5caba5" -dependencies = [ - "futures", - "log", - "netlink-packet-core", - "netlink-packet-route 0.19.0", - "netlink-packet-utils", - "netlink-proto", - "netlink-sys", - "nix 0.27.1", - "thiserror 1.0.69", - "tokio", -] - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4544,22 +4489,13 @@ dependencies = [ "semver", ] -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - [[package]] name = "rustix" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.10.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -4576,7 +4512,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki", "subtle", "zeroize", ] @@ -4617,7 +4553,7 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.103.8", + "rustls-webpki", "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", @@ -4630,17 +4566,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.103.8" @@ -4666,10 +4591,11 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa20" -version = "0.10.2" +version = "0.11.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +checksum = "d3ff3b81c8a6e381bc1673768141383f9328048a60edddcfc752a8291a138443" dependencies = [ + "cfg-if", "cipher", ] @@ -4709,7 +4635,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.10.0", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4754,15 +4680,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-error" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342110fb7a5d801060c885da03bf91bfa7c7ca936deafcc64bb6706375605d47" -dependencies = [ - "serde", -] - [[package]] name = "serde_bytes" version = "0.11.19" @@ -4865,9 +4782,9 @@ dependencies = [ [[package]] name = "serdect" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2" dependencies = [ "base16ct", "serde", @@ -4877,22 +4794,50 @@ dependencies = [ name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e046edf639aa2e7afb285589e5405de2ef7e61d4b0ac1e30256e3eab911af9" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.0-rc.3", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.11.0-rc.3", ] [[package]] @@ -4925,17 +4870,29 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "3.0.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0251c9d6468f4ba853b6352b190fb7c1e405087779917c238445eb03993826" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simple-dns" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.10.0", + "bitflags", ] [[package]] @@ -4963,6 +4920,12 @@ dependencies = [ "serde", ] +[[package]] +name = "smol_str" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" + [[package]] name = "socket2" version = "0.5.10" @@ -5018,6 +4981,17 @@ dependencies = [ "system-deps", ] +[[package]] +name = "spez" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "spin" version = "0.9.8" @@ -5040,7 +5014,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der 0.8.0-rc.10", ] [[package]] @@ -5082,7 +5066,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "thiserror 2.0.17", "tokio", @@ -5119,7 +5103,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -5137,12 +5121,12 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64", - "bitflags 2.10.0", + "bitflags", "byteorder", "bytes", "chrono", "crc", - "digest", + "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -5162,8 +5146,8 @@ dependencies = [ "rand 0.8.5", "rsa", "serde", - "sha1", - "sha2", + "sha1 0.10.6", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -5180,7 +5164,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64", - "bitflags 2.10.0", + "bitflags", "byteorder", "chrono", "crc", @@ -5201,7 +5185,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -5265,41 +5249,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "struct_iterable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "849a064c6470a650b72e41fa6c057879b68f804d113af92900f27574828e7712" -dependencies = [ - "struct_iterable_derive", - "struct_iterable_internal", -] - -[[package]] -name = "struct_iterable_derive" -version = "0.1.0" +name = "strum" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb939ce88a43ea4e9d012f2f6b4cc789deb2db9d47bad697952a85d6978662c" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "erased-serde", - "proc-macro2", - "quote", - "struct_iterable_internal", - "syn 2.0.111", + "strum_macros 0.26.4", ] -[[package]] -name = "struct_iterable_internal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9426b2a0c03e6cc2ea8dbc0168dbbf943f88755e409fb91bcb8f6a268305f4a" - [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", ] [[package]] @@ -5316,27 +5280,15 @@ dependencies = [ ] [[package]] -name = "stun-rs" -version = "0.1.11" +name = "strum_macros" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb921f10397d5669e1af6455e9e2d367bf1f9cebcd6b1dd1dc50e19f6a9ac2ac" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "base64", - "bounded-integer", - "byteorder", - "crc", - "enumflags2", - "fallible-iterator", - "hmac-sha1", - "hmac-sha256", - "hostname-validator", - "lazy_static", - "md5", - "paste", - "precis-core", - "precis-profiles", - "quoted-string-parser", - "rand 0.9.2", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -5346,17 +5298,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "surge-ping" -version = "0.8.3" +name = "swarm-discovery" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27ea7b4bfbd3d9980392cd9f90e4158212a5f775fa58e9b85216a0bf739067d" +checksum = "790d8444f7db1e88f70aed3234cab8e42c48e05360bfc86ca7dce0d9a5d95d26" dependencies = [ - "hex", - "parking_lot", - "pnet_packet", + "acto", + "hickory-proto", "rand 0.9.2", - "socket2 0.6.1", - "thiserror 1.0.69", + "socket2 0.5.10", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -5409,7 +5360,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.10.0", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5546,13 +5497,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", - "itoa", "js-sys", "num-conv", "powerfmt", "serde", "time-core", - "time-macros", ] [[package]] @@ -5561,16 +5510,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -5609,6 +5548,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.6.1", "tokio-macros", + "tracing", "windows-sys 0.61.2", ] @@ -5646,48 +5586,64 @@ dependencies = [ ] [[package]] -name = "tokio-tungstenite" -version = "0.24.0" +name = "tokio-util" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", "futures-util", - "log", + "pin-project-lite", "tokio", - "tungstenite", ] [[package]] -name = "tokio-tungstenite-wasm" -version = "0.4.0" +name = "tokio-websockets" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e21a5c399399c3db9f08d8297ac12b500e86bca82e930253fdc62eaf9c0de6ae" +checksum = "b1b6348ebfaaecd771cecb69e832961d277f59845d4220a584701f72728152b7" dependencies = [ - "futures-channel", - "futures-util", - "http 1.4.0", + "base64", + "bytes", + "futures-core", + "futures-sink", + "getrandom 0.3.4", + "http", "httparse", + "rand 0.9.2", + "ring", + "rustls-pki-types", + "simdutf8", + "tokio", + "tokio-rustls", + "tokio-util", +] + +[[package]] +name = "tokio_with_wasm" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dfba9b946459940fb564dcf576631074cdfb0bfe4c962acd4c31f0dca7897e6" +dependencies = [ "js-sys", - "thiserror 1.0.69", "tokio", - "tokio-tungstenite", + "tokio_with_wasm_proc", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] [[package]] -name = "tokio-util" -version = "0.7.17" +name = "tokio_with_wasm_proc" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "37e04c1865c281139e5ccf633cb9f76ffdaabeebfe53b703984cf82878e2aabb" dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "pin-project-lite", - "tokio", + "quote", + "syn 2.0.111", ] [[package]] @@ -5716,9 +5672,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ "indexmap", "toml_datetime", @@ -5758,14 +5714,14 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags", "bytes", "futures-util", - "http 1.4.0", + "http", "http-body", "iri-string", "pin-project-lite", @@ -5865,24 +5821,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.4.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - [[package]] name = "twox-hash" version = "2.1.2" @@ -5895,21 +5833,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "ucd-parse" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06ff81122fcbf4df4c1660b15f7e3336058e7aec14437c9f85c6b31a0f279b9" -dependencies = [ - "regex-lite", -] - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "uds_windows" version = "1.1.0" @@ -5948,6 +5871,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5956,11 +5885,11 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "a55be643b40a21558f44806b53ee9319595bc7ca6896372e4e08e5d7d83c9cd6" dependencies = [ - "crypto-common", + "crypto-common 0.2.0-rc.4", "subtle", ] @@ -5970,21 +5899,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" -dependencies = [ - "base64", - "log", - "once_cell", - "rustls", - "rustls-pki-types", - "url", - "webpki-roots 0.26.11", -] - [[package]] name = "url" version = "2.5.7" @@ -5997,12 +5911,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -6017,13 +5925,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -6200,15 +6108,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.4", -] - [[package]] name = "webpki-roots" version = "1.0.4" @@ -6267,25 +6166,27 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.59.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.5", + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", ] [[package]] name = "windows" -version = "0.61.3" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -6298,16 +6199,12 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.59.0" +name = "windows-collections" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement 0.59.0", - "windows-interface", - "windows-result 0.3.4", - "windows-strings 0.3.1", - "windows-targets 0.53.5", + "windows-core 0.62.2", ] [[package]] @@ -6316,7 +6213,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.2", + "windows-implement", "windows-interface", "windows-link 0.1.3", "windows-result 0.3.4", @@ -6329,7 +6226,7 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", + "windows-implement", "windows-interface", "windows-link 0.2.1", "windows-result 0.4.1", @@ -6344,18 +6241,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", ] [[package]] -name = "windows-implement" -version = "0.59.0" +name = "windows-future" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6402,6 +6299,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -6420,15 +6327,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -6573,6 +6471,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6780,17 +6687,17 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wmi" -version = "0.14.5" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7787dacdd8e71cbc104658aade4009300777f9b5fda6a75f19145fedb8a18e71" +checksum = "120d8c2b6a7c96c27bf4a7947fd7f02d73ca7f5958b8bd72a696e46cb5521ee6" dependencies = [ "chrono", "futures", "log", "serde", "thiserror 2.0.17", - "windows 0.59.0", - "windows-core 0.59.0", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] @@ -6800,20 +6707,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "x509-parser" -version = "0.16.0" +name = "ws_stream_wasm" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror 1.0.69", - "time", + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper", + "thiserror 2.0.17", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -6843,15 +6752,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - [[package]] name = "yoke" version = "0.8.1" @@ -6901,7 +6801,7 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix 0.30.1", + "nix", "ordered-stream", "serde", "serde_repr", diff --git a/reflection-node/Cargo.toml b/reflection-node/Cargo.toml index e2f1f33..2bc978a 100644 --- a/reflection-node/Cargo.toml +++ b/reflection-node/Cargo.toml @@ -10,20 +10,20 @@ authors = [ [dependencies] thiserror = "2.0.17" -async-trait = "0.1.89" chrono = "0.4.42" ciborium = "0.2.2" -p2panda-core = { git = "https://github.com/p2panda/p2panda" } -p2panda-discovery = { git = "https://github.com/p2panda/p2panda", features = ["mdns"] } -p2panda-net = { git = "https://github.com/p2panda/p2panda" } -p2panda-store = { git = "https://github.com/p2panda/p2panda", features = ["sqlite"], default-features = false} -p2panda-stream = { git = "https://github.com/p2panda/p2panda" } -p2panda-sync = { git = "https://github.com/p2panda/p2panda", features = ["log-sync"] } +p2panda-core = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436" } +p2panda-discovery = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436", package = "p2panda-discovery-next" } +p2panda-net = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436", package = "p2panda-net-next" } +p2panda-store = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436", features = ["sqlite"], default-features = false } +p2panda-stream = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436" } +p2panda-sync = { git = "https://github.com/p2panda/p2panda", rev = "a83a80b2ce0733c9437ceeaae33503d3b3742436", package = "p2panda-sync-next" } serde = { version = "1.0.228", features = ["derive"] } serde_bytes = "0.11.19" -sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "chrono"], default-features = false} +sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "chrono"], default-features = false } tokio = { version = "1.48.0", features = ["rt", "sync"] } tokio-stream = "0.1.17" tracing = "0.1" test-log = { version = "0.2.18", default-features = false, features = ["trace", "color"] } hex = "0.4.3" +rand_chacha = { version = "0.9.0", features = ["os_rng"] } diff --git a/reflection-node/src/author_tracker.rs b/reflection-node/src/author_tracker.rs index 693f62e..7320ae2 100644 --- a/reflection-node/src/author_tracker.rs +++ b/reflection-node/src/author_tracker.rs @@ -10,11 +10,8 @@ use crate::node_inner::NodeInner; use chrono::Utc; use p2panda_core::cbor::{DecodeError, decode_cbor, encode_cbor}; use p2panda_core::{PrivateKey, PublicKey}; -use p2panda_net::ToNetwork; -use tokio::{ - sync::mpsc, - sync::{Mutex, RwLock}, -}; +use p2panda_net::streams::EphemeralStream; +use tokio::sync::{Mutex, RwLock}; use tracing::error; const OFFLINE_TIMEOUT: Duration = Duration::from_secs(60); @@ -50,7 +47,7 @@ pub struct AuthorTracker { last_ping: Mutex>, document: Arc, node: Arc, - tx: RwLock>>, + tx: RwLock>, } impl AuthorTracker { @@ -63,7 +60,7 @@ impl AuthorTracker { }) } - pub async fn set_document_tx(&self, tx: Option>) { + pub async fn set_document_tx(&self, tx: Option) { let mut tx_guard = self.tx.write().await; // Send good bye message to the network if let Some(tx) = tx_guard.as_ref() { @@ -177,11 +174,7 @@ impl AuthorTracker { } } -async fn send_message( - private_key: &PrivateKey, - tx: &mpsc::Sender, - message: AuthorMessage, -) { +async fn send_message(private_key: &PrivateKey, tx: &EphemeralStream, message: AuthorMessage) { // FIXME: We need to add the current time to the message, // because iroh doesn't broadcast twice the same message message. let author_message = match encode_cbor(&(&message, SystemTime::now())) { @@ -199,7 +192,7 @@ async fn send_message( return; } }; - if let Err(error) = tx.send(ToNetwork::Message { bytes }).await { + if let Err(error) = tx.publish(bytes).await { error!("Failed to sent {message} to the network: {error}"); } } diff --git a/reflection-node/src/document.rs b/reflection-node/src/document.rs index 9eda405..44f9c15 100644 --- a/reflection-node/src/document.rs +++ b/reflection-node/src/document.rs @@ -1,53 +1,18 @@ -use std::fmt; -use std::hash::Hash; use std::sync::Arc; +use crate::operation::ReflectionExtensions; use crate::operation_store::CreationError; use crate::subscription_inner::SubscriptionInner; -use p2panda_core::PublicKey; -use p2panda_net::{ToNetwork, TopicId}; -use p2panda_sync::TopicQuery; -use serde::{Deserialize, Serialize}; +use p2panda_core::{Operation, PublicKey}; +use p2panda_net::streams::StreamError; use thiserror::Error; -use tokio::{ - sync::mpsc, - task::{AbortHandle, JoinError}, -}; +use tokio::task::{AbortHandle, JoinError}; use tracing::info; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub(crate) struct DocumentId(#[serde(with = "serde_bytes")] [u8; 32]); - -impl DocumentId { - pub const fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } -} - -impl TopicQuery for DocumentId {} - -impl TopicId for DocumentId { - fn id(&self) -> [u8; 32] { - self.0 - } -} - -impl From<[u8; 32]> for DocumentId { - fn from(bytes: [u8; 32]) -> Self { - Self(bytes) - } -} - -impl From for [u8; 32] { - fn from(id: DocumentId) -> Self { - id.0 - } -} - -impl fmt::Display for DocumentId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&hex::encode(self.0)) +impl From>> for DocumentError { + fn from(value: StreamError>) -> Self { + DocumentError::Publish(Box::new(value)) } } @@ -60,7 +25,10 @@ pub enum DocumentError { #[error(transparent)] Encode(#[from] p2panda_core::cbor::EncodeError), #[error(transparent)] - Send(#[from] mpsc::error::SendError), + // FIXME: The error is huge so but it into a Box + Publish(Box>>), + #[error(transparent)] + PublishEphemeral(#[from] StreamError>), #[error(transparent)] Runtime(#[from] JoinError), } @@ -132,7 +100,7 @@ impl Subscription { .spawn(async move { inner.unsubscribe().await }) .await??; - info!("Unsubscribed from document {}", document_id); + info!("Unsubscribed from document {}", hex::encode(document_id)); Ok(()) } diff --git a/reflection-node/src/document_store.rs b/reflection-node/src/document_store.rs index 26760bd..232b5e9 100644 --- a/reflection-node/src/document_store.rs +++ b/reflection-node/src/document_store.rs @@ -1,28 +1,22 @@ use std::collections::HashMap; use std::hash::Hash as StdHash; -use async_trait::async_trait; use chrono::{DateTime, Utc}; use p2panda_core::PublicKey; +use p2panda_net::TopicId; use p2panda_store::LogStore; -use p2panda_sync::log_sync::TopicLogMap; +use p2panda_sync::{log_sync::Logs, topic_log_sync::TopicLogMap}; use serde::{Deserialize, Serialize}; -use sqlx::{ - Decode, Encode, FromRow, Row, Sqlite, Type, - encode::IsNull, - error::BoxDynError, - sqlite::{SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef}, -}; +use sqlx::{FromRow, Row}; use tracing::error; -use crate::document::DocumentId; use crate::operation::{LogType, ReflectionExtensions}; use crate::operation_store::OperationStore; #[derive(Debug, FromRow)] pub struct StoreDocument { - #[sqlx(rename = "document_id")] - pub id: DocumentId, + #[sqlx(try_from = "Vec", rename = "document_id")] + pub id: TopicId, #[sqlx(default)] pub name: Option, pub last_accessed: Option>, @@ -36,33 +30,6 @@ pub struct Author { pub last_seen: Option>, } -impl Type for DocumentId { - fn type_info() -> SqliteTypeInfo { - <&[u8] as Type>::type_info() - } - - fn compatible(ty: &SqliteTypeInfo) -> bool { - <&[u8] as Type>::compatible(ty) - } -} - -impl<'q> Encode<'q, Sqlite> for &'q DocumentId { - fn encode_by_ref( - &self, - args: &mut Vec>, - ) -> Result { - <&[u8] as Encode>::encode_by_ref(&self.as_slice(), args) - } -} - -impl Decode<'_, Sqlite> for DocumentId { - fn decode(value: SqliteValueRef<'_>) -> Result { - let value = <&[u8] as Decode>::decode(value)?; - - Ok(DocumentId::from(TryInto::<[u8; 32]>::try_into(value)?)) - } -} - #[derive(Clone, Debug)] pub struct DocumentStore { pool: sqlx::SqlitePool, @@ -73,9 +40,9 @@ impl DocumentStore { Self { pool } } - async fn authors(&self, document_id: &DocumentId) -> sqlx::Result> { + async fn authors(&self, document_id: &TopicId) -> sqlx::Result> { let list = sqlx::query("SELECT public_key FROM authors WHERE document_id = ?") - .bind(document_id) + .bind(document_id.as_slice()) .fetch_all(&self.pool) .await?; @@ -95,7 +62,7 @@ impl DocumentStore { .await?; let mut authors_per_document = authors.iter().fold(HashMap::new(), |mut acc, row| { - let Ok(document_id) = row.try_get::("document_id") else { + let Ok(document_id) = TopicId::try_from(row.get::<&[u8], _>("document_id")) else { return acc; }; let Ok(public_key) = PublicKey::try_from(row.get::<&[u8], _>("public_key")) else { @@ -122,7 +89,7 @@ impl DocumentStore { Ok(documents) } - pub async fn add_document(&self, document_id: &DocumentId) -> sqlx::Result<()> { + pub async fn add_document(&self, document_id: &TopicId) -> sqlx::Result<()> { // The document_id is the primary key in the table therefore ignore insertion when the document exists already sqlx::query( " @@ -130,16 +97,16 @@ impl DocumentStore { VALUES ( ? ) ", ) - .bind(document_id) + .bind(document_id.as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn delete_document(&self, document_id: &DocumentId) -> sqlx::Result<()> { + pub async fn delete_document(&self, document_id: &TopicId) -> sqlx::Result<()> { sqlx::query("DELETE FROM documents WHERE document_id = ?") - .bind(document_id) + .bind(document_id.as_slice()) .execute(&self.pool) .await?; @@ -148,7 +115,7 @@ impl DocumentStore { pub async fn add_author( &self, - document_id: &DocumentId, + document_id: &TopicId, public_key: &PublicKey, ) -> sqlx::Result<()> { // The author/document_id pair is required to be unique therefore ignore if the insertion fails @@ -159,7 +126,7 @@ impl DocumentStore { ", ) .bind(public_key.as_bytes().as_slice()) - .bind(document_id) + .bind(document_id.as_slice()) .execute(&self.pool) .await?; @@ -188,7 +155,7 @@ impl DocumentStore { pub async fn set_name_for_document( &self, - document_id: &DocumentId, + document_id: &TopicId, name: Option, ) -> sqlx::Result<()> { sqlx::query( @@ -199,7 +166,7 @@ impl DocumentStore { ", ) .bind(name) - .bind(document_id) + .bind(document_id.as_slice()) .execute(&self.pool) .await?; @@ -208,7 +175,7 @@ impl DocumentStore { pub async fn set_last_accessed_for_document( &self, - document_id: &DocumentId, + document_id: &TopicId, last_accessed: Option>, ) -> sqlx::Result<()> { sqlx::query( @@ -219,7 +186,7 @@ impl DocumentStore { ", ) .bind(last_accessed) - .bind(document_id) + .bind(document_id.as_slice()) .execute(&self.pool) .await?; @@ -229,7 +196,7 @@ impl DocumentStore { pub async fn operations_for_document( &self, operation_store: &OperationStore, - document_id: &DocumentId, + document_id: &TopicId, ) -> sqlx::Result>> { let operation_store = operation_store.inner(); let authors = self.authors(document_id).await?; @@ -273,29 +240,27 @@ impl DocumentStore { } #[derive(Clone, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)] -pub struct LogId(LogType, DocumentId); +pub struct LogId(LogType, TopicId); impl LogId { - pub fn new(log_type: LogType, document: &DocumentId) -> Self { + pub fn new(log_type: LogType, document: &TopicId) -> Self { Self(log_type, *document) } } -#[async_trait] -impl TopicLogMap for DocumentStore { - async fn get(&self, topic: &DocumentId) -> Option>> { - let Ok(authors) = self.authors(topic).await else { - return None; - }; +impl TopicLogMap for DocumentStore { + type Error = sqlx::Error; + + async fn get(&self, topic: &TopicId) -> Result, Self::Error> { + let authors = self.authors(topic).await?; + let log_ids = [ LogId::new(LogType::Delta, topic), LogId::new(LogType::Snapshot, topic), ]; - Some( - authors - .into_iter() - .map(|author| (author, log_ids.to_vec())) - .collect(), - ) + Ok(authors + .into_iter() + .map(|author| (author, log_ids.to_vec())) + .collect()) } } diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index f4f0da4..5553d31 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -6,7 +6,6 @@ pub mod node; mod node_inner; mod operation; mod operation_store; -mod persistent_operation; mod subscription_inner; mod utils; diff --git a/reflection-node/src/node.rs b/reflection-node/src/node.rs index dfb691f..891675c 100644 --- a/reflection-node/src/node.rs +++ b/reflection-node/src/node.rs @@ -3,10 +3,11 @@ use std::sync::Arc; use chrono::{DateTime, Utc}; use p2panda_core::{Hash, PrivateKey}; +use p2panda_net::TopicId; use thiserror::Error; use tracing::info; -use crate::document::{DocumentError, DocumentId, SubscribableDocument, Subscription}; +use crate::document::{DocumentError, SubscribableDocument, Subscription}; pub use crate::document_store::Author; use crate::document_store::StoreDocument; use crate::node_inner::NodeInner; @@ -134,7 +135,7 @@ impl Node { authors, } = document; Document { - id: <[u8; 32]>::from(id).into(), + id: id.into(), name, last_accessed, authors, @@ -150,7 +151,7 @@ impl Node { document_id: ID, document_handle: T, ) -> Result, DocumentError> { - let document_id: DocumentId = DocumentId::from(document_id.into()); + let document_id: TopicId = document_id.into(); let document_handle = Arc::new(document_handle); let inner_clone = self.inner.clone(); let inner_subscription = self @@ -159,7 +160,7 @@ impl Node { .await??; let subscription = Subscription::new(self.runtime.clone(), inner_subscription).await; - info!("Subscribed to topic {}", document_id); + info!("Subscribed to topic {}", hex::encode(document_id)); Ok(subscription) } @@ -168,7 +169,7 @@ impl Node { &self, document_id: ID, ) -> Result<(), DocumentError> { - let document_id: DocumentId = DocumentId::from(document_id.into()); + let document_id: TopicId = document_id.into(); let inner_clone = self.inner.clone(); self.runtime .spawn(async move { inner_clone.delete_document(document_id).await }) diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index 8e2a2f9..e328490 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -1,29 +1,36 @@ -use std::ops::DerefMut; use std::path::PathBuf; use std::sync::Arc; -use crate::document::{DocumentError, DocumentId, SubscribableDocument}; -use crate::document_store::DocumentStore; +use crate::document::{DocumentError, SubscribableDocument}; +use crate::document_store::{DocumentStore, LogId}; use crate::ephemerial_operation::EphemerialOperation; use crate::node::{ConnectionMode, NodeError}; +use crate::operation::ReflectionExtensions; use crate::operation_store::OperationStore; -use crate::persistent_operation::PersistentOperation; use crate::subscription_inner::SubscriptionInner; use crate::utils::CombinedMigrationSource; use p2panda_core::{Hash, PrivateKey}; -use p2panda_discovery::mdns::LocalDiscovery; -use p2panda_net::config::GossipConfig; -use p2panda_net::{Network, NetworkBuilder, SyncConfiguration}; +use p2panda_discovery::address_book::memory::MemoryStore as MemoryAddressBook; +use p2panda_net::{MdnsDiscoveryMode, TopicId}; +use p2panda_net::{Network, NetworkBuilder}; use p2panda_store::sqlite::store::migrations as operation_store_migrations; -use p2panda_sync::log_sync::LogSyncProtocol; +use p2panda_sync::managers::topic_sync_manager::TopicSyncManagerConfig; +use rand_chacha::rand_core::SeedableRng; use sqlx::{migrate::Migrator, sqlite}; use tokio::sync::{Notify, RwLock}; use tracing::{info, warn}; +pub type TopicSyncManager = p2panda_sync::managers::topic_sync_manager::TopicSyncManager< + TopicId, + p2panda_store::SqliteStore, + DocumentStore, + LogId, + ReflectionExtensions, +>; + #[derive(Debug, serde::Serialize, serde::Deserialize)] pub(crate) enum MessageType { - Persistent(PersistentOperation), Ephemeral(EphemerialOperation), AuthorEphemeral(EphemerialOperation), } @@ -34,13 +41,10 @@ pub struct NodeInner { pub(crate) document_store: DocumentStore, pub(crate) private_key: PrivateKey, pub(crate) network_id: Hash, - pub(crate) network: RwLock>>, + pub(crate) network: RwLock>>, pub(crate) network_notifier: Notify, } -//const RELAY_URL: &str = "https://staging-euw1-1.relay.iroh.network/"; -//const BOOTSTRAP_NODE_ID: &str = "d825a2f929f935efcd6889bed5c3f5510b40f014969a729033d3fb7e33b97dbe"; - impl NodeInner { pub async fn new( network_id: Hash, @@ -118,30 +122,18 @@ impl NodeInner { } }; - let old_network = std::mem::replace(network_guard.deref_mut(), network); - - if let Some(old_network) = old_network { - // FIXME: For some reason we shutdown before the bye message is actually send - // This doesn't happen when shutting down the entire node, maybe because tokio is more busy here? - if let Err(error) = old_network.shutdown().await { - warn!("Failed to shutdown network: {error}"); - } - } + *network_guard = network; } pub async fn shutdown(&self) { // Wake up all subscriptions that may still exist self.network_notifier.notify_waiters(); - if let Some(network) = self.network.write().await.take() - && let Err(error) = network.shutdown().await - { - warn!("Failed to shutdown network: {error}"); - } + self.network.write().await.take(); } pub async fn subscribe( self: Arc, - document_id: DocumentId, + document_id: TopicId, document: Arc, ) -> Result, DocumentError> { self.document_store.add_document(&document_id).await?; @@ -167,7 +159,7 @@ impl NodeInner { pub async fn delete_document( self: Arc, - document_id: DocumentId, + document_id: TopicId, ) -> Result<(), DocumentError> { self.document_store.delete_document(&document_id).await?; Ok(()) @@ -179,33 +171,16 @@ async fn setup_network( network_id: &Hash, document_store: &DocumentStore, operation_store: &OperationStore, -) -> Option> { - let sync_config = { - let sync = LogSyncProtocol::new(document_store.clone(), operation_store.clone_inner()); - SyncConfiguration::::new(sync) +) -> Option> { + let address_book = MemoryAddressBook::new(rand_chacha::ChaCha20Rng::from_os_rng()); + let sync_conf = TopicSyncManagerConfig { + store: operation_store.clone_inner(), + topic_map: document_store.clone(), }; - let network = NetworkBuilder::new(network_id.into()) .private_key(private_key.clone()) - .discovery(LocalDiscovery::new()) - // NOTE(glyph): Internet networking is disabled until we can fix the - // more-than-two-peers gossip issue. - // - //.relay(RELAY_URL.parse().expect("valid relay URL"), false, 0) - //.direct_address( - // BOOTSTRAP_NODE_ID.parse().expect("valid node ID"), - // vec![], - // None, - //) - .gossip(GossipConfig { - // FIXME: This is a temporary workaround to account for larger delta patches (for - // example when the user Copy & Pastes a big chunk of text). - // - // Related issue: https://github.com/p2panda/reflection/issues/24 - max_message_size: 512_000, - }) - .sync(sync_config) - .build() + .mdns(MdnsDiscoveryMode::Active) + .build(address_book, sync_conf) .await; if let Err(error) = network { diff --git a/reflection-node/src/operation.rs b/reflection-node/src/operation.rs index 7c5d830..d3bfba0 100644 --- a/reflection-node/src/operation.rs +++ b/reflection-node/src/operation.rs @@ -1,9 +1,9 @@ use std::hash::Hash as StdHash; use p2panda_core::{Extension, Header, PruneFlag}; +use p2panda_net::TopicId; use serde::{Deserialize, Serialize}; -use crate::document::DocumentId; use crate::document_store::LogId; /// Custom extensions for p2panda header. @@ -34,7 +34,7 @@ pub struct ReflectionExtensions { /// Identifier of the document this operation relates to. #[serde(rename = "d")] - pub document: DocumentId, + pub document: TopicId, } #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)] @@ -56,8 +56,8 @@ impl Extension for ReflectionExtensions { } } -impl Extension for ReflectionExtensions { - fn extract(header: &Header) -> Option { +impl Extension for ReflectionExtensions { + fn extract(header: &Header) -> Option { Some(header.extensions.document) } } @@ -65,7 +65,7 @@ impl Extension for ReflectionExtensions { impl Extension for ReflectionExtensions { fn extract(header: &Header) -> Option { let log_type: LogType = header.extension()?; - let document_id: DocumentId = header.extension()?; + let document_id: TopicId = header.extension()?; Some(LogId::new(log_type, &document_id)) } diff --git a/reflection-node/src/operation_store.rs b/reflection-node/src/operation_store.rs index abaaf98..46b20a4 100644 --- a/reflection-node/src/operation_store.rs +++ b/reflection-node/src/operation_store.rs @@ -1,9 +1,9 @@ use std::sync::Arc; use std::time::{SystemTime, SystemTimeError}; -use crate::document::DocumentId; use crate::document_store::LogId; use p2panda_core::{Body, Header, Operation, PrivateKey, PruneFlag}; +use p2panda_net::TopicId; use p2panda_store::{ LogStore, OperationStore as TraitOperationStore, SqliteStore, SqliteStoreError, }; @@ -52,7 +52,7 @@ impl OperationStore { &self, private_key: &PrivateKey, log_type: LogType, - document: DocumentId, + document: TopicId, body: Option<&[u8]>, prune_flag: bool, ) -> Result, CreationError> { diff --git a/reflection-node/src/subscription_inner.rs b/reflection-node/src/subscription_inner.rs index 20b19a5..3c507fe 100644 --- a/reflection-node/src/subscription_inner.rs +++ b/reflection-node/src/subscription_inner.rs @@ -3,12 +3,17 @@ use std::ops::{Deref, DerefMut, Drop}; use std::sync::Arc; use chrono::Utc; +use p2panda_core::Hash; use p2panda_core::{ Body, Header, cbor::{decode_cbor, encode_cbor}, }; -use p2panda_net::{FromNetwork, Network, ToNetwork}; +use p2panda_net::{ + Network, TopicId, + streams::{EphemeralStream, EventuallyConsistentStream}, +}; use p2panda_stream::IngestExt; +use p2panda_sync::protocols::topic_log_sync::TopicLogSyncEvent; use tokio::{ sync::{RwLock, mpsc}, task::{AbortHandle, spawn}, @@ -17,17 +22,17 @@ use tokio_stream::{StreamExt, wrappers::ReceiverStream}; use tracing::{error, info, warn}; use crate::author_tracker::{AuthorMessage, AuthorTracker}; -use crate::document::{DocumentError, DocumentId, SubscribableDocument}; +use crate::document::{DocumentError, SubscribableDocument}; use crate::ephemerial_operation::EphemerialOperation; use crate::node_inner::MessageType; -use crate::node_inner::NodeInner; +use crate::node_inner::{NodeInner, TopicSyncManager}; use crate::operation::{LogType, ReflectionExtensions}; -use crate::persistent_operation::PersistentOperation; pub struct SubscriptionInner { - tx: RwLock>>, + ephemeral_tx: RwLock>, + tx: RwLock>>, pub(crate) node: Arc, - pub(crate) id: DocumentId, + pub(crate) id: TopicId, pub(crate) document: Arc, author_tracker: Arc>, abort_handles: RwLock>, @@ -42,10 +47,11 @@ impl Drop for SubscriptionInner { } impl SubscriptionInner { - pub fn new(node: Arc, id: DocumentId, document: Arc) -> Self { + pub fn new(node: Arc, id: TopicId, document: Arc) -> Self { let author_tracker = AuthorTracker::new(node.clone(), document.clone()); SubscriptionInner { tx: RwLock::new(None), + ephemeral_tx: RwLock::new(None), node, id, abort_handles: RwLock::new(Vec::new()), @@ -60,20 +66,22 @@ impl SubscriptionInner { let mut notify = Some(self.node.network_notifier.notified()); let mut network_guard = Some(self.node.network.read().await); - let (tx, abort_handles) = if let Some(network) = network_guard.as_ref().unwrap().deref() { - setup_network( - &self.node, - network, - self.id, - &self.document, - &self.author_tracker, - ) - .await - } else { - (None, Vec::new()) - }; + let (tx, ephemeral_tx, abort_handles) = + if let Some(network) = network_guard.as_ref().unwrap().deref() { + setup_network( + &self.node, + network, + self.id, + &self.document, + &self.author_tracker, + ) + .await + } else { + (None, None, Vec::new()) + }; *self.tx.write().await = tx; + *self.ephemeral_tx.write().await = ephemeral_tx; *self.abort_handles.write().await = abort_handles; loop { @@ -83,40 +91,52 @@ impl SubscriptionInner { let mut abort_handles_guard = self.abort_handles.write().await; let mut tx_guard = self.tx.write().await; + let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; let old_tx = take(tx_guard.deref_mut()); + let old_ephemeral_tx = take(ephemeral_tx_guard.deref_mut()); let old_abort_handles = take(abort_handles_guard.deref_mut()); - teardown_network(&self.id, &self.author_tracker, old_tx, old_abort_handles).await; + teardown_network( + &self.id, + &self.author_tracker, + old_tx, + old_ephemeral_tx, + old_abort_handles, + ) + .await; // Release network lock and get a new one, so that the network can be change between them network_guard.take(); notify = Some(self.node.network_notifier.notified()); network_guard = Some(self.node.network.read().await); - let (tx, abort_handles) = if let Some(network) = network_guard.as_ref().unwrap().deref() - { - setup_network( - &self.node, - network, - self.id, - &self.document, - &self.author_tracker, - ) - .await - } else { - (None, Vec::new()) - }; + let (tx, ephemeral_tx, abort_handles) = + if let Some(network) = network_guard.as_ref().unwrap().deref() { + setup_network( + &self.node, + network, + self.id, + &self.document, + &self.author_tracker, + ) + .await + } else { + (None, None, Vec::new()) + }; *tx_guard = tx; + *ephemeral_tx_guard = ephemeral_tx; *abort_handles_guard = abort_handles; } } pub async fn unsubscribe(&self) -> Result<(), DocumentError> { let mut tx_guard = self.tx.write().await; + let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; let mut abort_handles_guard = self.abort_handles.write().await; let tx = take(tx_guard.deref_mut()); + let ephemeral_tx = take(ephemeral_tx_guard.deref_mut()); let abort_handles = take(abort_handles_guard.deref_mut()); self.node @@ -124,7 +144,14 @@ impl SubscriptionInner { .set_last_accessed_for_document(&self.id, Some(Utc::now())) .await?; - teardown_network(&self.id, &self.author_tracker, tx, abort_handles).await; + teardown_network( + &self.id, + &self.author_tracker, + tx, + ephemeral_tx, + abort_handles, + ) + .await; Ok(()) } @@ -142,15 +169,13 @@ impl SubscriptionInner { ) .await?; - info!("Delta operation sent for document with id {}", self.id); + info!( + "Delta operation sent for document with id {}", + hex::encode(self.id) + ); if let Some(tx) = self.tx.read().await.as_ref() { - let bytes = encode_cbor(&MessageType::Persistent(PersistentOperation::new( - operation, - )))?; - - // Broadcast operation on gossip overlay. - tx.send(ToNetwork::Message { bytes }).await?; + tx.publish(operation).await?; } Ok(()) @@ -186,25 +211,23 @@ impl SubscriptionInner { .create_operation(&self.node.private_key, LogType::Delta, self.id, None, true) .await?; - info!("Snapshot saved for document with id {}", self.id); + info!( + "Snapshot saved for document with id {}", + hex::encode(self.id) + ); if let Some(tx) = self.tx.read().await.as_ref() { - let bytes = encode_cbor(&MessageType::Persistent(PersistentOperation::new( - operation, - )))?; - - // Broadcast operation on gossip overlay. - tx.send(ToNetwork::Message { bytes }).await?; + tx.publish(operation).await?; } Ok(()) } pub async fn send_ephemeral(&self, data: Vec) -> Result<(), DocumentError> { - if let Some(tx) = self.tx.read().await.as_ref() { + if let Some(ephemeral_tx) = self.ephemeral_tx.read().await.as_ref() { let operation = EphemerialOperation::new(data, &self.node.private_key); let bytes = encode_cbor(&MessageType::Ephemeral(operation))?; - tx.send(ToNetwork::Message { bytes }).await?; + ephemeral_tx.publish(bytes).await?; } Ok(()) @@ -223,81 +246,100 @@ impl SubscriptionInner { } } +// FIXME: return errors async fn setup_network( node: &Arc, - network: &Network, - document_id: DocumentId, + network: &Network, + document_id: TopicId, document: &Arc, author_tracker: &Arc>, -) -> (Option>, Vec) { +) -> ( + Option>, + Option, + Vec, +) { let mut abort_handles = Vec::with_capacity(3); - let (document_tx, mut document_rx, gossip_ready) = match network.subscribe(document_id).await { + let stream = match network.stream(document_id, true).await { Ok(result) => result, Err(error) => { - warn!("Failed to setup network for subscription to document {document_id}: {error}"); - return (None, abort_handles); + warn!( + "Failed to setup network for subscription to document {}: {error}", + hex::encode(document_id) + ); + return (None, None, abort_handles); } }; + let mut document_rx = stream.subscribe().await.unwrap(); + let document_tx = stream; + let (persistent_tx, persistent_rx) = mpsc::channel::<(Header, Option, Vec)>(128); - author_tracker - .set_document_tx(Some(document_tx.clone())) - .await; + let abort_handle = spawn(async move { + while let Ok(event) = document_rx.recv().await { + match event.event() { + TopicLogSyncEvent::Operation(operation) => { + match validate_and_unpack(operation.as_ref().to_owned(), document_id) { + Ok(data) => { + persistent_tx.send(data).await.unwrap(); + } + Err(err) => { + error!("Failed to unpack operation: {err}"); + } + } + } + _ => { + // TODO: Handle sync events + } + } + } + }) + .abort_handle(); + + abort_handles.push(abort_handle); + + // Generate a different topic than eventually consistent streams to avoid collisions. + // + // @TODO(adz): We want to throw an error if users try to subscribe with the same topic across + // different streams. + let topic = Hash::new(document_id); + let ephemeral_stream = network.ephemeral_stream(topic.into()).await.unwrap(); + let mut ephemeral_rx = ephemeral_stream.subscribe().await.unwrap(); + let ephemeral_tx = ephemeral_stream; + + author_tracker.set_document_tx(Some(ephemeral_tx)).await; let author_tracker_clone = author_tracker.clone(); let document_clone = document.clone(); let abort_handle = spawn(async move { - while let Some(event) = document_rx.recv().await { - match event { - FromNetwork::GossipMessage { bytes, .. } => match decode_cbor(&bytes[..]) { - Ok(MessageType::Ephemeral(operation)) => { - if let Some((author, body)) = operation.validate_and_unpack() { - document_clone.ephemeral_bytes_received(author, body); - } else { - warn!("Got ephemeral operation with a bad signature"); - } + while let Ok(bytes) = ephemeral_rx.recv().await { + match decode_cbor(&bytes[..]) { + Ok(MessageType::Ephemeral(operation)) => { + if let Some((author, body)) = operation.validate_and_unpack() { + document_clone.ephemeral_bytes_received(author, body); + } else { + warn!("Got ephemeral operation with a bad signature"); } - Ok(MessageType::AuthorEphemeral(operation)) => { - if let Some((author, body)) = operation.validate_and_unpack() { - match AuthorMessage::try_from(&body[..]) { - Ok(message) => { - author_tracker_clone.received(message, author).await; - } - Err(error) => { - warn!("Failed to deserialize AuthorMessage: {error}"); - } + } + Ok(MessageType::AuthorEphemeral(operation)) => { + if let Some((author, body)) = operation.validate_and_unpack() { + match AuthorMessage::try_from(&body[..]) { + Ok(message) => { + author_tracker_clone.received(message, author).await; } - } else { - warn!("Got internal ephemeral operation with a bad signature"); - } - } - Ok(MessageType::Persistent(operation)) => { - match operation.validate_and_unpack(document_id) { - Ok(data) => { - persistent_tx.send(data).await.unwrap(); - } - Err(err) => { - error!("Failed to unpack operation: {err}"); + Err(error) => { + warn!("Failed to deserialize AuthorMessage: {error}"); } } + } else { + warn!("Got internal ephemeral operation with a bad signature"); } - Err(err) => { - error!("Failed to decode gossip message: {err}"); - } - }, - FromNetwork::SyncMessage { - header, payload, .. - } => match PersistentOperation::from_serialized(header, payload) - .validate_and_unpack(document_id) - { - Ok(data) => persistent_tx.send(data).await.unwrap(), - Err(err) => { - error!("Failed to unpack operation: {err}"); - } - }, + } + Err(err) => { + error!("Failed to decode gossip message: {err}"); + } } } }) @@ -352,26 +394,28 @@ async fn setup_network( abort_handles.push(abort_handle); let author_tracker_clone = author_tracker.clone(); let abort_handle = spawn(async move { - // Only start track authors once we have joined the gossip overlay - if let Err(error) = gossip_ready.await { - error!("Failed to join the gossip overlay: {error}"); - } - author_tracker_clone.spawn().await; }) .abort_handle(); abort_handles.push(abort_handle); - info!("Network subscription set up for document {document_id}"); + info!( + "Network subscription set up for document {}", + hex::encode(document_id) + ); - (Some(document_tx), abort_handles) + let topic = Hash::new(document_id); + let ephemeral_tx = network.ephemeral_stream(topic.into()).await.unwrap(); + + (Some(document_tx), Some(ephemeral_tx), abort_handles) } async fn teardown_network( - document_id: &DocumentId, + document_id: &TopicId, author_tracker: &Arc>, - tx: Option>, + tx: Option>, + ephemeral_tx: Option, abort_handles: Vec, ) { for handle in abort_handles { @@ -380,7 +424,52 @@ async fn teardown_network( author_tracker.set_document_tx(None).await; - if tx.is_some() { - info!("Network subscription torn down for document {document_id}"); + if let Some(ephemeral_tx) = ephemeral_tx + && let Err(error) = ephemeral_tx.close() + { + error!( + "Failed to tear down ephemeral channel for document {}: {error}", + hex::encode(document_id) + ); } + + if let Some(tx) = tx { + if let Err(error) = tx.close() { + error!( + "Failed to tear down persistent channel for document {}: {error}", + hex::encode(document_id) + ); + } + info!( + "Network subscription torn down for document {}", + hex::encode(document_id) + ); + } +} + +type OperationWithRawHeader = (Header, Option, Vec); + +#[derive(Debug, thiserror::Error)] +pub enum UnpackError { + #[error(transparent)] + Cbor(#[from] p2panda_core::cbor::DecodeError), + #[error("Operation with invalid document id")] + InvalidDocumentId, +} + +fn validate_and_unpack( + operation: p2panda_core::Operation, + document_id: TopicId, +) -> Result { + let p2panda_core::Operation:: { header, body, .. } = operation; + + let Some(operation_document_id): Option = header.extension() else { + return Err(UnpackError::InvalidDocumentId); + }; + + if operation_document_id != document_id { + return Err(UnpackError::InvalidDocumentId); + } + + Ok((header.clone(), body, header.to_bytes())) } From b0b9916876ec13cff69c68ecc52d919f1c2c72d8 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 9 Dec 2025 15:53:31 +0100 Subject: [PATCH 06/11] node: Add iroh dependency Ideally p2panda would reexport it but we will need it to turn on online collaboration. --- Cargo.lock | 1 + reflection-node/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f73c7b6..55aa002 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4347,6 +4347,7 @@ dependencies = [ "chrono", "ciborium", "hex", + "iroh", "p2panda-core", "p2panda-discovery-next", "p2panda-net-next", diff --git a/reflection-node/Cargo.toml b/reflection-node/Cargo.toml index 2bc978a..d6b00ec 100644 --- a/reflection-node/Cargo.toml +++ b/reflection-node/Cargo.toml @@ -27,3 +27,4 @@ tracing = "0.1" test-log = { version = "0.2.18", default-features = false, features = ["trace", "color"] } hex = "0.4.3" rand_chacha = { version = "0.9.0", features = ["os_rng"] } +iroh = "0.95.1" From 1a246815e4076d0903cb2cd323b7ca63b9e3fd90 Mon Sep 17 00:00:00 2001 From: adz Date: Wed, 26 Nov 2025 12:41:17 +0100 Subject: [PATCH 07/11] node: Register bootstrap node and relay url This enables online collaboration. --- reflection-node/src/node_inner.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index e328490..4022e76 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -1,5 +1,5 @@ use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use crate::document::{DocumentError, SubscribableDocument}; use crate::document_store::{DocumentStore, LogId}; @@ -11,15 +11,16 @@ use crate::subscription_inner::SubscriptionInner; use crate::utils::CombinedMigrationSource; use p2panda_core::{Hash, PrivateKey}; +use p2panda_discovery::address_book::AddressBookStore; use p2panda_discovery::address_book::memory::MemoryStore as MemoryAddressBook; -use p2panda_net::{MdnsDiscoveryMode, TopicId}; +use p2panda_net::{MdnsDiscoveryMode, NodeInfo, TopicId}; use p2panda_net::{Network, NetworkBuilder}; use p2panda_store::sqlite::store::migrations as operation_store_migrations; use p2panda_sync::managers::topic_sync_manager::TopicSyncManagerConfig; use rand_chacha::rand_core::SeedableRng; use sqlx::{migrate::Migrator, sqlite}; use tokio::sync::{Notify, RwLock}; -use tracing::{info, warn}; +use tracing::{error, info, warn}; pub type TopicSyncManager = p2panda_sync::managers::topic_sync_manager::TopicSyncManager< TopicId, @@ -45,6 +46,22 @@ pub struct NodeInner { pub(crate) network_notifier: Notify, } +static RELAY_URL: LazyLock = LazyLock::new(|| { + "https://euc1-1.relay.n0.iroh-canary.iroh.link" + .parse() + .expect("valid relay URL") +}); + +static BOOTSTRAP_NODE: LazyLock = LazyLock::new(|| { + let endpoint_addr = iroh::EndpointAddr::new( + "7ccdbeed587a8ec8c71cdc9b98e941ac597e11b0216aac1387ef81089a4930b2" + .parse() + .expect("valid bootstrap node id"), + ) + .with_relay_url(RELAY_URL.clone()); + NodeInfo::from(endpoint_addr).bootstrap() +}); + impl NodeInner { pub async fn new( network_id: Hash, @@ -173,6 +190,11 @@ async fn setup_network( operation_store: &OperationStore, ) -> Option> { let address_book = MemoryAddressBook::new(rand_chacha::ChaCha20Rng::from_os_rng()); + + if let Err(error) = address_book.insert_node_info(BOOTSTRAP_NODE.clone()).await { + error!("Failed to add bootstrap node to the address book: {error}"); + } + let sync_conf = TopicSyncManagerConfig { store: operation_store.clone_inner(), topic_map: document_store.clone(), @@ -180,6 +202,7 @@ async fn setup_network( let network = NetworkBuilder::new(network_id.into()) .private_key(private_key.clone()) .mdns(MdnsDiscoveryMode::Active) + .relay(RELAY_URL.clone()) .build(address_book, sync_conf) .await; From be2f3c743bc3d7d32adb8fc90733d117e53f5671 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 10 Dec 2025 12:35:05 +0100 Subject: [PATCH 08/11] node: Drop migrations We can reset the migrations since we are changing the CNN, so old databases will be inaccessible for us any way. --- .../migrations/20250418140035_create_tables.sql | 2 +- .../migrations/20251130191542_cascate_deletion.sql | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 reflection-node/migrations/20251130191542_cascate_deletion.sql diff --git a/reflection-node/migrations/20250418140035_create_tables.sql b/reflection-node/migrations/20250418140035_create_tables.sql index 39ad104..2d3cdb0 100644 --- a/reflection-node/migrations/20250418140035_create_tables.sql +++ b/reflection-node/migrations/20250418140035_create_tables.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS authors ( document_id TEXT NOT NULL, last_seen INTEGER, UNIQUE(public_key, document_id), - FOREIGN KEY(document_id) REFERENCES documents(document_id) + FOREIGN KEY(document_id) REFERENCES documents(document_id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS documents ( diff --git a/reflection-node/migrations/20251130191542_cascate_deletion.sql b/reflection-node/migrations/20251130191542_cascate_deletion.sql deleted file mode 100644 index 04ca923..0000000 --- a/reflection-node/migrations/20251130191542_cascate_deletion.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE IF NOT EXISTS new_authors ( - public_key TEXT NOT NULL, - document_id TEXT NOT NULL, - last_seen INTEGER, - UNIQUE(public_key, document_id), - FOREIGN KEY(document_id) REFERENCES documents(document_id) ON DELETE CASCADE -); - -INSERT INTO new_authors SELECT * FROM authors; - -DROP TABLE authors; - -ALTER TABLE new_authors RENAME TO authors; \ No newline at end of file From 96d459b81b7bff58344d4eebcc5ed9e5f52fad3f Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 10 Dec 2025 13:16:56 +0100 Subject: [PATCH 09/11] node: Rename document_id to id --- .../20250418140035_create_tables.sql | 8 +-- reflection-node/src/document.rs | 4 +- reflection-node/src/document_store.rs | 70 +++++++++---------- reflection-node/src/lib.rs | 16 ++--- reflection-node/src/node.rs | 17 ++--- reflection-node/src/node_inner.rs | 17 ++--- reflection-node/src/operation.rs | 4 +- reflection-node/src/persistent_operation.rs | 6 +- reflection-node/src/subscription_inner.rs | 30 ++++---- 9 files changed, 80 insertions(+), 92 deletions(-) diff --git a/reflection-node/migrations/20250418140035_create_tables.sql b/reflection-node/migrations/20250418140035_create_tables.sql index 2d3cdb0..47b74fa 100644 --- a/reflection-node/migrations/20250418140035_create_tables.sql +++ b/reflection-node/migrations/20250418140035_create_tables.sql @@ -1,13 +1,13 @@ CREATE TABLE IF NOT EXISTS authors ( public_key TEXT NOT NULL, - document_id TEXT NOT NULL, + topic_id TEXT NOT NULL, last_seen INTEGER, - UNIQUE(public_key, document_id), - FOREIGN KEY(document_id) REFERENCES documents(document_id) ON DELETE CASCADE + UNIQUE(public_key, topic_id), + FOREIGN KEY(topic_id) REFERENCES documents(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS documents ( - document_id TEXT NOT NULL PRIMARY KEY, + id TEXT NOT NULL PRIMARY KEY, name TEXT, last_accessed INTEGER ); \ No newline at end of file diff --git a/reflection-node/src/document.rs b/reflection-node/src/document.rs index 44f9c15..0da43b0 100644 --- a/reflection-node/src/document.rs +++ b/reflection-node/src/document.rs @@ -92,7 +92,7 @@ impl Subscription { } pub async fn unsubscribe(self) -> Result<(), DocumentError> { - let document_id = self.inner.id; + let id = self.inner.id; self.network_monitor_task.abort(); let inner = self.inner.clone(); @@ -100,7 +100,7 @@ impl Subscription { .spawn(async move { inner.unsubscribe().await }) .await??; - info!("Unsubscribed from document {}", hex::encode(document_id)); + info!("Unsubscribed from document {}", hex::encode(id)); Ok(()) } diff --git a/reflection-node/src/document_store.rs b/reflection-node/src/document_store.rs index 232b5e9..286b38b 100644 --- a/reflection-node/src/document_store.rs +++ b/reflection-node/src/document_store.rs @@ -15,7 +15,7 @@ use crate::operation_store::OperationStore; #[derive(Debug, FromRow)] pub struct StoreDocument { - #[sqlx(try_from = "Vec", rename = "document_id")] + #[sqlx(try_from = "Vec")] pub id: TopicId, #[sqlx(default)] pub name: Option, @@ -40,9 +40,9 @@ impl DocumentStore { Self { pool } } - async fn authors(&self, document_id: &TopicId) -> sqlx::Result> { - let list = sqlx::query("SELECT public_key FROM authors WHERE document_id = ?") - .bind(document_id.as_slice()) + async fn authors(&self, id: &TopicId) -> sqlx::Result> { + let list = sqlx::query("SELECT public_key FROM authors WHERE topic_id = ?") + .bind(id.as_slice()) .fetch_all(&self.pool) .await?; @@ -54,15 +54,15 @@ impl DocumentStore { pub async fn documents(&self) -> sqlx::Result> { let mut documents: Vec = - sqlx::query_as("SELECT document_id, name, last_accessed FROM documents") + sqlx::query_as("SELECT id, name, last_accessed FROM documents") .fetch_all(&self.pool) .await?; - let authors = sqlx::query("SELECT public_key, document_id, last_seen FROM authors") + let authors = sqlx::query("SELECT public_key, topic_id, last_seen FROM authors") .fetch_all(&self.pool) .await?; let mut authors_per_document = authors.iter().fold(HashMap::new(), |mut acc, row| { - let Ok(document_id) = TopicId::try_from(row.get::<&[u8], _>("document_id")) else { + let Ok(id) = TopicId::try_from(row.get::<&[u8], _>("topic_id")) else { return acc; }; let Ok(public_key) = PublicKey::try_from(row.get::<&[u8], _>("public_key")) else { @@ -71,12 +71,10 @@ impl DocumentStore { let Ok(last_seen) = row.try_get::>, _>("last_seen") else { return acc; }; - acc.entry(document_id) - .or_insert_with(Vec::new) - .push(Author { - public_key, - last_seen, - }); + acc.entry(id).or_insert_with(Vec::new).push(Author { + public_key, + last_seen, + }); acc }); @@ -89,44 +87,40 @@ impl DocumentStore { Ok(documents) } - pub async fn add_document(&self, document_id: &TopicId) -> sqlx::Result<()> { - // The document_id is the primary key in the table therefore ignore insertion when the document exists already + pub async fn add_document(&self, id: &TopicId) -> sqlx::Result<()> { + // The id is the primary key in the table therefore ignore insertion when the document exists already sqlx::query( " - INSERT OR IGNORE INTO documents ( document_id ) + INSERT OR IGNORE INTO documents ( id ) VALUES ( ? ) ", ) - .bind(document_id.as_slice()) + .bind(id.as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn delete_document(&self, document_id: &TopicId) -> sqlx::Result<()> { - sqlx::query("DELETE FROM documents WHERE document_id = ?") - .bind(document_id.as_slice()) + pub async fn delete_document(&self, id: &TopicId) -> sqlx::Result<()> { + sqlx::query("DELETE FROM documents WHERE id = ?") + .bind(id.as_slice()) .execute(&self.pool) .await?; Ok(()) } - pub async fn add_author( - &self, - document_id: &TopicId, - public_key: &PublicKey, - ) -> sqlx::Result<()> { - // The author/document_id pair is required to be unique therefore ignore if the insertion fails + pub async fn add_author(&self, id: &TopicId, public_key: &PublicKey) -> sqlx::Result<()> { + // The author/id pair is required to be unique therefore ignore if the insertion fails sqlx::query( " - INSERT OR IGNORE INTO authors ( public_key, document_id ) + INSERT OR IGNORE INTO authors ( public_key, topic_id ) VALUES ( ?, ? ) ", ) .bind(public_key.as_bytes().as_slice()) - .bind(document_id.as_slice()) + .bind(id.as_slice()) .execute(&self.pool) .await?; @@ -155,18 +149,18 @@ impl DocumentStore { pub async fn set_name_for_document( &self, - document_id: &TopicId, + id: &TopicId, name: Option, ) -> sqlx::Result<()> { sqlx::query( " UPDATE documents SET name = ? - WHERE document_id = ? + WHERE id = ? ", ) .bind(name) - .bind(document_id.as_slice()) + .bind(id.as_slice()) .execute(&self.pool) .await?; @@ -175,18 +169,18 @@ impl DocumentStore { pub async fn set_last_accessed_for_document( &self, - document_id: &TopicId, + id: &TopicId, last_accessed: Option>, ) -> sqlx::Result<()> { sqlx::query( " UPDATE documents SET last_accessed = ? - WHERE document_id = ? + WHERE id = ? ", ) .bind(last_accessed) - .bind(document_id.as_slice()) + .bind(id.as_slice()) .execute(&self.pool) .await?; @@ -196,14 +190,14 @@ impl DocumentStore { pub async fn operations_for_document( &self, operation_store: &OperationStore, - document_id: &TopicId, + id: &TopicId, ) -> sqlx::Result>> { let operation_store = operation_store.inner(); - let authors = self.authors(document_id).await?; + let authors = self.authors(id).await?; let log_ids = [ - LogId::new(LogType::Delta, document_id), - LogId::new(LogType::Snapshot, document_id), + LogId::new(LogType::Delta, id), + LogId::new(LogType::Snapshot, id), ]; let mut result = Vec::new(); diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index 5553d31..e7af654 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -31,12 +31,12 @@ mod tests { .await .unwrap(); - let document_id: [u8; 32] = [0; 32]; - let _sub = node.subscribe(document_id, TestDocument::new()).await; + let id: [u8; 32] = [0; 32]; + let _sub = node.subscribe(id, TestDocument::new()).await; let documents = node.documents::<[u8; 32]>().await.unwrap(); assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, document_id); + assert_eq!(documents.first().unwrap().id, id); node.shutdown().await.unwrap(); } @@ -82,12 +82,12 @@ mod tests { let test_document = TestDocument::new(); - let document_id: [u8; 32] = [0; 32]; - let subscription = node.subscribe(document_id, test_document).await.unwrap(); + let id: [u8; 32] = [0; 32]; + let subscription = node.subscribe(id, test_document).await.unwrap(); let documents = node.documents::<[u8; 32]>().await.unwrap(); assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, document_id); + assert_eq!(documents.first().unwrap().id, id); let private_key2 = PrivateKey::new(); let network_id2 = Hash::new(b"reflection"); @@ -98,13 +98,13 @@ mod tests { let test_document2 = TestDocument::new(); let _subscription2 = node2 - .subscribe(document_id, test_document2.clone()) + .subscribe(id, test_document2.clone()) .await .unwrap(); let documents2 = node2.documents::<[u8; 32]>().await.unwrap(); assert_eq!(documents2.len(), 1); - assert_eq!(documents2.first().unwrap().id, document_id); + assert_eq!(documents2.first().unwrap().id, id); let test_snapshot = "test".as_bytes().to_vec(); subscription diff --git a/reflection-node/src/node.rs b/reflection-node/src/node.rs index 891675c..c17e2e3 100644 --- a/reflection-node/src/node.rs +++ b/reflection-node/src/node.rs @@ -148,31 +148,28 @@ impl Node { pub async fn subscribe, T: SubscribableDocument + 'static>( &self, - document_id: ID, + id: ID, document_handle: T, ) -> Result, DocumentError> { - let document_id: TopicId = document_id.into(); + let id: TopicId = id.into(); let document_handle = Arc::new(document_handle); let inner_clone = self.inner.clone(); let inner_subscription = self .runtime - .spawn(async move { inner_clone.subscribe(document_id, document_handle).await }) + .spawn(async move { inner_clone.subscribe(id, document_handle).await }) .await??; let subscription = Subscription::new(self.runtime.clone(), inner_subscription).await; - info!("Subscribed to topic {}", hex::encode(document_id)); + info!("Subscribed to topic {}", hex::encode(id)); Ok(subscription) } - pub async fn delete_document>( - &self, - document_id: ID, - ) -> Result<(), DocumentError> { - let document_id: TopicId = document_id.into(); + pub async fn delete_document>(&self, id: ID) -> Result<(), DocumentError> { + let id: TopicId = id.into(); let inner_clone = self.inner.clone(); self.runtime - .spawn(async move { inner_clone.delete_document(document_id).await }) + .spawn(async move { inner_clone.delete_document(id).await }) .await? } } diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index 4022e76..d8d295e 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -150,17 +150,17 @@ impl NodeInner { pub async fn subscribe( self: Arc, - document_id: TopicId, + id: TopicId, document: Arc, ) -> Result, DocumentError> { - self.document_store.add_document(&document_id).await?; + self.document_store.add_document(&id).await?; // Add ourselves as an author to the document store. self.document_store - .add_author(&document_id, &self.private_key.public_key()) + .add_author(&id, &self.private_key.public_key()) .await?; let stored_operations = self .document_store - .operations_for_document(&self.operation_store, &document_id) + .operations_for_document(&self.operation_store, &id) .await?; for operation in stored_operations { @@ -171,14 +171,11 @@ impl NodeInner { } } - Ok(SubscriptionInner::new(self.clone(), document_id, document)) + Ok(SubscriptionInner::new(self.clone(), id, document)) } - pub async fn delete_document( - self: Arc, - document_id: TopicId, - ) -> Result<(), DocumentError> { - self.document_store.delete_document(&document_id).await?; + pub async fn delete_document(self: Arc, id: TopicId) -> Result<(), DocumentError> { + self.document_store.delete_document(&id).await?; Ok(()) } } diff --git a/reflection-node/src/operation.rs b/reflection-node/src/operation.rs index d3bfba0..c6c2bb8 100644 --- a/reflection-node/src/operation.rs +++ b/reflection-node/src/operation.rs @@ -65,8 +65,8 @@ impl Extension for ReflectionExtensions { impl Extension for ReflectionExtensions { fn extract(header: &Header) -> Option { let log_type: LogType = header.extension()?; - let document_id: TopicId = header.extension()?; + let id: TopicId = header.extension()?; - Some(LogId::new(log_type, &document_id)) + Some(LogId::new(log_type, &id)) } } diff --git a/reflection-node/src/persistent_operation.rs b/reflection-node/src/persistent_operation.rs index 251e07d..e09f067 100644 --- a/reflection-node/src/persistent_operation.rs +++ b/reflection-node/src/persistent_operation.rs @@ -42,7 +42,7 @@ impl PersistentOperation { /// Validates and unpacks the operation pub fn validate_and_unpack( self, - document_id: DocumentId, + id: DocumentId, ) -> Result { let PersistentOperation { header, body } = self; @@ -50,12 +50,12 @@ impl PersistentOperation { let header_deserialized: Header = decode_cbor(&header[..])?; let body_deserialized = body.map(|body| Body::from(body.into_vec())); - let Some(operation_document_id): Option = header_deserialized.extension() + let Some(operation_id): Option = header_deserialized.extension() else { return Err(UnpackError::InvalidDocumentId); }; - if operation_document_id != document_id { + if operation_id != id { return Err(UnpackError::InvalidDocumentId); } diff --git a/reflection-node/src/subscription_inner.rs b/reflection-node/src/subscription_inner.rs index 3c507fe..4a5064e 100644 --- a/reflection-node/src/subscription_inner.rs +++ b/reflection-node/src/subscription_inner.rs @@ -250,7 +250,7 @@ impl SubscriptionInner { async fn setup_network( node: &Arc, network: &Network, - document_id: TopicId, + id: TopicId, document: &Arc, author_tracker: &Arc>, ) -> ( @@ -260,12 +260,12 @@ async fn setup_network( ) { let mut abort_handles = Vec::with_capacity(3); - let stream = match network.stream(document_id, true).await { + let stream = match network.stream(id, true).await { Ok(result) => result, Err(error) => { warn!( "Failed to setup network for subscription to document {}: {error}", - hex::encode(document_id) + hex::encode(id) ); return (None, None, abort_handles); } @@ -281,7 +281,7 @@ async fn setup_network( while let Ok(event) = document_rx.recv().await { match event.event() { TopicLogSyncEvent::Operation(operation) => { - match validate_and_unpack(operation.as_ref().to_owned(), document_id) { + match validate_and_unpack(operation.as_ref().to_owned(), id) { Ok(data) => { persistent_tx.send(data).await.unwrap(); } @@ -304,7 +304,7 @@ async fn setup_network( // // @TODO(adz): We want to throw an error if users try to subscribe with the same topic across // different streams. - let topic = Hash::new(document_id); + let topic = Hash::new(id); let ephemeral_stream = network.ephemeral_stream(topic.into()).await.unwrap(); let mut ephemeral_rx = ephemeral_stream.subscribe().await.unwrap(); let ephemeral_tx = ephemeral_stream; @@ -377,7 +377,7 @@ async fn setup_network( // When we discover a new author we need to add them to our document store. if let Err(error) = node .document_store - .add_author(&document_id, &operation.header.public_key) + .add_author(&id, &operation.header.public_key) .await { error!("Can't store author to database: {error}"); @@ -402,17 +402,17 @@ async fn setup_network( info!( "Network subscription set up for document {}", - hex::encode(document_id) + hex::encode(id) ); - let topic = Hash::new(document_id); + let topic = Hash::new(id); let ephemeral_tx = network.ephemeral_stream(topic.into()).await.unwrap(); (Some(document_tx), Some(ephemeral_tx), abort_handles) } async fn teardown_network( - document_id: &TopicId, + id: &TopicId, author_tracker: &Arc>, tx: Option>, ephemeral_tx: Option, @@ -429,7 +429,7 @@ async fn teardown_network( { error!( "Failed to tear down ephemeral channel for document {}: {error}", - hex::encode(document_id) + hex::encode(id) ); } @@ -437,12 +437,12 @@ async fn teardown_network( if let Err(error) = tx.close() { error!( "Failed to tear down persistent channel for document {}: {error}", - hex::encode(document_id) + hex::encode(id) ); } info!( "Network subscription torn down for document {}", - hex::encode(document_id) + hex::encode(id) ); } } @@ -459,15 +459,15 @@ pub enum UnpackError { fn validate_and_unpack( operation: p2panda_core::Operation, - document_id: TopicId, + id: TopicId, ) -> Result { let p2panda_core::Operation:: { header, body, .. } = operation; - let Some(operation_document_id): Option = header.extension() else { + let Some(operation_id): Option = header.extension() else { return Err(UnpackError::InvalidDocumentId); }; - if operation_document_id != document_id { + if operation_id != id { return Err(UnpackError::InvalidDocumentId); } From 074a81368092166b8152c9903ca6b0e99b111cba Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 10 Dec 2025 13:35:46 +0100 Subject: [PATCH 10/11] node: Rename document to topic Use the same naming convetion as p2panda. And also also reduce the confusion with reflection-doc. --- reflection-doc/src/document.rs | 10 +- reflection-doc/src/documents.rs | 2 +- reflection-doc/src/service.rs | 4 +- .../20250418140035_create_tables.sql | 4 +- reflection-node/src/author_tracker.rs | 30 ++--- reflection-node/src/lib.rs | 53 ++++----- reflection-node/src/node.rs | 40 +++---- reflection-node/src/node_inner.rs | 46 ++++---- reflection-node/src/operation.rs | 14 +-- reflection-node/src/operation_store.rs | 12 +- reflection-node/src/persistent_operation.rs | 14 +-- reflection-node/src/subscription_inner.rs | 111 +++++++++--------- reflection-node/src/{document.rs => topic.rs} | 26 ++-- .../src/{document_store.rs => topic_store.rs} | 52 ++++---- 14 files changed, 203 insertions(+), 215 deletions(-) rename reflection-node/src/{document.rs => topic.rs} (84%) rename reflection-node/src/{document_store.rs => topic_store.rs} (83%) diff --git a/reflection-doc/src/document.rs b/reflection-doc/src/document.rs index 479b8ff..bb5fa1b 100644 --- a/reflection-doc/src/document.rs +++ b/reflection-doc/src/document.rs @@ -9,8 +9,8 @@ use glib::{Properties, clone}; pub use hex::FromHexError; use loro::{ExportMode, LoroDoc, LoroText, event::Diff}; use p2panda_core::cbor::{decode_cbor, encode_cbor}; -use reflection_node::document::{SubscribableDocument, Subscription as DocumentSubscription}; use reflection_node::p2panda_core; +use reflection_node::topic::{SubscribableTopic, Subscription as TopicSubscription}; use tracing::error; use crate::author::Author; @@ -127,7 +127,7 @@ mod imp { #[property(get, construct_only)] id: OnceCell, #[property(name = "subscribed", get = Self::subscribed, type = bool)] - pub(super) subscription: RwLock>>>, + pub(super) subscription: RwLock>>>, #[property(get = Self::service, set = Self::set_service, construct_only, type = Service)] service: glib::WeakRef, #[property(get)] @@ -639,7 +639,7 @@ mod imp { } } - pub(super) fn subscription(&self) -> Option>> { + pub(super) fn subscription(&self) -> Option>> { self.subscription.read().unwrap().clone() } } @@ -879,7 +879,7 @@ impl Document { } pub async fn delete(&self) { - if let Err(error) = self.service().node().delete_document(self.id()).await { + if let Err(error) = self.service().node().delete_topic(self.id()).await { error!("Failed to delete document from document store: {}", error); return; } @@ -893,7 +893,7 @@ unsafe impl Sync for Document {} struct DocumentHandle(glib::WeakRef); -impl SubscribableDocument for DocumentHandle { +impl SubscribableTopic for DocumentHandle { fn bytes_received(&self, author: p2panda_core::PublicKey, data: Vec) { if let Some(document) = self.0.upgrade() { document.main_context().invoke(move || { diff --git a/reflection-doc/src/documents.rs b/reflection-doc/src/documents.rs index ab9ba18..d9dc609 100644 --- a/reflection-doc/src/documents.rs +++ b/reflection-doc/src/documents.rs @@ -70,7 +70,7 @@ impl Documents { pub(crate) async fn load(&self, service: &Service) -> Result<(), StartupError> { let public_key = service.private_key().public_key(); - let documents = service.node().documents::().await?; + let documents = service.node().topics::().await?; let mut list = self.imp().list.write().unwrap(); assert!(list.is_empty()); diff --git a/reflection-doc/src/service.rs b/reflection-doc/src/service.rs index 820d279..67bba88 100644 --- a/reflection-doc/src/service.rs +++ b/reflection-doc/src/service.rs @@ -13,9 +13,9 @@ use crate::{ documents::Documents, }; use reflection_node::{ - document::DocumentError, node, node::{Node, NodeError}, + topic::TopicError, }; #[derive(Error, Debug)] @@ -23,7 +23,7 @@ pub enum StartupError { #[error(transparent)] Node(#[from] NodeError), #[error(transparent)] - Document(#[from] DocumentError), + Topic(#[from] TopicError), } #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum, Default)] diff --git a/reflection-node/migrations/20250418140035_create_tables.sql b/reflection-node/migrations/20250418140035_create_tables.sql index 47b74fa..186eb4c 100644 --- a/reflection-node/migrations/20250418140035_create_tables.sql +++ b/reflection-node/migrations/20250418140035_create_tables.sql @@ -3,10 +3,10 @@ CREATE TABLE IF NOT EXISTS authors ( topic_id TEXT NOT NULL, last_seen INTEGER, UNIQUE(public_key, topic_id), - FOREIGN KEY(topic_id) REFERENCES documents(id) ON DELETE CASCADE + FOREIGN KEY(topic_id) REFERENCES topics(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS documents ( +CREATE TABLE IF NOT EXISTS topics ( id TEXT NOT NULL PRIMARY KEY, name TEXT, last_accessed INTEGER diff --git a/reflection-node/src/author_tracker.rs b/reflection-node/src/author_tracker.rs index 7320ae2..a7d5791 100644 --- a/reflection-node/src/author_tracker.rs +++ b/reflection-node/src/author_tracker.rs @@ -3,10 +3,10 @@ use std::ops::DerefMut; use std::sync::Arc; use std::time::{Duration, Instant, SystemTime}; -use crate::document::SubscribableDocument; use crate::ephemerial_operation::EphemerialOperation; use crate::node_inner::MessageType; use crate::node_inner::NodeInner; +use crate::topic::SubscribableTopic; use chrono::Utc; use p2panda_core::cbor::{DecodeError, decode_cbor, encode_cbor}; use p2panda_core::{PrivateKey, PublicKey}; @@ -45,22 +45,22 @@ impl TryFrom<&[u8]> for AuthorMessage { pub struct AuthorTracker { last_ping: Mutex>, - document: Arc, + subscribable_topic: Arc, node: Arc, tx: RwLock>, } -impl AuthorTracker { - pub fn new(node: Arc, document: Arc) -> Arc { +impl AuthorTracker { + pub fn new(node: Arc, subscribable_topic: Arc) -> Arc { Arc::new(Self { last_ping: Mutex::new(HashMap::new()), - document, + subscribable_topic, node, tx: RwLock::new(None), }) } - pub async fn set_document_tx(&self, tx: Option) { + pub async fn set_topic_tx(&self, tx: Option) { let mut tx_guard = self.tx.write().await; // Send good bye message to the network if let Some(tx) = tx_guard.as_ref() { @@ -70,15 +70,15 @@ impl AuthorTracker { // Set all authors that the tracker has seen to offline, authors the tracker hasn't seen are already offline let old_authors = std::mem::take(self.last_ping.lock().await.deref_mut()); for author in old_authors.into_keys() { - self.document.author_left(author); + self.subscribable_topic.author_left(author); self.set_last_seen(author).await; } let this_author = self.node.private_key.public_key(); if tx.is_some() { - self.document.author_joined(this_author); + self.subscribable_topic.author_joined(this_author); } else { - self.document.author_left(this_author); + self.subscribable_topic.author_left(this_author); self.set_last_seen(this_author).await; } @@ -107,7 +107,7 @@ impl AuthorTracker { async fn join(&self, author: PublicKey) { self.last_ping.lock().await.insert(author, Instant::now()); - self.document.author_joined(author); + self.subscribable_topic.author_joined(author); self.set_last_seen(author).await; // Send a ping to the network to ensure that the new author knows we exist @@ -120,19 +120,19 @@ impl AuthorTracker { // If this is a new author emit author join if old.is_none() { - self.document.author_joined(author); + self.subscribable_topic.author_joined(author); } self.set_last_seen(author).await; } async fn left(&self, author: PublicKey) { self.last_ping.lock().await.remove(&author); - self.document.author_left(author); + self.subscribable_topic.author_left(author); self.set_last_seen(author).await; } pub async fn spawn(&self) { - // Send a hello to the network so other authors know we joined the document + // Send a hello to the network so other authors know we joined the topic self.send(AuthorMessage::Hello).await; let mut interval = tokio::time::interval(OFFLINE_TIMEOUT / 2); @@ -156,7 +156,7 @@ impl AuthorTracker { }); for author in expired { - self.document.author_left(author); + self.subscribable_topic.author_left(author); self.set_last_seen(author).await; } } @@ -165,7 +165,7 @@ impl AuthorTracker { async fn set_last_seen(&self, author: PublicKey) { if let Err(error) = self .node - .document_store + .topic_store .set_last_seen_for_author(author, Some(Utc::now())) .await { diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index e7af654..bcd59d8 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -1,20 +1,20 @@ mod author_tracker; -pub mod document; -mod document_store; mod ephemerial_operation; pub mod node; mod node_inner; mod operation; mod operation_store; mod subscription_inner; +pub mod topic; +mod topic_store; mod utils; -pub use document::SubscribableDocument; pub use p2panda_core; +pub use topic::SubscribableTopic; #[cfg(test)] mod tests { - use crate::SubscribableDocument; + use crate::SubscribableTopic; use crate::node::{ConnectionMode, Node}; use p2panda_core::Hash; use p2panda_core::PrivateKey; @@ -24,7 +24,7 @@ mod tests { #[tokio::test] #[test_log::test] - async fn create_document() { + async fn create_topic() { let private_key = PrivateKey::new(); let network_id = Hash::new(b"reflection"); let node = Node::new(private_key, network_id, None, ConnectionMode::Network) @@ -32,25 +32,25 @@ mod tests { .unwrap(); let id: [u8; 32] = [0; 32]; - let _sub = node.subscribe(id, TestDocument::new()).await; - let documents = node.documents::<[u8; 32]>().await.unwrap(); + let _sub = node.subscribe(id, TestTopic::new()).await; + let topics = node.topics::<[u8; 32]>().await.unwrap(); - assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, id); + assert_eq!(topics.len(), 1); + assert_eq!(topics.first().unwrap().id, id); node.shutdown().await.unwrap(); } #[derive(Clone)] - struct TestDocument { + struct TestTopic { tx: mpsc::UnboundedSender>, rx: Arc>>>, } - impl TestDocument { + impl TestTopic { fn new() -> Self { let (tx, rx) = mpsc::unbounded_channel::>(); - TestDocument { + TestTopic { tx, rx: Arc::new(Mutex::new(rx)), } @@ -61,7 +61,7 @@ mod tests { } } - impl SubscribableDocument for TestDocument { + impl SubscribableTopic for TestTopic { fn bytes_received(&self, _author: PublicKey, data: Vec) { self.tx.send(data).unwrap(); } @@ -73,21 +73,21 @@ mod tests { #[tokio::test] #[test_log::test] - async fn subscribe_document() { + async fn subscribe_topic() { let private_key = PrivateKey::new(); let network_id = Hash::new(b"reflection"); let node = Node::new(private_key, network_id, None, ConnectionMode::Network) .await .unwrap(); - let test_document = TestDocument::new(); + let test_topic = TestTopic::new(); let id: [u8; 32] = [0; 32]; - let subscription = node.subscribe(id, test_document).await.unwrap(); + let subscription = node.subscribe(id, test_topic).await.unwrap(); - let documents = node.documents::<[u8; 32]>().await.unwrap(); - assert_eq!(documents.len(), 1); - assert_eq!(documents.first().unwrap().id, id); + let topics = node.topics::<[u8; 32]>().await.unwrap(); + assert_eq!(topics.len(), 1); + assert_eq!(topics.first().unwrap().id, id); let private_key2 = PrivateKey::new(); let network_id2 = Hash::new(b"reflection"); @@ -95,16 +95,13 @@ mod tests { .await .unwrap(); - let test_document2 = TestDocument::new(); + let test_topic2 = TestTopic::new(); - let _subscription2 = node2 - .subscribe(id, test_document2.clone()) - .await - .unwrap(); + let _subscription2 = node2.subscribe(id, test_topic2.clone()).await.unwrap(); - let documents2 = node2.documents::<[u8; 32]>().await.unwrap(); - assert_eq!(documents2.len(), 1); - assert_eq!(documents2.first().unwrap().id, id); + let topics2 = node2.topics::<[u8; 32]>().await.unwrap(); + assert_eq!(topics2.len(), 1); + assert_eq!(topics2.first().unwrap().id, id); let test_snapshot = "test".as_bytes().to_vec(); subscription @@ -112,7 +109,7 @@ mod tests { .await .unwrap(); - assert_eq!(test_document2.wait_for_bytes().await, test_snapshot); + assert_eq!(test_topic2.wait_for_bytes().await, test_snapshot); node.shutdown().await.unwrap(); node2.shutdown().await.unwrap(); diff --git a/reflection-node/src/node.rs b/reflection-node/src/node.rs index c17e2e3..e8a0330 100644 --- a/reflection-node/src/node.rs +++ b/reflection-node/src/node.rs @@ -7,10 +7,10 @@ use p2panda_net::TopicId; use thiserror::Error; use tracing::info; -use crate::document::{DocumentError, SubscribableDocument, Subscription}; -pub use crate::document_store::Author; -use crate::document_store::StoreDocument; use crate::node_inner::NodeInner; +use crate::topic::{SubscribableTopic, Subscription, TopicError}; +pub use crate::topic_store::Author; +use crate::topic_store::StoreTopic; #[derive(Debug, Error)] pub enum NodeError { @@ -33,7 +33,7 @@ pub enum ConnectionMode { } #[derive(Clone, Debug)] -pub struct Document { +pub struct Topic { pub id: ID, pub name: Option, pub last_accessed: Option>, @@ -118,23 +118,23 @@ impl Node { Ok(()) } - pub async fn documents>(&self) -> Result>, DocumentError> { + pub async fn topics>(&self) -> Result>, TopicError> { let inner_clone = self.inner.clone(); - let documents = self + let topics = self .runtime - .spawn(async move { inner_clone.document_store.documents().await }) + .spawn(async move { inner_clone.topic_store.topics().await }) .await??; - let documents = documents + let topics = topics .into_iter() - .map(|document| { - let StoreDocument { + .map(|topic| { + let StoreTopic { id, name, last_accessed, authors, - } = document; - Document { + } = topic; + Topic { id: id.into(), name, last_accessed, @@ -143,20 +143,20 @@ impl Node { }) .collect(); - Ok(documents) + Ok(topics) } - pub async fn subscribe, T: SubscribableDocument + 'static>( + pub async fn subscribe, T: SubscribableTopic + 'static>( &self, id: ID, - document_handle: T, - ) -> Result, DocumentError> { + topic_handle: T, + ) -> Result, TopicError> { let id: TopicId = id.into(); - let document_handle = Arc::new(document_handle); + let topic_handle = Arc::new(topic_handle); let inner_clone = self.inner.clone(); let inner_subscription = self .runtime - .spawn(async move { inner_clone.subscribe(id, document_handle).await }) + .spawn(async move { inner_clone.subscribe(id, topic_handle).await }) .await??; let subscription = Subscription::new(self.runtime.clone(), inner_subscription).await; @@ -165,11 +165,11 @@ impl Node { Ok(subscription) } - pub async fn delete_document>(&self, id: ID) -> Result<(), DocumentError> { + pub async fn delete_topic>(&self, id: ID) -> Result<(), TopicError> { let id: TopicId = id.into(); let inner_clone = self.inner.clone(); self.runtime - .spawn(async move { inner_clone.delete_document(id).await }) + .spawn(async move { inner_clone.delete_topic(id).await }) .await? } } diff --git a/reflection-node/src/node_inner.rs b/reflection-node/src/node_inner.rs index d8d295e..c91f7ac 100644 --- a/reflection-node/src/node_inner.rs +++ b/reflection-node/src/node_inner.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; use std::sync::{Arc, LazyLock}; -use crate::document::{DocumentError, SubscribableDocument}; -use crate::document_store::{DocumentStore, LogId}; use crate::ephemerial_operation::EphemerialOperation; use crate::node::{ConnectionMode, NodeError}; use crate::operation::ReflectionExtensions; use crate::operation_store::OperationStore; use crate::subscription_inner::SubscriptionInner; +use crate::topic::{SubscribableTopic, TopicError}; +use crate::topic_store::{LogId, TopicStore}; use crate::utils::CombinedMigrationSource; use p2panda_core::{Hash, PrivateKey}; @@ -25,7 +25,7 @@ use tracing::{error, info, warn}; pub type TopicSyncManager = p2panda_sync::managers::topic_sync_manager::TopicSyncManager< TopicId, p2panda_store::SqliteStore, - DocumentStore, + TopicStore, LogId, ReflectionExtensions, >; @@ -39,7 +39,7 @@ pub(crate) enum MessageType { #[derive(Debug)] pub struct NodeInner { pub(crate) operation_store: OperationStore, - pub(crate) document_store: DocumentStore, + pub(crate) topic_store: TopicStore, pub(crate) private_key: PrivateKey, pub(crate) network_id: Hash, pub(crate) network: RwLock>>, @@ -84,7 +84,7 @@ impl NodeInner { pool_options.connect_with(connection_options).await? }; - // Run migration for p2panda OperationStore and for the our DocumentStore + // Run migration for p2panda OperationStore and for the our TopicStore Migrator::new(CombinedMigrationSource::new(vec![ operation_store_migrations(), sqlx::migrate!(), @@ -94,7 +94,7 @@ impl NodeInner { .await?; let operation_store = OperationStore::new(pool.clone()); - let document_store = DocumentStore::new(pool); + let topic_store = TopicStore::new(pool); let network = match connection_mode { ConnectionMode::None => None, @@ -102,13 +102,13 @@ impl NodeInner { unimplemented!("Bluetooth is currently not implemented") } ConnectionMode::Network => { - setup_network(&private_key, &network_id, &document_store, &operation_store).await + setup_network(&private_key, &network_id, &topic_store, &operation_store).await } }; Ok(Self { operation_store, - document_store, + topic_store, private_key, network_id, network: RwLock::new(network), @@ -132,7 +132,7 @@ impl NodeInner { setup_network( &self.private_key, &self.network_id, - &self.document_store, + &self.topic_store, &self.operation_store, ) .await @@ -148,34 +148,34 @@ impl NodeInner { self.network.write().await.take(); } - pub async fn subscribe( + pub async fn subscribe( self: Arc, id: TopicId, - document: Arc, - ) -> Result, DocumentError> { - self.document_store.add_document(&id).await?; - // Add ourselves as an author to the document store. - self.document_store + subscribable_topic: Arc, + ) -> Result, TopicError> { + self.topic_store.add_topic(&id).await?; + // Add ourselves as an author to the topic store. + self.topic_store .add_author(&id, &self.private_key.public_key()) .await?; let stored_operations = self - .document_store - .operations_for_document(&self.operation_store, &id) + .topic_store + .operations_for_topic(&self.operation_store, &id) .await?; for operation in stored_operations { // Send all stored operation bytes to the app, // it doesn't matter if the app already knows some or all of them if let Some(body) = operation.body { - document.bytes_received(operation.header.public_key, body.to_bytes()); + subscribable_topic.bytes_received(operation.header.public_key, body.to_bytes()); } } - Ok(SubscriptionInner::new(self.clone(), id, document)) + Ok(SubscriptionInner::new(self.clone(), id, subscribable_topic)) } - pub async fn delete_document(self: Arc, id: TopicId) -> Result<(), DocumentError> { - self.document_store.delete_document(&id).await?; + pub async fn delete_topic(self: Arc, id: TopicId) -> Result<(), TopicError> { + self.topic_store.delete_topic(&id).await?; Ok(()) } } @@ -183,7 +183,7 @@ impl NodeInner { async fn setup_network( private_key: &PrivateKey, network_id: &Hash, - document_store: &DocumentStore, + topic_store: &TopicStore, operation_store: &OperationStore, ) -> Option> { let address_book = MemoryAddressBook::new(rand_chacha::ChaCha20Rng::from_os_rng()); @@ -194,7 +194,7 @@ async fn setup_network( let sync_conf = TopicSyncManagerConfig { store: operation_store.clone_inner(), - topic_map: document_store.clone(), + topic_map: topic_store.clone(), }; let network = NetworkBuilder::new(network_id.into()) .private_key(private_key.clone()) diff --git a/reflection-node/src/operation.rs b/reflection-node/src/operation.rs index c6c2bb8..beb5c99 100644 --- a/reflection-node/src/operation.rs +++ b/reflection-node/src/operation.rs @@ -4,7 +4,7 @@ use p2panda_core::{Extension, Header, PruneFlag}; use p2panda_net::TopicId; use serde::{Deserialize, Serialize}; -use crate::document_store::LogId; +use crate::topic_store::LogId; /// Custom extensions for p2panda header. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -13,7 +13,7 @@ pub struct ReflectionExtensions { /// /// This usually indicates that a "snapshot" has been inserted into the body of this operation, /// containing all required state to reconstruct the full version including all previous edits - /// of this document. + /// of this topic. /// /// In our case of a text-editor, this would be the encoded payload of a state-based CRDT. #[serde( @@ -26,15 +26,15 @@ pub struct ReflectionExtensions { /// Operations can be organised in separate logs. With a "log id" we can declare where this /// operation belongs to. /// - /// We organise two logs per author per document, one for "short lived" / ephemeral deltas - /// (small text changes) and one for persisted snapshots (full document history). These are two + /// We organise two logs per author per topic, one for "short lived" / ephemeral deltas + /// (small text changes) and one for persisted snapshots (full topic history). These are two /// distinct "log types". #[serde(rename = "t")] pub log_type: LogType, - /// Identifier of the document this operation relates to. + /// Identifier of the topic this operation relates to. #[serde(rename = "d")] - pub document: TopicId, + pub topic: TopicId, } #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)] @@ -58,7 +58,7 @@ impl Extension for ReflectionExtensions { impl Extension for ReflectionExtensions { fn extract(header: &Header) -> Option { - Some(header.extensions.document) + Some(header.extensions.topic) } } diff --git a/reflection-node/src/operation_store.rs b/reflection-node/src/operation_store.rs index 46b20a4..44b7219 100644 --- a/reflection-node/src/operation_store.rs +++ b/reflection-node/src/operation_store.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use std::time::{SystemTime, SystemTimeError}; -use crate::document_store::LogId; +use crate::topic_store::LogId; use p2panda_core::{Body, Header, Operation, PrivateKey, PruneFlag}; use p2panda_net::TopicId; use p2panda_store::{ @@ -46,13 +46,13 @@ impl OperationStore { /// Creates, signs and stores new operation in the author's append-only log. /// - /// If no document is specified we create a new operation in a new log. The resulting hash of the - /// header can be used to identify that new document. + /// If no topic is specified we create a new operation in a new log. The resulting hash of the + /// header can be used to identify that new topic. pub async fn create_operation( &self, private_key: &PrivateKey, log_type: LogType, - document: TopicId, + topic: TopicId, body: Option<&[u8]>, prune_flag: bool, ) -> Result, CreationError> { @@ -65,7 +65,7 @@ impl OperationStore { let body = body.map(Body::new); let public_key = private_key.public_key(); - let log_id = LogId::new(log_type, &document); + let log_id = LogId::new(log_type, &topic); let latest_operation = self.inner.latest_operation(&public_key, &log_id).await?; let (seq_num, backlink) = match latest_operation { @@ -80,7 +80,7 @@ impl OperationStore { let extensions = ReflectionExtensions { prune_flag: PruneFlag::new(prune_flag), log_type, - document, + topic, }; let mut header = Header { diff --git a/reflection-node/src/persistent_operation.rs b/reflection-node/src/persistent_operation.rs index e09f067..dd90a21 100644 --- a/reflection-node/src/persistent_operation.rs +++ b/reflection-node/src/persistent_operation.rs @@ -4,7 +4,7 @@ use p2panda_core::{ }; use thiserror::Error; -use crate::document::DocumentId; +use crate::topic::TopicId; use crate::operation::ReflectionExtensions; type OperationWithRawHeader = (Header, Option, Vec); @@ -13,8 +13,8 @@ type OperationWithRawHeader = (Header, Option, Vec Result { let PersistentOperation { header, body } = self; @@ -50,13 +50,13 @@ impl PersistentOperation { let header_deserialized: Header = decode_cbor(&header[..])?; let body_deserialized = body.map(|body| Body::from(body.into_vec())); - let Some(operation_id): Option = header_deserialized.extension() + let Some(operation_id): Option = header_deserialized.extension() else { - return Err(UnpackError::InvalidDocumentId); + return Err(UnpackError::InvalidTopicId); }; if operation_id != id { - return Err(UnpackError::InvalidDocumentId); + return Err(UnpackError::InvalidTopicId); } Ok((header_deserialized, body_deserialized, header)) diff --git a/reflection-node/src/subscription_inner.rs b/reflection-node/src/subscription_inner.rs index 4a5064e..a52eea5 100644 --- a/reflection-node/src/subscription_inner.rs +++ b/reflection-node/src/subscription_inner.rs @@ -22,18 +22,18 @@ use tokio_stream::{StreamExt, wrappers::ReceiverStream}; use tracing::{error, info, warn}; use crate::author_tracker::{AuthorMessage, AuthorTracker}; -use crate::document::{DocumentError, SubscribableDocument}; use crate::ephemerial_operation::EphemerialOperation; use crate::node_inner::MessageType; use crate::node_inner::{NodeInner, TopicSyncManager}; use crate::operation::{LogType, ReflectionExtensions}; +use crate::topic::{SubscribableTopic, TopicError}; pub struct SubscriptionInner { ephemeral_tx: RwLock>, tx: RwLock>>, pub(crate) node: Arc, pub(crate) id: TopicId, - pub(crate) document: Arc, + pub(crate) subscribable_topic: Arc, author_tracker: Arc>, abort_handles: RwLock>, } @@ -46,16 +46,16 @@ impl Drop for SubscriptionInner { } } -impl SubscriptionInner { - pub fn new(node: Arc, id: TopicId, document: Arc) -> Self { - let author_tracker = AuthorTracker::new(node.clone(), document.clone()); +impl SubscriptionInner { + pub fn new(node: Arc, id: TopicId, subscribable_topic: Arc) -> Self { + let author_tracker = AuthorTracker::new(node.clone(), subscribable_topic.clone()); SubscriptionInner { tx: RwLock::new(None), ephemeral_tx: RwLock::new(None), node, id, abort_handles: RwLock::new(Vec::new()), - document, + subscribable_topic, author_tracker, } } @@ -72,7 +72,7 @@ impl SubscriptionInner { &self.node, network, self.id, - &self.document, + &self.subscribable_topic, &self.author_tracker, ) .await @@ -116,7 +116,7 @@ impl SubscriptionInner { &self.node, network, self.id, - &self.document, + &self.subscribable_topic, &self.author_tracker, ) .await @@ -130,7 +130,7 @@ impl SubscriptionInner { } } - pub async fn unsubscribe(&self) -> Result<(), DocumentError> { + pub async fn unsubscribe(&self) -> Result<(), TopicError> { let mut tx_guard = self.tx.write().await; let mut ephemeral_tx_guard = self.ephemeral_tx.write().await; let mut abort_handles_guard = self.abort_handles.write().await; @@ -140,8 +140,8 @@ impl SubscriptionInner { let abort_handles = take(abort_handles_guard.deref_mut()); self.node - .document_store - .set_last_accessed_for_document(&self.id, Some(Utc::now())) + .topic_store + .set_last_accessed_for_topic(&self.id, Some(Utc::now())) .await?; teardown_network( @@ -156,7 +156,7 @@ impl SubscriptionInner { Ok(()) } - pub async fn send_delta(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_delta(&self, data: Vec) -> Result<(), TopicError> { let operation = // Append one operation to our "ephemeral" delta log. self.node.operation_store @@ -170,7 +170,7 @@ impl SubscriptionInner { .await?; info!( - "Delta operation sent for document with id {}", + "Delta operation sent for topic with id {}", hex::encode(self.id) ); @@ -181,12 +181,12 @@ impl SubscriptionInner { Ok(()) } - pub async fn send_snapshot(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_snapshot(&self, data: Vec) -> Result<(), TopicError> { // Append an operation to our "snapshot" log and set the prune flag to // true. This will remove previous snapshots. // // Snapshots are not broadcasted on the gossip overlay as they would be - // too large. Peers will sync them up when they join the document. + // too large. Peers will sync them up when they join the topic. self.node .operation_store .create_operation( @@ -211,10 +211,7 @@ impl SubscriptionInner { .create_operation(&self.node.private_key, LogType::Delta, self.id, None, true) .await?; - info!( - "Snapshot saved for document with id {}", - hex::encode(self.id) - ); + info!("Snapshot saved for topic with id {}", hex::encode(self.id)); if let Some(tx) = self.tx.read().await.as_ref() { tx.publish(operation).await?; @@ -223,7 +220,7 @@ impl SubscriptionInner { Ok(()) } - pub async fn send_ephemeral(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_ephemeral(&self, data: Vec) -> Result<(), TopicError> { if let Some(ephemeral_tx) = self.ephemeral_tx.read().await.as_ref() { let operation = EphemerialOperation::new(data, &self.node.private_key); let bytes = encode_cbor(&MessageType::Ephemeral(operation))?; @@ -233,13 +230,13 @@ impl SubscriptionInner { Ok(()) } - /// Set the name for a given document + /// Set the name for a given topic /// /// This information will be written to the database - pub async fn set_name(&self, name: Option) -> Result<(), DocumentError> { + pub async fn set_name(&self, name: Option) -> Result<(), TopicError> { self.node - .document_store - .set_name_for_document(&self.id, name) + .topic_store + .set_name_for_topic(&self.id, name) .await?; Ok(()) @@ -247,11 +244,11 @@ impl SubscriptionInner { } // FIXME: return errors -async fn setup_network( +async fn setup_network( node: &Arc, network: &Network, id: TopicId, - document: &Arc, + subscribable_topic: &Arc, author_tracker: &Arc>, ) -> ( Option>, @@ -264,21 +261,21 @@ async fn setup_network( Ok(result) => result, Err(error) => { warn!( - "Failed to setup network for subscription to document {}: {error}", + "Failed to setup network for subscription to topic {}: {error}", hex::encode(id) ); return (None, None, abort_handles); } }; - let mut document_rx = stream.subscribe().await.unwrap(); - let document_tx = stream; + let mut topic_rx = stream.subscribe().await.unwrap(); + let topic_tx = stream; let (persistent_tx, persistent_rx) = mpsc::channel::<(Header, Option, Vec)>(128); let abort_handle = spawn(async move { - while let Ok(event) = document_rx.recv().await { + while let Ok(event) = topic_rx.recv().await { match event.event() { TopicLogSyncEvent::Operation(operation) => { match validate_and_unpack(operation.as_ref().to_owned(), id) { @@ -300,25 +297,25 @@ async fn setup_network( abort_handles.push(abort_handle); - // Generate a different topic than eventually consistent streams to avoid collisions. + // Generate a different id than the eventually consistent streams to avoid collisions. // - // @TODO(adz): We want to throw an error if users try to subscribe with the same topic across + // @TODO(adz): We want to throw an error if users try to subscribe with the same id across // different streams. - let topic = Hash::new(id); - let ephemeral_stream = network.ephemeral_stream(topic.into()).await.unwrap(); + let ephemeral_id = Hash::new(id); + let ephemeral_stream = network.ephemeral_stream(ephemeral_id.into()).await.unwrap(); let mut ephemeral_rx = ephemeral_stream.subscribe().await.unwrap(); let ephemeral_tx = ephemeral_stream; - author_tracker.set_document_tx(Some(ephemeral_tx)).await; + author_tracker.set_topic_tx(Some(ephemeral_tx)).await; let author_tracker_clone = author_tracker.clone(); - let document_clone = document.clone(); + let subscribable_topic_clone = subscribable_topic.clone(); let abort_handle = spawn(async move { while let Ok(bytes) = ephemeral_rx.recv().await { match decode_cbor(&bytes[..]) { Ok(MessageType::Ephemeral(operation)) => { if let Some((author, body)) = operation.validate_and_unpack() { - document_clone.ephemeral_bytes_received(author, body); + subscribable_topic_clone.ephemeral_bytes_received(author, body); } else { warn!("Got ephemeral operation with a bad signature"); } @@ -370,13 +367,13 @@ async fn setup_network( }); let node = node.clone(); - let document_clone = document.clone(); - // Send checked and ingested operations for this document to application layer. + let subscribable_topic_clone = subscribable_topic.clone(); + // Send checked and ingested operations for this topic to application layer. let abort_handle = spawn(async move { while let Some(operation) = stream.next().await { - // When we discover a new author we need to add them to our document store. + // When we discover a new author we need to add them to our topic store. if let Err(error) = node - .document_store + .topic_store .add_author(&id, &operation.header.public_key) .await { @@ -385,7 +382,8 @@ async fn setup_network( // Forward the payload up to the app. if let Some(body) = operation.body { - document_clone.bytes_received(operation.header.public_key, body.to_bytes()); + subscribable_topic_clone + .bytes_received(operation.header.public_key, body.to_bytes()); } } }) @@ -400,18 +398,15 @@ async fn setup_network( abort_handles.push(abort_handle); - info!( - "Network subscription set up for document {}", - hex::encode(id) - ); + info!("Network subscription set up for topic {}", hex::encode(id)); - let topic = Hash::new(id); - let ephemeral_tx = network.ephemeral_stream(topic.into()).await.unwrap(); + let ephemeral_id = Hash::new(id); + let ephemeral_tx = network.ephemeral_stream(ephemeral_id.into()).await.unwrap(); - (Some(document_tx), Some(ephemeral_tx), abort_handles) + (Some(topic_tx), Some(ephemeral_tx), abort_handles) } -async fn teardown_network( +async fn teardown_network( id: &TopicId, author_tracker: &Arc>, tx: Option>, @@ -422,13 +417,13 @@ async fn teardown_network( handle.abort(); } - author_tracker.set_document_tx(None).await; + author_tracker.set_topic_tx(None).await; if let Some(ephemeral_tx) = ephemeral_tx && let Err(error) = ephemeral_tx.close() { error!( - "Failed to tear down ephemeral channel for document {}: {error}", + "Failed to tear down ephemeral channel for topic {}: {error}", hex::encode(id) ); } @@ -436,12 +431,12 @@ async fn teardown_network( if let Some(tx) = tx { if let Err(error) = tx.close() { error!( - "Failed to tear down persistent channel for document {}: {error}", + "Failed to tear down persistent channel for topic {}: {error}", hex::encode(id) ); } info!( - "Network subscription torn down for document {}", + "Network subscription torn down for topic {}", hex::encode(id) ); } @@ -453,8 +448,8 @@ type OperationWithRawHeader = (Header, Option, Vec { header, body, .. } = operation; let Some(operation_id): Option = header.extension() else { - return Err(UnpackError::InvalidDocumentId); + return Err(UnpackError::InvalidTopicId); }; if operation_id != id { - return Err(UnpackError::InvalidDocumentId); + return Err(UnpackError::InvalidTopicId); } Ok((header.clone(), body, header.to_bytes())) diff --git a/reflection-node/src/document.rs b/reflection-node/src/topic.rs similarity index 84% rename from reflection-node/src/document.rs rename to reflection-node/src/topic.rs index 0da43b0..c2319d0 100644 --- a/reflection-node/src/document.rs +++ b/reflection-node/src/topic.rs @@ -10,16 +10,16 @@ use thiserror::Error; use tokio::task::{AbortHandle, JoinError}; use tracing::info; -impl From>> for DocumentError { +impl From>> for TopicError { fn from(value: StreamError>) -> Self { - DocumentError::Publish(Box::new(value)) + TopicError::Publish(Box::new(value)) } } #[derive(Debug, Error)] -pub enum DocumentError { +pub enum TopicError { #[error(transparent)] - DocumentStore(#[from] sqlx::Error), + TopicStore(#[from] sqlx::Error), #[error(transparent)] OperationStore(#[from] CreationError), #[error(transparent)] @@ -33,7 +33,7 @@ pub enum DocumentError { Runtime(#[from] JoinError), } -pub trait SubscribableDocument: Sync + Send { +pub trait SubscribableTopic: Sync + Send { fn bytes_received(&self, author: PublicKey, data: Vec); fn author_joined(&self, author: PublicKey); fn author_left(&self, author: PublicKey); @@ -52,7 +52,7 @@ impl Drop for Subscription { } } -impl Subscription { +impl Subscription { pub(crate) async fn new(runtime: tokio::runtime::Handle, inner: SubscriptionInner) -> Self { let inner = Arc::new(inner); @@ -70,28 +70,28 @@ impl Subscription { } } - pub async fn send_delta(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_delta(&self, data: Vec) -> Result<(), TopicError> { let inner = self.inner.clone(); self.runtime .spawn(async move { inner.send_delta(data).await }) .await? } - pub async fn send_snapshot(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_snapshot(&self, data: Vec) -> Result<(), TopicError> { let inner = self.inner.clone(); self.runtime .spawn(async move { inner.send_snapshot(data).await }) .await? } - pub async fn send_ephemeral(&self, data: Vec) -> Result<(), DocumentError> { + pub async fn send_ephemeral(&self, data: Vec) -> Result<(), TopicError> { let inner = self.inner.clone(); self.runtime .spawn(async move { inner.send_ephemeral(data).await }) .await? } - pub async fn unsubscribe(self) -> Result<(), DocumentError> { + pub async fn unsubscribe(self) -> Result<(), TopicError> { let id = self.inner.id; self.network_monitor_task.abort(); @@ -100,15 +100,15 @@ impl Subscription { .spawn(async move { inner.unsubscribe().await }) .await??; - info!("Unsubscribed from document {}", hex::encode(id)); + info!("Unsubscribed from topic {}", hex::encode(id)); Ok(()) } - /// Set the name for a given document + /// Set the name for a given topic /// /// This information will be written to the database - pub async fn set_name(&self, name: Option) -> Result<(), DocumentError> { + pub async fn set_name(&self, name: Option) -> Result<(), TopicError> { let inner = self.inner.clone(); self.runtime .spawn(async move { inner.set_name(name).await }) diff --git a/reflection-node/src/document_store.rs b/reflection-node/src/topic_store.rs similarity index 83% rename from reflection-node/src/document_store.rs rename to reflection-node/src/topic_store.rs index 286b38b..6d33a9b 100644 --- a/reflection-node/src/document_store.rs +++ b/reflection-node/src/topic_store.rs @@ -14,7 +14,7 @@ use crate::operation::{LogType, ReflectionExtensions}; use crate::operation_store::OperationStore; #[derive(Debug, FromRow)] -pub struct StoreDocument { +pub struct StoreTopic { #[sqlx(try_from = "Vec")] pub id: TopicId, #[sqlx(default)] @@ -31,11 +31,11 @@ pub struct Author { } #[derive(Clone, Debug)] -pub struct DocumentStore { +pub struct TopicStore { pool: sqlx::SqlitePool, } -impl DocumentStore { +impl TopicStore { pub fn new(pool: sqlx::SqlitePool) -> Self { Self { pool } } @@ -52,16 +52,16 @@ impl DocumentStore { .collect()) } - pub async fn documents(&self) -> sqlx::Result> { - let mut documents: Vec = - sqlx::query_as("SELECT id, name, last_accessed FROM documents") + pub async fn topics(&self) -> sqlx::Result> { + let mut topics: Vec = + sqlx::query_as("SELECT id, name, last_accessed FROM topics") .fetch_all(&self.pool) .await?; let authors = sqlx::query("SELECT public_key, topic_id, last_seen FROM authors") .fetch_all(&self.pool) .await?; - let mut authors_per_document = authors.iter().fold(HashMap::new(), |mut acc, row| { + let mut authors_per_topic = authors.iter().fold(HashMap::new(), |mut acc, row| { let Ok(id) = TopicId::try_from(row.get::<&[u8], _>("topic_id")) else { return acc; }; @@ -78,20 +78,20 @@ impl DocumentStore { acc }); - for document in &mut documents { - if let Some(authors) = authors_per_document.remove(&document.id) { - document.authors = authors; + for topic in &mut topics { + if let Some(authors) = authors_per_topic.remove(&topic.id) { + topic.authors = authors; } } - Ok(documents) + Ok(topics) } - pub async fn add_document(&self, id: &TopicId) -> sqlx::Result<()> { - // The id is the primary key in the table therefore ignore insertion when the document exists already + pub async fn add_topic(&self, id: &TopicId) -> sqlx::Result<()> { + // The id is the primary key in the table therefore ignore insertion when the topic exists already sqlx::query( " - INSERT OR IGNORE INTO documents ( id ) + INSERT OR IGNORE INTO topics ( id ) VALUES ( ? ) ", ) @@ -102,8 +102,8 @@ impl DocumentStore { Ok(()) } - pub async fn delete_document(&self, id: &TopicId) -> sqlx::Result<()> { - sqlx::query("DELETE FROM documents WHERE id = ?") + pub async fn delete_topic(&self, id: &TopicId) -> sqlx::Result<()> { + sqlx::query("DELETE FROM topics WHERE id = ?") .bind(id.as_slice()) .execute(&self.pool) .await?; @@ -147,14 +147,10 @@ impl DocumentStore { Ok(()) } - pub async fn set_name_for_document( - &self, - id: &TopicId, - name: Option, - ) -> sqlx::Result<()> { + pub async fn set_name_for_topic(&self, id: &TopicId, name: Option) -> sqlx::Result<()> { sqlx::query( " - UPDATE documents + UPDATE topics SET name = ? WHERE id = ? ", @@ -167,14 +163,14 @@ impl DocumentStore { Ok(()) } - pub async fn set_last_accessed_for_document( + pub async fn set_last_accessed_for_topic( &self, id: &TopicId, last_accessed: Option>, ) -> sqlx::Result<()> { sqlx::query( " - UPDATE documents + UPDATE topics SET last_accessed = ? WHERE id = ? ", @@ -187,7 +183,7 @@ impl DocumentStore { Ok(()) } - pub async fn operations_for_document( + pub async fn operations_for_topic( &self, operation_store: &OperationStore, id: &TopicId, @@ -237,12 +233,12 @@ impl DocumentStore { pub struct LogId(LogType, TopicId); impl LogId { - pub fn new(log_type: LogType, document: &TopicId) -> Self { - Self(log_type, *document) + pub fn new(log_type: LogType, topic: &TopicId) -> Self { + Self(log_type, *topic) } } -impl TopicLogMap for DocumentStore { +impl TopicLogMap for TopicStore { type Error = sqlx::Error; async fn get(&self, topic: &TopicId) -> Result, Self::Error> { From 4a0dd65e5ee1ed62feeb70661af07f86a0a3fdad Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 10 Dec 2025 12:36:14 +0100 Subject: [PATCH 11/11] Rename CNN to cx.modal.reflection Close: https://github.com/p2panda/reflection/issues/139 --- .github/workflows/build.yml | 6 +++--- README.md | 2 +- ...nda.reflection.json => cx.modal.reflection.json | 2 +- meson.build | 2 +- ...n.desktop.in => cx.modal.reflection.desktop.in} | 2 +- ...gschema.xml => cx.modal.reflection.gschema.xml} | 2 +- ....xml.in => cx.modal.reflection.metainfo.xml.in} | 4 ++-- ...n.service.in => cx.modal.reflection.service.in} | 2 +- ...mbolic.svg => cx.modal.reflection-symbolic.svg} | 0 ...ion.Devel.svg => cx.modal.reflection.Devel.svg} | 0 ...n.Source.svg => cx.modal.reflection.Source.svg} | 2 +- ...anda.reflection.svg => cx.modal.reflection.svg} | 0 reflection-app/data/meson.build | 14 +++++++------- .../data/resources/resources.gresource.xml | 2 +- reflection-app/po/POTFILES.in | 6 +++--- reflection-app/src/landing_view/landing_view.blp | 2 +- reflection-app/src/main.rs | 2 +- reflection-app/src/ui-resources.gresource.xml | 2 +- 18 files changed, 26 insertions(+), 26 deletions(-) rename org.p2panda.reflection.json => cx.modal.reflection.json (97%) rename reflection-app/data/{org.p2panda.reflection.desktop.in => cx.modal.reflection.desktop.in} (84%) rename reflection-app/data/{org.p2panda.reflection.gschema.xml => cx.modal.reflection.gschema.xml} (60%) rename reflection-app/data/{org.p2panda.reflection.metainfo.xml.in => cx.modal.reflection.metainfo.xml.in} (97%) rename reflection-app/data/{org.p2panda.reflection.service.in => cx.modal.reflection.service.in} (69%) rename reflection-app/data/icons/{org.p2panda.reflection-symbolic.svg => cx.modal.reflection-symbolic.svg} (100%) rename reflection-app/data/icons/{org.p2panda.reflection.Devel.svg => cx.modal.reflection.Devel.svg} (100%) rename reflection-app/data/icons/{org.p2panda.reflection.Source.svg => cx.modal.reflection.Source.svg} (99%) rename reflection-app/data/icons/{org.p2panda.reflection.svg => cx.modal.reflection.svg} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4cc9fb..c1a8282 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: reflection.flatpak - manifest-path: org.p2panda.reflection.json + manifest-path: cx.modal.reflection.json cache-key: flatpak-builder-${{ github.sha }} arch: ${{ matrix.variant.arch }} run-tests: true @@ -36,7 +36,7 @@ jobs: # the zip artifact, extract it, install the flatpak and run it. # unzip reflection-x86_64.zip # flatpak --user install reflection.flatpak - # flatpak run org.p2panda.reflection + # flatpak run cx.modal.reflection macos: if: false # This disable macos for now. @@ -187,7 +187,7 @@ jobs: CFBundleExecutable reflection CFBundleIdentifier - org.p2panda.reflection + cx.modal.reflection CFBundleName Reflection CFBundlePackageType diff --git a/README.md b/README.md index 4efe942..9389efe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

Reflection

Collaboratively take meeting notes, even when there's no internet

diff --git a/org.p2panda.reflection.json b/cx.modal.reflection.json similarity index 97% rename from org.p2panda.reflection.json rename to cx.modal.reflection.json index 79612f8..055e434 100644 --- a/org.p2panda.reflection.json +++ b/cx.modal.reflection.json @@ -1,5 +1,5 @@ { - "id" : "org.p2panda.reflection", + "id" : "cx.modal.reflection", "runtime" : "org.gnome.Platform", "runtime-version" : "49", "sdk" : "org.gnome.Sdk", diff --git a/meson.build b/meson.build index b03e07d..ba76793 100644 --- a/meson.build +++ b/meson.build @@ -7,7 +7,7 @@ project('reflection', 'rust', i18n = import('i18n') gnome = import('gnome') -application_id = 'org.p2panda.reflection' +application_id = 'cx.modal.reflection' pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name() iconsdir = get_option('datadir') / 'icons' diff --git a/reflection-app/data/org.p2panda.reflection.desktop.in b/reflection-app/data/cx.modal.reflection.desktop.in similarity index 84% rename from reflection-app/data/org.p2panda.reflection.desktop.in rename to reflection-app/data/cx.modal.reflection.desktop.in index 8f79acc..cdf5906 100644 --- a/reflection-app/data/org.p2panda.reflection.desktop.in +++ b/reflection-app/data/cx.modal.reflection.desktop.in @@ -1,7 +1,7 @@ [Desktop Entry] Name=reflection Exec=reflection -Icon=org.p2panda.reflection +Icon=cx.modal.reflection Terminal=false Type=Application Categories=Utility; diff --git a/reflection-app/data/org.p2panda.reflection.gschema.xml b/reflection-app/data/cx.modal.reflection.gschema.xml similarity index 60% rename from reflection-app/data/org.p2panda.reflection.gschema.xml rename to reflection-app/data/cx.modal.reflection.gschema.xml index f3a8d95..d3a1451 100644 --- a/reflection-app/data/org.p2panda.reflection.gschema.xml +++ b/reflection-app/data/cx.modal.reflection.gschema.xml @@ -1,5 +1,5 @@ - + diff --git a/reflection-app/data/org.p2panda.reflection.metainfo.xml.in b/reflection-app/data/cx.modal.reflection.metainfo.xml.in similarity index 97% rename from reflection-app/data/org.p2panda.reflection.metainfo.xml.in rename to reflection-app/data/cx.modal.reflection.metainfo.xml.in index ee94722..71b02a8 100644 --- a/reflection-app/data/org.p2panda.reflection.metainfo.xml.in +++ b/reflection-app/data/cx.modal.reflection.metainfo.xml.in @@ -1,6 +1,6 @@ - org.p2panda.reflection + cx.modal.reflection CC0-1.0 GPL-3.0-or-later @@ -43,7 +43,7 @@ reflection - org.p2panda.reflection.desktop + cx.modal.reflection.desktop intense diff --git a/reflection-app/data/org.p2panda.reflection.service.in b/reflection-app/data/cx.modal.reflection.service.in similarity index 69% rename from reflection-app/data/org.p2panda.reflection.service.in rename to reflection-app/data/cx.modal.reflection.service.in index 0eb9ecc..b4ba629 100644 --- a/reflection-app/data/org.p2panda.reflection.service.in +++ b/reflection-app/data/cx.modal.reflection.service.in @@ -1,3 +1,3 @@ [D-BUS Service] -Name=org.p2panda.reflection +Name=cx.modal.reflection Exec=@bindir@/reflection --gapplication-service diff --git a/reflection-app/data/icons/org.p2panda.reflection-symbolic.svg b/reflection-app/data/icons/cx.modal.reflection-symbolic.svg similarity index 100% rename from reflection-app/data/icons/org.p2panda.reflection-symbolic.svg rename to reflection-app/data/icons/cx.modal.reflection-symbolic.svg diff --git a/reflection-app/data/icons/org.p2panda.reflection.Devel.svg b/reflection-app/data/icons/cx.modal.reflection.Devel.svg similarity index 100% rename from reflection-app/data/icons/org.p2panda.reflection.Devel.svg rename to reflection-app/data/icons/cx.modal.reflection.Devel.svg diff --git a/reflection-app/data/icons/org.p2panda.reflection.Source.svg b/reflection-app/data/icons/cx.modal.reflection.Source.svg similarity index 99% rename from reflection-app/data/icons/org.p2panda.reflection.Source.svg rename to reflection-app/data/icons/cx.modal.reflection.Source.svg index 66e7935..e7c152a 100644 --- a/reflection-app/data/icons/org.p2panda.reflection.Source.svg +++ b/reflection-app/data/icons/cx.modal.reflection.Source.svg @@ -10,7 +10,7 @@ id="svg11300" sodipodi:version="0.32" inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" - sodipodi:docname="org.p2panda.Reflection.Source.svg" + sodipodi:docname="cx.modal.reflection.Source.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" version="1.0" style="display:inline;enable-background:new" diff --git a/reflection-app/data/icons/org.p2panda.reflection.svg b/reflection-app/data/icons/cx.modal.reflection.svg similarity index 100% rename from reflection-app/data/icons/org.p2panda.reflection.svg rename to reflection-app/data/icons/cx.modal.reflection.svg diff --git a/reflection-app/data/meson.build b/reflection-app/data/meson.build index d9d09e5..033cdaf 100644 --- a/reflection-app/data/meson.build +++ b/reflection-app/data/meson.build @@ -1,6 +1,6 @@ desktop_file = i18n.merge_file( - input: 'org.p2panda.reflection.desktop.in', - output: 'org.p2panda.reflection.desktop', + input: 'cx.modal.reflection.desktop.in', + output: 'cx.modal.reflection.desktop', type: 'desktop', po_dir: '../po', install: true, @@ -13,8 +13,8 @@ if desktop_utils.found() endif appstream_file = i18n.merge_file( - input: 'org.p2panda.reflection.metainfo.xml.in', - output: 'org.p2panda.reflection.metainfo.xml', + input: 'cx.modal.reflection.metainfo.xml.in', + output: 'cx.modal.reflection.metainfo.xml', po_dir: '../po', install: true, install_dir: get_option('datadir') / 'metainfo' @@ -24,7 +24,7 @@ appstreamcli = find_program('appstreamcli', required: false, disabler: true) test('Validate appstream file', appstreamcli, args: ['validate', '--no-net', '--explain', appstream_file]) -install_data('org.p2panda.reflection.gschema.xml', +install_data('cx.modal.reflection.gschema.xml', install_dir: get_option('datadir') / 'glib-2.0' / 'schemas' ) @@ -37,8 +37,8 @@ test('Validate schema file', service_conf = configuration_data() service_conf.set('bindir', get_option('prefix') / get_option('bindir')) configure_file( - input: 'org.p2panda.reflection.service.in', - output: 'org.p2panda.reflection.service', + input: 'cx.modal.reflection.service.in', + output: 'cx.modal.reflection.service', configuration: service_conf, install_dir: get_option('datadir') / 'dbus-1' / 'services' ) diff --git a/reflection-app/data/resources/resources.gresource.xml b/reflection-app/data/resources/resources.gresource.xml index 50f24b6..e76528e 100644 --- a/reflection-app/data/resources/resources.gresource.xml +++ b/reflection-app/data/resources/resources.gresource.xml @@ -1,6 +1,6 @@ - + icons/scalable/actions/about-symbolic.svg icons/scalable/actions/bluetooth-symbolic.svg icons/scalable/status/folder-symbolic.svg diff --git a/reflection-app/po/POTFILES.in b/reflection-app/po/POTFILES.in index 8200340..b130fed 100644 --- a/reflection-app/po/POTFILES.in +++ b/reflection-app/po/POTFILES.in @@ -1,8 +1,8 @@ # List of source files containing translatable strings. # Please keep this file sorted alphabetically. -data/org.p2panda.reflection.desktop.in -data/org.p2panda.reflection.metainfo.xml.in -data/org.p2panda.reflection.gschema.xml +data/cx.modal.reflection.desktop.in +data/cx.modal.reflection.metainfo.xml.in +data/cx.modal.reflection.gschema.xml src/application.rs src/components/zoom_level_selector.blp src/connection_popover/author_list.rs diff --git a/reflection-app/src/landing_view/landing_view.blp b/reflection-app/src/landing_view/landing_view.blp index e1129c6..8aeb836 100644 --- a/reflection-app/src/landing_view/landing_view.blp +++ b/reflection-app/src/landing_view/landing_view.blp @@ -49,7 +49,7 @@ template $ReflectionLandingView: Adw.NavigationPage { StackPage { name: "no-documents"; child: Adw.StatusPage no-document-page { - icon-name: "org.p2panda.reflection-symbolic"; + icon-name: "cx.modal.reflection-symbolic"; title: _("Take Notes, Together"); child: Box { orientation: vertical; diff --git a/reflection-app/src/main.rs b/reflection-app/src/main.rs index d7d6314..f85bf66 100644 --- a/reflection-app/src/main.rs +++ b/reflection-app/src/main.rs @@ -66,7 +66,7 @@ fn main() -> glib::ExitCode { // Create a new GtkApplication. The application manages our main loop, // application windows, integration with the window manager/compositor, and // desktop features such as file opening and single-instance applications. - let app = ReflectionApplication::new("org.p2panda.reflection", &gio::ApplicationFlags::empty()); + let app = ReflectionApplication::new("cx.modal.reflection", &gio::ApplicationFlags::empty()); info!("Reflection ({})", APP_ID); info!("Version: {}", VERSION); diff --git a/reflection-app/src/ui-resources.gresource.xml b/reflection-app/src/ui-resources.gresource.xml index 2e2f572..57dbd12 100644 --- a/reflection-app/src/ui-resources.gresource.xml +++ b/reflection-app/src/ui-resources.gresource.xml @@ -1,6 +1,6 @@ - + shortcuts-dialog.ui style.css