Gun.rs
A Rust port of Gun.js - a realtime, decentralized, offline-first, graph data synchronization engine.
⚠️ Important Disclaimer
This is an unofficial, vibe-coded port of Gun.js to Rust. This project is:
- NOT for production use - Do not use in production environments
- NOT actively maintained - This is an experimental port
- For testing purposes only - Use at your own risk
- No guarantees - No warranties, no support, use at your own risk
This port was created for experimentation and learning purposes. If you need a production-ready solution, please use the official Gun.js implementation.
Table of Contents
Key Crate Concepts
Graph Database Model
Gun.rs stores data as a directed graph where:
- Nodes are identified by unique souls (self-describing unique IDs)
- Properties are key-value pairs stored on nodes
- Relationships are formed through soul references
- No schema required - data is flexible and dynamic
// Example graph structure:
// {
// "user:alice": {
// "name": "Alice",
// "age": 30,
// "friend": "user:bob" // Reference to another node
// },
// "user:bob": {
// "name": "Bob",
// "age": 28
// }
// }
Chain API
The Chain API provides a fluent interface for navigating and manipulating the graph:
// Chain methods are chained together
gun.get.get.get.put;
// ^^^^ ^^^^ ^^^^ ^^^^
// Chain Chain Chain Chain
Each method returns a new Chain instance, allowing method chaining. Chains maintain context about their position in the graph hierarchy.
Souls (Node Identifiers)
Souls are deterministic, unique identifiers for nodes. They are:
- Self-describing: Generated deterministically from node content
- Globally unique: No central authority needed
- Verifiable: Can be validated by any peer
- Stable: Same data generates the same soul
// Souls look like: "abc123def456..."
// They're used to reference nodes across the network
State-Based CRDT
Gun.rs uses Conflict-free Replicated Data Types (CRDTs) with state-based conflict resolution:
- HAM (Hypothetical Amnesia Machine) Algorithm: Resolves conflicts using timestamps and logical clocks
- Last-Write-Wins: With tie-breaking based on peer IDs
- Automatic merging: Conflicting updates are automatically resolved
- Eventual consistency: All peers eventually converge to the same state
DAM Protocol (Directed Acyclic Mesh)
The DAM protocol is Gun's custom P2P networking layer:
- Message routing: Efficient message broadcasting through the mesh
- Deduplication: Prevents message loops and duplicate processing
- Peer discovery: Automatic discovery of nearby peers
- NAT traversal: Works behind firewalls with relay support
- Cryptographic security: All messages are signed with BLS signatures and verified
Offline-First Architecture
Gun.rs is designed for offline-first operation:
- Local storage: Data is persisted locally using pluggable storage backends
- Sync on connect: Automatically syncs when peers become available
- Works offline: Full functionality available without network connectivity
- Conflict resolution: Handles conflicts when sync occurs
Storage Backends
Multiple storage backends are available:
- MemoryStorage: In-memory storage (default, no persistence)
- LocalStorage: File-based storage (localStorage-like)
- SledStorage: High-performance embedded database (radisk mode)
Cryptographic Security
Gun.rs uses BLS (Boneh-Lynn-Shacham) signatures for cryptographic security:
- Message signing: All outgoing messages are signed with the secret key
- Message verification: All incoming messages are verified using public keys
- Peer authentication: Each peer maintains a mapping of peer IDs to public keys
- Tamper detection: Invalid signatures cause messages to be rejected
- Message predicates: Optional custom filtering after signature verification
Crate Usage
Installation
Add to your Cargo.toml:
[]
= { = "https://github.com/DIG-Network/gun.rs" }
= "12.2"
= "1.0"
= { = "1.0", = ["full"] }
Note: Gun.rs requires BLS (Boneh-Lynn-Shacham) key pairs for cryptographic security. All messages are signed and verified using BLS signatures.
Basic Usage
Creating a Gun Instance
use Gun;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
// Simple instance (no networking, in-memory storage)
let gun = new;
// With options (networking, storage, etc.)
use GunOptions;
let secret_key = from_seed;
let public_key = secret_key.public_key;
let gun = with_options.await?;
Reading and Writing Data
use Gun;
use ;
use json;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
let gun = new;
// Write data
gun.get.get.put.await?;
// Read data once
gun.get.get.once.await?;
// Subscribe to updates
gun.get.get.on;
Working with Objects
use json;
// Put a complete object
gun.get.get.put.await?;
// Update specific fields
gun.get.get.get.put.await?;
Relationships (Soul References)
// Create two users
gun.get.get.put.await?;
gun.get.get.put.await?;
// Create a relationship (reference)
// Note: In practice, you'd get the soul from the created node
// This is a simplified example
let alice_soul = "..."; // Soul from Alice node
gun.get.get.get.put.await?;
Arrays/Lists with Map
// Map over a collection
gun.get.map;
// Add items to a collection
for i in 1..=10
Removing Listeners
// Remove all listeners from a chain
gun.get.get.off;
// Note: off() is chain-aware and removes listeners from the current chain point
Navigation with Back
// Navigate back up the chain
let chain = gun.get.get.get;
let parent = chain.back; // Go back 1 level -> "alice" chain
let root = chain.back; // Go back to root -> gun root
Network Configuration
Connecting to Relay Servers
use ;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
// Single relay
let gun = with_options.await?;
// Multiple relays for redundancy
let secret_key2 = from_seed;
let public_key2 = secret_key2.public_key;
let gun = with_options.await?;
Running a Relay Server
use ;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
// Start a relay server on port 8765
let gun = with_options.await?;
// Server will accept connections from other peers
WebRTC Configuration
use ;
use WebRTCOptions;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
let mut webrtc_opts = default;
webrtc_opts.enabled = true;
webrtc_opts.max_connections = 10;
let mut opts = default;
opts.peers = vec!;
opts.webrtc = webrtc_opts;
let gun = with_options.await?;
Storage Configuration
Using Local Storage
use ;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
let opts = GunOptions ;
let gun = with_options.await?;
// Data will be persisted to ./gun_data/
Using Sled Storage (Radisk Mode)
use ;
use ;
// Generate BLS key pair
let secret_key = from_seed;
let public_key = secret_key.public_key;
let opts = GunOptions ;
let gun = with_options.await?;
// Uses high-performance sled database
Connection Management
// Check connection status
let is_connected = gun.is_connected.await;
let peer_count = gun.connected_peer_count.await;
// Wait for connection with timeout
let connected = gun.wait_for_connection.await; // 5 second timeout
// Graceful shutdown
gun.shutdown.await?;
Error Handling
use GunError;
match gun.get.put.await
Exhaustive Reference Guide
Core Types
Gun
The main entry point for the Gun.rs library.
Methods:
-
new(secret_key: SecretKey, public_key: PublicKey) -> Gun- Creates a new Gun instance with default settings (no networking, in-memory storage)
- Requires BLS key pair for cryptographic security
- All messages are signed with the secret key and verified with public keys
-
with_options(secret_key: SecretKey, public_key: PublicKey, options: GunOptions) -> GunResult<Gun>- Creates a Gun instance with custom options
- Requires BLS key pair for cryptographic security
- Async function - must be awaited
-
get(key: &str) -> Arc<Chain>- Returns a chain pointing to the specified key
- Entry point for navigating the graph
-
root() -> Arc<Chain>- Returns a chain pointing to the root of the graph
-
state() -> f64- Returns the current state timestamp (used for conflict resolution)
-
connected_peer_count() -> usize- Returns the number of currently connected peers
- Async function
-
is_connected() -> bool- Returns true if connected to at least one peer
- Async function
-
wait_for_connection(timeout_ms: u64) -> bool- Waits for a connection to be established
- Returns true if connected within timeout, false otherwise
- Async function
-
shutdown() -> GunResult<()>- Gracefully shuts down the Gun instance
- Stops servers and closes connections
- Async function
Chain
The fluent API for interacting with the graph. All chain methods return Arc<Chain> for method chaining.
Methods:
-
get(key: &str) -> Arc<Chain>- Navigate to a property or child node
- Returns a new chain with the key appended
-
put(data: Value) -> GunResult<Arc<Chain>>- Write data to the current chain position
- Accepts
serde_json::Value(numbers, strings, booleans, objects, arrays) - Objects are automatically expanded into the graph
- Returns error if data is invalid
- Async function
-
on<F>(callback: F) -> Arc<Chain>- Subscribe to updates at this chain position
- Callback signature:
Fn(Value, Option<String>) - Returns immediately (non-blocking)
- Returns the chain for further chaining
- Listeners persist until explicitly removed
-
once<F>(callback: F) -> GunResult<Arc<Chain>>- Execute callback once when data is available
- Callback signature:
Fn(Value, Option<String>) - Returns error if data is already available but callback fails
- Async function (waits for data)
-
map<F>(callback: F) -> Arc<Chain>- Iterate over child nodes/keys
- Callback signature:
Fn(Value, Option<String>) - Used for working with collections/arrays
- Returns immediately (non-blocking)
-
set(item: Value) -> GunResult<Arc<Chain>>- Add an item to a collection (similar to array.push)
- Generates a unique key for the item
- Returns the chain with the generated key appended
- Async function
-
back(amount: Option<usize>) -> Option<Arc<Chain>>- Navigate back up the chain
amount: Some(n)- go back n levelsamount: None- go back to root- Returns
Noneif cannot go back that far
-
off() -> Arc<Chain>- Remove all listeners from this chain position
- Does not remove listeners from parent/child chains
- Returns the chain for further operations
GunOptions
Configuration options for creating a Gun instance.
Fields:
-
peers: Vec<String>- List of WebSocket URLs to connect to (relay servers)
- Empty by default
-
localStorage: bool- Enable local storage persistence
- Default:
false
-
storage_path: Option<String>- Path for local storage
- Default:
None(uses "./gun_data" if localStorage is true)
-
radisk: bool- Use SledStorage instead of LocalStorage (high-performance mode)
- Default:
false
-
super_peer: bool- Enable relay server mode
- Default:
false
-
port: Option<u16>- Port to listen on (for relay server mode)
- Default:
None
-
webrtc: WebRTCOptions- WebRTC configuration (see
WebRTCOptionsbelow) - Default:
WebRTCOptions::default()
- WebRTC configuration (see
-
message_predicate: Option<MessagePredicate>- Optional predicate function to filter incoming messages
- Receives the entire message object and returns
trueto accept,falseto reject - Called after signature verification but before message processing
- Useful for implementing custom filtering, rate limiting, or access control
- Default:
None(all verified messages are accepted)
Methods:
-
default() -> GunOptions- Creates default options (no networking, no storage)
-
with_relay(relay_url: &str) -> GunOptions- Convenience method for single relay connection
-
with_peers(peers: Vec<String>) -> GunOptions- Convenience method for multiple peer connections
-
relay_server(port: u16) -> GunOptions- Convenience method for relay server configuration
WebRTCOptions
Configuration for WebRTC peer-to-peer connections.
Fields:
-
ice_servers: Vec<RTCIceServer>- STUN/TURN servers for NAT traversal
- Default: Google and Cloudflare STUN servers
-
data_channel: RTCDataChannelInit- Data channel configuration
- Default: unordered, max retransmits 2
-
max_connections: usize- Maximum number of WebRTC connections
- Default:
55
-
room: Option<String>- Room name for peer discovery (optional)
- Default:
None
-
enabled: bool- Enable/disable WebRTC
- Default:
true
Methods:
default() -> WebRTCOptions- Creates default WebRTC options
GunError
Error type for Gun.rs operations.
Variants:
-
GunError::InvalidData(String)- Invalid data provided (e.g., invalid type, invalid structure)
-
GunError::Storage(sled::Error)- Storage operation failed (from sled database)
-
GunError::Serialization(serde_json::Error)- JSON serialization/deserialization failed
-
GunError::Network(String)- Network operation failed (connection lost, timeout, etc.)
-
GunError::InvalidSoul(String)- Invalid soul (node ID) format
-
GunError::NodeNotFound- Requested node doesn't exist in the graph
-
GunError::Io(std::io::Error)- I/O operation failed (file read/write, etc.)
-
GunError::UrlParseError(url::ParseError)- URL parsing failed (invalid peer URL)
-
GunError::WebRTC(String)- WebRTC operation failed (connection, signaling, etc.)
-
GunError::Crypto(String)- Cryptographic operation failed (encryption, signing, etc.)
Module Reference
gun::chain
Chain- Main chain API type
gun::core
GunCore- Core graph database engine (internal)
gun::dam
Mesh- DAM protocol mesh networking (internal)
gun::error
GunError- Error typesGunResult<T>- Result type alias:Result<T, GunError>
gun::graph
- Graph data structures (internal)
gun::storage
Storage- Storage traitMemoryStorage- In-memory storageLocalStorage- File-based storageSledStorage- Sled database storage
gun::webrtc
WebRTCOptions- WebRTC configurationWebRTCManager- WebRTC manager (internal)WebRTCPeer- WebRTC peer connection (internal)
gun::websocket
- WebSocket client and server (internal)
gun::sea
- Security, Encryption, Authorization module (partial implementation)
gun::types
MessagePredicate- Message filtering predicate type for custom message filtering
Type Aliases
GunResult<T>=Result<T, GunError>MessagePredicate=Arc<dyn Fn(&serde_json::Value) -> bool + Send + Sync>- Function type for custom message filtering
Constants
None currently exported.
Examples
Basic Example
use Gun;
use ;
use json;
async
Real-time Sync Example
use ;
use ;
use json;
async
WebRTC Example
See examples/two_clients_webrtc.rs for a complete WebRTC example demonstrating:
- Two clients with WebRTC enabled
- Direct peer-to-peer communication
- NAT traversal
Relay Server Example
See examples/two_clients.rs for a complete example demonstrating:
- Two clients connecting via relay
- Data synchronization
- Real-time updates
Run examples:
Additional Resources
Protocol Documentation
- DAM Protocol: See NETWORKING.md
- Relay Servers: See RELAY_SERVERS.md
- WebRTC Status: See WEBRTC_STATUS.md
Testing
See tests/ directory for comprehensive test suites:
- Unit tests
- Integration tests
- WebRTC tests
- Lock contention tests
- Stress tests
Architecture Notes
Important: Gun.js uses a custom DAM (Directed Acyclic Mesh) protocol over WebSocket, NOT libp2p. For 1:1 behavioral compatibility, we use:
tokio-tungstenitefor WebSocket transport (matches Gun.js)- Custom DAM protocol implementation (matches mesh.js)
- Message deduplication (matches dup.js)
License
MIT OR Apache-2.0 OR Zlib (same as Gun.js)