gun/lib.rs
1//! # Gun.rs - A Real-time, Decentralized, Offline-First Graph Database
2//!
3//! Gun.rs is a Rust implementation of the [Gun.js](https://gun.eco/) protocol, providing a
4//! real-time, decentralized, offline-first graph database. It enables peer-to-peer data
5//! synchronization without requiring a central server.
6//!
7//! ## Features
8//!
9//! - **Real-time Synchronization**: Automatic data synchronization with connected peers
10//! - **Decentralized**: P2P mesh networking - no central server required
11//! - **Offline-First**: Works locally and syncs when connected
12//! - **Graph Database**: Store data as a graph with nodes and relationships
13//! - **Conflict Resolution**: Automatic conflict resolution using state timestamps (HAM algorithm)
14//! - **Cryptographic Security**: BLS signatures for message authentication
15//! - **Pluggable Storage**: Memory, file-based (localStorage-like), or Sled database
16//! - **WebRTC Support**: Direct peer-to-peer connections with NAT traversal
17//! - **WebSocket Support**: Relay server connections for NAT traversal
18//!
19//! ## Quick Start
20//!
21//! ```rust,no_run
22//! use gun::{Gun, GunOptions};
23//! use chia_bls::{SecretKey, PublicKey};
24//! use serde_json::json;
25//!
26//! #[tokio::main]
27//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
28//! // Generate BLS key pair
29//! let secret_key = SecretKey::from_seed(&[0u8; 32]);
30//! let public_key = secret_key.public_key();
31//!
32//! // Create a local Gun instance
33//! let gun = Gun::new(secret_key, public_key);
34//!
35//! // Store data
36//! gun.get("user").put(json!({
37//! "name": "Alice",
38//! "age": 30
39//! })).await?;
40//!
41//! // Read data once
42//! gun.get("user").once(|data, _key| {
43//! println!("User: {:?}", data);
44//! }).await?;
45//!
46//! // Subscribe to updates
47//! gun.get("user").on(|data, _key| {
48//! println!("User updated: {:?}", data);
49//! });
50//!
51//! // Connect to peers (optional)
52//! let options = GunOptions {
53//! peers: vec!["ws://relay.example.com/gun".to_string()],
54//! ..Default::default()
55//! };
56//! let gun_with_peers = Gun::with_options(secret_key, public_key, options).await?;
57//!
58//! Ok(())
59//! }
60//! ```
61//!
62//! ## Architecture
63//!
64//! ### Core Components
65//!
66//! - **[`Gun`](gun::Gun)**: Main entry point - creates and manages Gun instances
67//! - **[`Chain`](chain::Chain)**: Fluent API for data operations (get, put, on, once, map, set)
68//! - **[`GunCore`](core::GunCore)**: Core engine managing graph, state, events, and storage
69//! - **[`Graph`](graph::Graph)**: In-memory graph storage with conflict resolution
70//! - **[`Mesh`](dam::Mesh)**: DAM protocol implementation for P2P message routing
71//! - **[`Storage`](storage::Storage)**: Pluggable storage backend trait
72//!
73//! ### Data Model
74//!
75//! Gun uses a graph data model where:
76//! - **Nodes**: Identified by a unique "soul" (UUID-like string)
77//! - **Properties**: Key-value pairs on nodes
78//! - **References**: Links between nodes using soul references `{"#": "soul_id"}`
79//! - **State**: Timestamps for conflict resolution
80//!
81//! ### Conflict Resolution
82//!
83//! Gun uses the HAM (Hypothetical Amnesia Machine) algorithm:
84//! - Each property update gets a state timestamp
85//! - Higher state wins in conflicts
86//! - Automatic merge of non-conflicting updates
87//!
88//! ### Network Protocol
89//!
90//! Gun uses the DAM (Directed Acyclic Mesh) protocol:
91//! - Message deduplication to prevent loops
92//! - Automatic routing through peer mesh
93//! - Support for WebSocket relays and WebRTC direct connections
94//!
95//! ## Examples
96//!
97//! See the `examples/` directory for comprehensive examples including:
98//! - Basic operations (put, get, once, on)
99//! - Chain operations (get, put, back, map, set)
100//! - Network synchronization
101//! - Storage backends
102//! - WebRTC connections
103//! - Certificate-based security (SEA)
104//!
105//! ## Security
106//!
107//! Gun.rs supports cryptographic security through:
108//! - **BLS Signatures**: All messages are signed and verified
109//! - **SEA (Security, Encryption, Authorization)**: Certificate-based access control
110//! - **Message Predicates**: Custom filtering logic for incoming messages
111//!
112//! ## Performance
113//!
114//! - In-memory graph for fast reads
115//! - Persistent storage options for durability
116//! - Message batching and deduplication
117//! - Efficient conflict resolution algorithm
118//!
119//! ## Compatibility
120//!
121//! Gun.rs aims to be compatible with Gun.js:
122//! - Same protocol and message format
123//! - Compatible graph structure
124//! - Interoperable with JavaScript peers
125//!
126//! ## License
127//!
128//! See the LICENSE file for license information.
129//!
130//! ## Links
131//!
132//! - [Gun.js Documentation](https://gun.eco/docs)
133//! - [Gun.js GitHub](https://github.com/amark/gun)
134
135pub mod chain;
136pub mod core;
137pub mod dam;
138pub mod dup;
139pub mod error;
140pub mod events;
141pub mod graph;
142pub mod gun;
143pub mod sea;
144pub mod state;
145pub mod storage;
146pub mod types;
147pub mod valid;
148pub mod webrtc;
149pub mod websocket;
150
151pub use chain::Chain;
152pub use error::GunError;
153pub use gun::{Gun, GunOptions};
154pub use sea::*;
155pub use types::MessagePredicate;
156pub use valid::valid;
157pub use valid::{is_valid_data, valid_soul};
158pub use webrtc::{WebRTCManager, WebRTCOptions, WebRTCPeer};
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use chia_bls::{SecretKey, PublicKey};
164
165 #[tokio::test]
166 async fn test_basic_put_get() {
167 // Generate BLS key pair for testing
168 let secret_key = SecretKey::from_seed(&[0u8; 32]);
169 let public_key = secret_key.public_key();
170
171 let gun = Gun::new(secret_key, public_key);
172 let chain = gun.get("test");
173 // In tests, unwrap is acceptable for error handling
174 chain
175 .put(serde_json::json!({"name": "test"}))
176 .await
177 .expect("put should succeed in test");
178
179 let mut called = false;
180 chain
181 .once(|data, _key| {
182 called = true;
183 assert!(data.is_object());
184 })
185 .await
186 .expect("once should succeed in test");
187 assert!(called);
188 }
189}