Skip to main content

p2panda_net/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! Data-type-agnostic p2p networking, discovery, gossip and local-first sync.
6//!
7//! ## Features
8//!
9//! `p2panda-net` is a collection of Rust modules providing solutions for a whole set of
10//! peer-to-peer and [local-first] application requirements. Collectively these modules solve the
11//! problem of event delivery.
12//!
13//! - [Publish & Subscribe] for ephemeral messages (gossip protocol)
14//! - Publish & Subscribe for messages with [Eventual Consistency] guarantee (sync protocol)
15//! - Confidentially discover nodes who are interested in the same topic ([Private Set Intersection])
16//! - Establish and manage direct connections to any device over the Internet (using [iroh])
17//! - Monitor system with supervisors and restart modules on critical failure (Erlang-inspired
18//!   [Supervision Trees])
19//! - Modular API allowing users to choose or replace the layers they want to use
20//!
21//! ## Getting Started
22//!
23//! Install the Rust crate using `cargo add p2panda-net`.
24//!
25//! ```rust
26//! # use std::error::Error;
27//! #
28//! # #[tokio::main]
29//! # async fn main() -> Result<(), Box<dyn Error>> {
30//! use futures_util::StreamExt;
31//! use p2panda_core::Hash;
32//! use p2panda_net::iroh_mdns::MdnsDiscoveryMode;
33//! use p2panda_net::{AddressBook, Discovery, Endpoint, MdnsDiscovery, Gossip};
34//!
35//! // Topics are used to discover other nodes and establish connections around them.
36//! let topic = Hash::digest(b"shirokuma-cafe").into();
37//!
38//! // Maintain an address book of newly discovered or manually added nodes.
39//! let address_book = AddressBook::builder().spawn().await?;
40//!
41//! // Establish direct connections to any device with the help of iroh.
42//! let endpoint = Endpoint::builder(address_book.clone())
43//!     .spawn()
44//!     .await?;
45//!
46//! // Discover nodes on your local-area network.
47//! let mdns = MdnsDiscovery::builder(address_book.clone(), endpoint.clone())
48//!     .mode(MdnsDiscoveryMode::Active)
49//!     .spawn()
50//!     .await?;
51//!
52//! // Confidentially discover nodes interested in the same topic.
53//! let discovery = Discovery::builder(address_book.clone(), endpoint.clone())
54//!     .spawn()
55//!     .await?;
56//!
57//! // Disseminate messages among nodes.
58//! let gossip = Gossip::builder(address_book.clone(), endpoint.clone())
59//!     .spawn()
60//!     .await?;
61//!
62//! // Join topic to publish and subscribe to stream of (ephemeral) messages.
63//! let cafe = gossip.stream(topic).await?;
64//!
65//! // This message will be seen by other nodes if they're online. If you want messages to arrive
66//! // eventually, even when they've been offline, you need to use p2panda's "sync" module.
67//! cafe.publish(b"Hello, Panda!").await?;
68//!
69//! let mut rx = cafe.subscribe();
70//! tokio::spawn(async move {
71//!     while let Some(Ok(bytes)) = rx.next().await {
72//!         println!("{}", String::from_utf8(bytes).expect("valid UTF-8 string"));
73//!     }
74//! });
75//! #
76//! # Ok(())
77//! # }
78//! ```
79//!
80//! For a complete command-line application using `p2panda-net` with a sync protocol, see our
81//! [`chat.rs`] example.
82//!
83//! ## Local-first Event Delivery
84//!
85//! `p2panda-net` is concerned with the **event delivery** layer of peer-to-peer and local-first
86//! application stacks.
87//!
88//! This layer ensures that your application's data eventually arrives on all devices in a
89//! peer-to-peer fashion, no matter where they are and if they've been offline.
90//!
91//! ## Decentralised and offline-first
92//!
93//! `p2panda-net` is designed for ad-hoc network topologies with **no central registry** and where
94//! the size of the network might be unknown. Nodes can go on- or offline at any point in time.
95//!
96//! ## Broadcast topology
97//!
98//! The [Publish & Subscribe] methods in `p2panda-net` suggest a broadcast topology, where one node
99//! can communicate to a whole group by sending a single message.
100//!
101//! Reducing the API surface of direct connections helps with building a wide range of peer-to-peer
102//! applications which do not require knowledge of stateful connections but rather look for **state
103//! convergence**. This aligns well with the **local-first** paradigm.
104//!
105//! This approach is a prerequisite to make applications compatible with genuine broadcast-based
106//! communication systems, like **LoRa, Bluetooth Low Energy (BLE) or packet radio**.
107//!
108//! `p2panda-net` can be understood as a broadcast abstraction independent of the underlying
109//! transport, including the Internet Protocol or other stateful connection protocols where
110//! underneath we're still maintaining connections.
111//!
112//! ## Bring your own Data-Type
113//!
114//! `p2panda-net` is agnostic over the actual data of your application. It can be encoded in any
115//! way (JSON, CBOR, etc.) and hold any data you need (CRDTs, messages, etc.).
116//!
117//! Your choice of sync protocol will determine a concrete **Base Convergent Data Type** (Base
118//! CDT). The data type must be known in order to design efficient sync protocols. However, these
119//! data types are simply carriers for your own application data, regardless of what form it takes.
120//!
121//! If you're interested in bringing your own Base CDT (for example Merkle-Trees or Sets) we have
122//! lower-level APIs and traits in [`p2panda-sync`] which allow you to implement your own sync
123//! protocol next to the rest of `p2panda-net`.
124//!
125//! ## Modules
126//!
127//! All modules can be enabled by feature flags, most of them are enabled by default.
128//!
129//! ### Direct Internet connections with iroh [`Endpoint`]
130//!
131//! Most of the lower-level Internet Protocol networking of `p2panda-net` is made possible by the
132//! work of [iroh] utilising well-established and known standards, like QUIC for transport,
133//! (self-certified) TLS 1.3 for transport encryption, QUIC Address Discovery (QAD) for STUN and
134//! TURN servers for relayed fallbacks.
135//!
136//! ### Node and Confidental Topic [`Discovery`]
137//!
138//! Our random-walk discovery algorithm finds other nodes in the network without any centralised
139//! registry. Any node can serve as a **bootstrap** into the network.
140//!
141//! `p2panda-net` is designed around topics of shared interest and we need an additional topic
142//! discovery strategy to find nodes sharing the same topics.
143//!
144//! Topics usually represent identifiers or namespaces for data and documents associated with a
145//! specific group of people (for example a text document, chat group or image folder). For this
146//! reason, a topic should never be leaked to people outside of the intended group, whether
147//! accidentally or purposefully.
148//!
149//! Read more about how we've implemented confidential topic discovery (and thus sync) in
150//! [`p2panda-discovery`].
151//!
152//! ### Ephemeral Messaging via [`Gossip`] Protocol
153//!
154//! Not all messages in peer-to-peer applications need to be persisted, for example cursor
155//! positions or "awareness & presence" status.
156//!
157//! For these use-cases `p2panda-net` offers a gossip protocol to broadcast ephemeral messages to
158//! all online nodes interested in the same topic.
159//!
160//! ### Eventual Consistent local-first [`LogSync`]
161//!
162//! In local-first applications we want to converge towards the same state eventually, which
163//! requires nodes to catch up on missed messages - independent of if they've been offline or
164//! not.
165//!
166//! `p2panda-net` comes with a default `LogSync` protocol implementation which uses p2panda's
167//! **append-only log** Base Convergent Data Type (CDT).
168//!
169//! After initial sync finished, nodes switch to **live-mode** to directly push new messages to the
170//! network using a gossip protocol.
171//!
172//! ### Local [`MdnsDiscovery`] finding nearby devices
173//!
174//! Some devices might be already reachable on your local-area network where no Internet will be
175//! required to connect to them. mDNS discovery helps with finding these nodes.
176//!
177//! ### Manage nodes and associated topics in [`AddressBook`]
178//!
179//! To keep track of all nodes and their topic interests we're managing a local and persisted
180//! address book.
181//!
182//! The address book is an important tool to watch for transport information changes, keep track of
183//! stale nodes and **identify network partitions** which can be automatically "healed".
184//!
185//! Use the address book to manually add nodes to bootstrap a network from.
186//!
187//! ### Robust, failure-resistant modules with [`Supervisor`]
188//!
189//! All modules in `p2panda-net` are internally implemented with the [Actor Model]. Inspired by
190//! Erlang's [Supervision Trees] these actors can be monitored and automatically restarted on
191//! critical failure (caused by bugs in our code or third-party dependencies).
192//!
193//! Use the `supervisor` flag to enable this feature.
194//!
195//! ## Security & Privacy
196//!
197//! Every connection attempt to any node in a network can reveal sensitive meta-data, for example
198//! IP addresses or knowledge of the network or data itself.
199//!
200//! With `p2panda-net` we work towards a best-effort in containing accidental leakage of such
201//! information by:
202//!
203//! - Use **Network identifiers** to actively partition the network. Please note that the
204//!   identifier itself is not secret (yet). [`#965`]
205//! - Use **Confidential Discovery and Sync** to only reveal information about ourselves and
206//!   exchange application data with nodes who have knowledge of the same topic.
207//! - **Disable mDNS** discovery by default to avoid unknowingly leaking information in local-area
208//!   networks.
209//! - Give full control over which **boostrap nodes** and **STUN / TURN / relay servers** to
210//!   choose. They aid with establishing connections and overlays and can acquire more knowledge over
211//!   networking behaviour than other participants.
212//! - Allow **connecting to nodes** without any intermediaries, which unfortunately is only
213//!   possible if the address is directly reachable.
214//!
215//! In the future we're planning additional features to improve privacy:
216//!
217//! - Support **"onion" routing protocols** (Tor, I2P, Veilid) and mix-networks (Katzenpost) to
218//!   allow multi-hop routing without revealing the origin of the sender. [`#934`]
219//! - Introduce **Allow- and Deny-lists** for nodes to give fine-grained access with whom we can
220//!   ever form a connection with. This can be nicely paired with an access control system. [`#925`]
221//!
222//! [local-first]: https://www.inkandswitch.com/local-first-software/
223//! [Publish & Subscribe]: https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
224//! [Eventual Consistency]: https://en.wikipedia.org/wiki/Eventual_consistency
225//! [Actor Model]: https://en.wikipedia.org/wiki/Actor_model
226//! [Private Set Intersection]: https://en.wikipedia.org/wiki/Private_set_intersection
227//! [Supervision Trees]: https://adoptingerlang.org/docs/development/supervision_trees/
228//! [iroh]: https://www.iroh.computer/
229//! [`chat.rs`]: https://github.com/p2panda/p2panda/blob/main/p2panda-net/examples/chat.rs
230//! [`#925`]: https://github.com/p2panda/p2panda/issues/925
231//! [`#934`]: https://github.com/p2panda/p2panda/issues/934
232//! [`#965`]: https://github.com/p2panda/p2panda/issues/965
233//! [`p2panda-discovery`]: https://docs.rs/p2panda-discovery/latest/p2panda_discovery/
234//! [`p2panda-sync`]: https://docs.rs/p2panda-discovery/latest/p2panda_sync/
235#[cfg(feature = "address_book")]
236pub mod address_book;
237pub mod addrs;
238pub mod cbor;
239#[cfg(feature = "discovery")]
240pub mod discovery;
241#[cfg(feature = "gossip")]
242pub mod gossip;
243#[cfg(feature = "iroh_endpoint")]
244pub mod iroh_endpoint;
245#[cfg(feature = "iroh_mdns")]
246pub mod iroh_mdns;
247#[cfg(feature = "supervisor")]
248pub mod supervisor;
249#[cfg(feature = "sync")]
250pub mod sync;
251#[cfg(any(test, feature = "test_utils"))]
252#[doc(hidden)]
253pub mod test_utils;
254pub mod utils;
255pub mod watchers;
256
257#[cfg(feature = "address_book")]
258#[doc(inline)]
259pub use address_book::AddressBook;
260#[cfg(feature = "discovery")]
261#[doc(inline)]
262pub use discovery::Discovery;
263#[cfg(feature = "gossip")]
264#[doc(inline)]
265pub use gossip::Gossip;
266#[cfg(feature = "iroh_endpoint")]
267#[doc(inline)]
268pub use iroh_endpoint::Endpoint;
269#[cfg(feature = "iroh_mdns")]
270#[doc(inline)]
271pub use iroh_mdns::MdnsDiscovery;
272#[cfg(feature = "supervisor")]
273#[doc(inline)]
274pub use supervisor::Supervisor;
275#[cfg(feature = "sync")]
276#[doc(inline)]
277pub use sync::LogSync;
278
279/// Identifer for a node in the network.
280///
281/// Each node in p2panda has a unique identifier created as a cryptographic key to globally
282/// identify it and encrypt network traffic for this node only.
283pub type NodeId = p2panda_core::VerifyingKey;
284
285/// Identifier for a network.
286///
287/// The network identifier is used to achieve separation and prevent interoperability between
288/// distinct networks. This is the most global identifier to group nodes into networks. Different
289/// applications may choose to share the same underlying network infrastructure by using the same
290/// network identifier.
291///
292/// A BLAKE3 hash function is performed against each protocol identifier which is registered with
293/// `p2panda-net`, using the network identifier as an additional input. Even if two instances of
294/// `p2panda-net` are created with the same network protocols, any communication attempts will fail
295/// if they are not using the same network identifier.
296///
297/// **WARNING:** The network identifier is _not_ confidentially exchanged with a remote node and
298/// can not be treated as a secret value. See: <https://github.com/p2panda/p2panda/issues/965>
299pub type NetworkId = [u8; 32];
300
301/// Identifier for a protocol.
302///
303/// A BLAKE3 hash function is performed against each protocol identifier which is registered with
304/// `p2panda-net`, using the network identifier as an additional input. Even if two instances of
305/// `p2panda-net` are created with the same network protocols, any communication attempts will fail
306/// if they are not using the same network identifier.
307pub type ProtocolId = Vec<u8>;
308
309/// Default network identifier used by `p2panda-net` for all transports.
310///
311/// See [NetworkId] for configuring your custom identifier to opt-out of the default network.
312pub const DEFAULT_NETWORK_ID: NetworkId = [
313    247, 69, 248, 242, 132, 120, 159, 230, 98, 100, 214, 200, 78, 40, 79, 94, 174, 8, 12, 27, 84,
314    195, 246, 159, 132, 240, 79, 208, 1, 43, 132, 118,
315];
316
317/// Hash the concatenation of the given protocol- and network identifiers.
318fn hash_protocol_id_with_network_id(
319    protocol_id: impl AsRef<[u8]>,
320    network_id: NetworkId,
321) -> Vec<u8> {
322    p2panda_core::Hash::digest([protocol_id.as_ref(), &network_id].concat())
323        .as_bytes()
324        .to_vec()
325}