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