### Local Test Network Setup: Start Coordinators Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Sets up a local test network by starting multiple Saorsa coordinator nodes. This example demonstrates launching two coordinators on different ports with distinct roles and publish intervals, facilitating testing of multi-node interactions. Uses `saorsa-coordinator` binary. ```bash # Terminal 1 - Start Coordinator: saorsa-coordinator --verbose --bind 127.0.0.1:7000 --roles coordinator,reflector --publish-interval 10 # Terminal 2 - Start Second Coordinator: saorsa-coordinator --verbose --bind 127.0.0.1:7001 --roles coordinator,relay --publish-interval 15 \ --identity-path ~/.saorsa-gossip/coordinator2.identity ``` -------------------------------- ### Start Saorsa Gossip Coordinator Node Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Initiates a coordinator node for the Saorsa Gossip network. It binds to a specified address, assigns roles, and sets the interval for publishing adverts. Verbose logging can be enabled for detailed output. Dependencies include the `saorsa-gossip-coordinator` binary. ```bash saorsa-gossip-coordinator \ --verbose \ --bind 0.0.0.0:7000 \ --roles coordinator,reflector,relay \ --publish-interval 60 ``` -------------------------------- ### Rust: Basic Saorsa Gossip Library Usage Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Example demonstrating basic library usage in Rust for Saorsa Gossip. It covers initializing membership and pub/sub layers, subscribing to a topic, publishing a message, and receiving messages. Requires tokio runtime. ```rust use saorsa_gossip_types::{TopicId, PeerId}; use saorsa_gossip_membership::{Membership, HyParViewMembership}; use saorsa_gossip_pubsub::{PubSub, PlumtreePubSub}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create a topic for your group let topic = TopicId::new([1u8; 32]); // Initialize membership layer let membership = HyParViewMembership::default(); membership.join(vec!["127.0.0.1:8080".to_string()]).await?; // Initialize pub/sub let pubsub = PlumtreePubSub::new(); let mut rx = pubsub.subscribe(topic); // Publish a message pubsub.publish(topic, bytes::Bytes::from("Hello, gossip!")).await?; // Receive messages while let Some((peer_id, data)) = rx.recv().await { println!("Received from {}: {:?}", peer_id, data); } Ok(()) } ``` -------------------------------- ### Local Test Network Setup: Create Test Identities Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Creates multiple test identities required for a local Saorsa Gossip test network using the `saorsa-gossip` CLI. This step ensures that each node in the test network has a unique identity for communication and authentication. ```bash # Create 3 test node identities saorsa-gossip identity create --alias Node1 saorsa-gossip identity create --alias Node2 saorsa-gossip identity create --alias Node3 # Verify they were created saorsa-gossip identity list ``` -------------------------------- ### Migrate MlsGroup Initialization from 0.2.0 to 0.3.0 Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md Demonstrates the API changes required when migrating from saorsa-mls version 0.2.0 to 0.3.0. It shows the older default initialization and the new required setup involving GroupConfig and MemberIdentity. ```rust // Old (0.2.0) let group = MlsGroup::default(); // New (0.3.0) let config = GroupConfig::default(); let identity = MemberIdentity::generate(MemberId::generate())?; let group = MlsGroup::new(config, identity).await?; ``` -------------------------------- ### Saorsa Gossip CLI: Presence Beacons Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Manages presence information within the Saorsa Gossip network via the CLI. It allows users to start broadcasting their presence on specified topics, check which peers are currently online for a topic, and stop broadcasting presence. Useful for real-time awareness. ```bash # Start broadcasting presence saorsa-gossip presence start --topic general # Check who's online saorsa-gossip presence online --topic general # Stop broadcasting saorsa-gossip presence stop --topic general ``` -------------------------------- ### Format Code with Cargo Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Formats all Rust code within the project using the 'cargo fmt' command. This ensures consistent code style across the codebase. It requires Cargo to be installed and configured for a Rust project. ```bash # Format code cargo fmt --all ``` -------------------------------- ### Implement QUIC Transport with NAT Traversal in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Details the implementation of a production-ready QUIC transport layer using ant-quic, featuring post-quantum TLS 1.3, hole-punching NAT traversal, and stream multiplexing. The example shows configuration, transport creation with an ML-DSA identity, listening for connections, dialing peers, sending messages on a PubSub stream, and asynchronously receiving messages. Requires `tokio` for async operations. ```rust use saorsa_gossip_transport::{ AntQuicTransport, AntQuicTransportConfig, StreamType, GossipTransport }; use saorsa_gossip_types::PeerId; use bytes::Bytes; use std::sync::Arc; // Configure transport let config = AntQuicTransportConfig { bind_addr: "0.0.0.0:8080".parse().unwrap(), enable_nat_traversal: true, max_idle_timeout_ms: 30_000, keep_alive_interval_ms: 10_000, }; // Create transport with ML-DSA identity use saorsa_gossip_identity::MlDsaKeyPair; let keypair = MlDsaKeyPair::generate().expect("keypair"); let peer_id = keypair.peer_id(); let transport = Arc::new(AntQuicTransport::new(config, keypair) .await .expect("Failed to create transport")); // Listen for incoming connections transport.listen("0.0.0.0:8080".parse().unwrap()) .await .expect("Failed to listen"); // Dial a peer let remote_peer = PeerId::new([2u8; 32]); let remote_addr = "192.168.1.100:8080".parse().unwrap(); transport.dial(remote_peer, remote_addr) .await .expect("Failed to dial peer"); // Send message on PubSub stream let message = Bytes::from("Hello, peer!"); transport.send_to_peer(remote_peer, StreamType::PubSub, message) .await .expect("Failed to send"); // Receive messages (async loop) tokio::spawn(async move { loop { match transport.receive_message().await { Ok((peer, stream_type, data)) => { println!("Received from {}: {:?}", peer, data); } Err(e) => { eprintln!("Receive error: {}", e); break; } } } }); ``` -------------------------------- ### Create and Manage MLS Group (Rust) Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md Provides code examples for essential MLS group operations including creating a new group, adding members, removing members, and encrypting/decrypting messages. It utilizes the MlsGroup API for these functionalities. ```rust use saorsa_mls::{MlsGroup, GroupConfig, MemberIdentity, MemberId}; // Create MLS group let config = GroupConfig::default(); let creator = MemberIdentity::generate(MemberId::generate())?; let mut group = MlsGroup::new(config, creator).await?; // Add Member let new_member = MemberIdentity::generate(MemberId::generate())?; let welcome = group.add_member(&new_member).await?; // Send welcome message to new member via QUIC // New member processes welcome to join group // Remove Member let member_id = MemberId::from_bytes(&[...])?; group.remove_member(&member_id).await?; // Encrypt message to group let plaintext = b"Hello, group!"; let ciphertext = group.encrypt_message(plaintext)?; // Decrypt received message let decrypted = group.decrypt_message(&ciphertext)?; assert_eq!(plaintext, &decrypted[..]); ``` -------------------------------- ### Lint Code with Clippy Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Lints the Rust code using 'cargo clippy' with all features and targets enabled, enforcing zero warnings. This helps catch potential errors and improve code quality. It requires the Clippy linter to be installed. ```bash # Lint with Clippy (zero warnings enforced) cargo clippy --all-features --all-targets -- -D warnings ``` -------------------------------- ### Delta-CRDTs Example in JSON Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This example demonstrates how Delta-CRDTs are used to efficiently transfer only the changes (deltas) in data since the last synchronization, rather than the entire state. It includes a payload for 'adds' and a 'version' vector to track synchronization progress. This reduces bandwidth significantly, especially for large datasets. ```json { "delta": { "adds": [ {"id": "peer1", "name": "Charlie", ...}, {"id": "peer2", "name": "Diana", ...}, ... ] }, "version": {"alice": 42, "bob": 30} } ``` -------------------------------- ### Bash: Build and Test Saorsa Gossip Project Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Commands for building the Saorsa Gossip project and running its tests. Includes options for building in release mode, running all tests, and building with all features enabled. ```bash # Build all crates cargo build --release # Run tests cargo test --all # Run with all features cargo build --all-features ``` -------------------------------- ### Create and Sign Coordinator Advert using ML-DSA-65 Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Demonstrates the creation, signing, verification, serialization, and deserialization of a CoordinatorAdvert. This advert is used for seedless bootstrap and includes roles, NAT classification, and address hints. It utilizes ML-DSA-65 for signing and CBOR for serialization. Dependencies include `saorsa-gossip-coordinator`, `saorsa-gossip-types`, and `saorsa-pqc`. ```rust use saorsa_gossip_coordinator::{ CoordinatorAdvert, CoordinatorRoles, NatClass, AddrHint }; use saorsa_gossip_types::PeerId; use saorsa_pqc::{MlDsa65, MlDsaOperations}; // Create coordinator advert let peer = PeerId::new([1u8; 32]); let roles = CoordinatorRoles { coordinator: true, reflector: true, rendezvous: false, relay: false, }; let addr_hints = vec![ AddrHint::new("192.168.1.100:7000".parse().unwrap()), AddrHint::new("203.0.113.50:7000".parse().unwrap()), ]; let mut advert = CoordinatorAdvert::new( peer, roles, addr_hints, NatClass::Eim, 3_600_000 // 1 hour validity ); // Sign with ML-DSA-65 let signer = MlDsa65::new(); let (pk, sk) = signer.generate_keypair() .expect("Failed to generate keypair"); advert.sign(&sk) .expect("Failed to sign advert"); // Verify signature let valid = advert.verify(&pk) .expect("Verification failed"); assert!(valid, "Signature must be valid"); // Check validity (time-based) assert!(advert.is_valid(), "Advert must be valid"); // Serialize to CBOR (RFC 8949) let cbor_bytes = advert.to_cbor() .expect("Failed to serialize"); println!("Advert size: {} bytes", cbor_bytes.len()); // Deserialize from CBOR let decoded = CoordinatorAdvert::from_cbor(&cbor_bytes) .expect("Failed to deserialize"); assert_eq!(decoded.peer, peer); // Transport-ready bytes let transport_bytes = advert.to_bytes() .expect("to_bytes"); let received = CoordinatorAdvert::from_bytes(&transport_bytes) .expect("from_bytes"); assert_eq!(received.roles.coordinator, true); ``` -------------------------------- ### HyParViewMembership: Initialize, Join, and Manage Peers with SWIM Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Demonstrates initializing HyParViewMembership, joining a network using seed peers, and managing active/passive peer views. It also shows how to interact with the SWIM failure detector to mark peers as alive and retrieve their states. ```rust use saorsa_gossip_membership::{ HyParViewMembership, Membership, SwimDetector, PeerState }; use saorsa_gossip_transport::{QuicTransport, TransportConfig}; use saorsa_gossip_types::PeerId; use std::sync::Arc; // Create transport and membership let transport = Arc::new(QuicTransport::new(TransportConfig::default())); let membership = HyParViewMembership::new( 8, // active degree (8-12) 64, // passive degree (64-128) transport ); // Join network via seed peers let seeds = vec![ "coordinator1.example.com:7000".to_string(), "coordinator2.example.com:7000".to_string(), ]; membership.join(seeds).await .expect("Failed to join network"); // Add peer to active view let peer = PeerId::new([1u8; 32]); membership.add_active(peer).await .expect("Failed to add peer"); // Check active/passive views let active = membership.active_view(); println!("Active peers: {}", active.len()); let passive = membership.passive_view(); println!("Passive peers: {}", passive.len()); // SWIM failure detection let swim = membership.swim(); swim.mark_alive(peer).await; // Check peer state let state = swim.get_state(&peer).await; assert_eq!(state, Some(PeerState::Alive)); // Get all alive peers let alive_peers = swim.get_peers_in_state(PeerState::Alive).await; println!("Alive peers: {:?}", alive_peers); // Promote peer from passive to active membership.promote(peer).await .expect("Failed to promote"); // Remove dead peer membership.remove_active(peer).await .expect("Failed to remove"); ``` -------------------------------- ### Bash: Execute Saorsa Gossip Integration and Performance Tests Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Commands for running specific types of tests for the Saorsa Gossip project, including integration tests, performance benchmarks, and generating a code coverage report. ```bash # Unit tests cargo test --all # Integration tests cargo test --test integration_tests # Performance benchmarks cargo bench --bench performance # Code coverage report ./scripts/coverage.sh ``` -------------------------------- ### Set Up Pub/Sub Messaging Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Configures the PlumtreePubSub module for efficient message dissemination within the network. This enables publish-subscribe functionality, allowing nodes to broadcast messages to interested subscribers. It depends on an initialized membership module. ```Rust use saorsa::pubsub::plumtree::PlumtreePubSub; use saorsa::pubsub::PubSub; use saorsa::membership::Membership; // Assuming 'membership' is an initialized instance of Membership (e.g., HyParViewMembership) // let membership = ...; let pubsub = PlumtreePubSub::new(membership).await.unwrap(); println!("PubSub module initialized."); ``` -------------------------------- ### Rust: Saorsa Gossip Startup Peer Discovery Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md Illustrates the initial peer discovery process on Saorsa Gossip startup. It involves reading a local peer cache, sorting peers by score and success timestamp, and attempting connections to the top-ranked peers to join the overlay network. ```rust // On startup: // 1. Read peer cache from disk (~/.saorsa-gossip/peers.db) // 2. Sort by score and last_success // 3. Attempt connection to top 3-5 peers // 4. If any connect, join overlay via membership protocol ``` -------------------------------- ### Generate Documentation with Cargo Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Generates Rust documentation for all features of the project, excluding dependencies, and opens it in a web browser. This command is essential for understanding the project's API and structure. It requires Cargo and a web browser. ```bash # Generate documentation cargo doc --all-features --no-deps --open ``` -------------------------------- ### PlumtreePubSub: Subscribe, Publish, and Handle Messages with ML-DSA Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Illustrates setting up PlumtreePubSub for epidemic broadcast, including subscribing to topics, publishing signed messages using ML-DSA, and handling incoming messages like EAGER. It also covers initializing topic peers and unsubscribing. ```rust use saorsa_gossip_pubsub::{PlumtreePubSub, PubSub}; use saorsa_gossip_transport::{QuicTransport, TransportConfig}; use saorsa_gossip_identity::MlDsaKeyPair; use saorsa_gossip_types::{TopicId, PeerId}; use bytes::Bytes; use std::sync::Arc; // Create transport and signing key let keypair = MlDsaKeyPair::generate().expect("keypair"); let peer_id = keypair.peer_id(); let transport = Arc::new(QuicTransport::new(TransportConfig::default())); // Create pub/sub with ML-DSA signing let pubsub = PlumtreePubSub::new(peer_id, transport, keypair); // Subscribe to topic let topic = TopicId::from_entity("channel-general") .expect("topic"); let mut rx = pubsub.subscribe(topic); // Publish message (signed with ML-DSA) let message = Bytes::from("Hello, gossip!"); pubsub.publish(topic, message.clone()).await .expect("Failed to publish"); // Receive messages tokio::spawn(async move { while let Some((sender, data)) = rx.recv().await { println!("From {}: {:?}", sender, data); } }); // Initialize topic with known peers let peers = vec![ PeerId::new([1u8; 32]), PeerId::new([2u8; 32]), PeerId::new([3u8; 32]), ]; pubsub.initialize_topic_peers(topic, peers).await; // Handle incoming EAGER message use saorsa_gossip_pubsub::GossipMessage; use saorsa_gossip_types::{MessageHeader, MessageKind}; let header = MessageHeader::new(topic, MessageKind::Eager, 10); let signature = keypair.sign(&bincode::serialize(&header).unwrap()) .expect("sign"); let msg = GossipMessage { header, payload: Some(message), signature, public_key: keypair.public_key().to_vec(), }; let from_peer = PeerId::new([4u8; 32]); pubsub.handle_eager(from_peer, topic, msg).await .expect("Failed to handle EAGER"); // Unsubscribe pubsub.unsubscribe(topic).await .expect("Failed to unsubscribe"); ``` -------------------------------- ### Implement FoafQuery for DHT-Free Discovery in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Illustrates how to create and manage `FoafQuery` messages for Friends-of-Friends discovery. This includes generating unique query IDs, setting search parameters like `target_four_words`, `max_hops`, and handling visited peers for cycle detection. It also shows how to construct a `FoafResponse` upon finding a peer. ```rust use saorsa_gossip_types::{FoafQuery, FoafResponse, PeerId}; use rand::Rng; // Create FOAF query let mut query_id = [0u8; 16]; rand::thread_rng().fill(&mut query_id); let originator = PeerId::new([1u8; 32]); let query = FoafQuery { query_id, target_four_words: "river-mountain-cloud-light".to_string(), hop: 0, max_hops: 2, // Typical: 2-3 hops visited: vec![originator], originator, }; // Forward query (increment hop) let mut forwarded = query.clone(); forwarded.hop += 1; forwarded.visited.push(PeerId::new([2u8; 32])); // Check if should forward if forwarded.hop < forwarded.max_hops { println!("Forwarding to next hop"); } else { println!("Max hops reached"); } // Cycle detection let next_peer = PeerId::new([1u8; 32]); if forwarded.visited.contains(&next_peer) { println!("Cycle detected, dropping"); } else { forwarded.visited.push(next_peer); } // Create response when found let response = FoafResponse { query_id, peer_id: PeerId::new([5u8; 32]), addr_hints: vec!["192.168.1.200:8080".to_string()], hops: forwarded.hop, }; println!("Found peer after {} hops", response.hops); println!("Address hints: {:?}", response.addr_hints); ``` -------------------------------- ### Create and Validate PresenceRecord Beacons in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Shows how to construct a `PresenceRecord` for encrypted presence beacons, including address hints and Time-To-Live (TTL). It also demonstrates how to create records with optional four-word identifiers for FOAF discovery and check their expiration status. ```rust use saorsa_gossip_types::PresenceRecord; // Create presence record let presence_tag = [42u8; 32]; let addr_hints = vec![ "192.168.1.100:8080".to_string(), "203.0.113.25:9000".to_string(), ]; let ttl_seconds = 900; // 15 minutes let record = PresenceRecord::new(presence_tag, addr_hints, ttl_seconds); println!("Since: {}", record.since); println!("Expires: {}", record.expires); println!("Address hints: {:?}", record.addr_hints); // With four-word identity for FOAF let record_foaf = PresenceRecord::with_four_words( presence_tag, addr_hints, ttl_seconds, "ocean-forest-moon-star".to_string() ); assert!(record_foaf.four_words.is_some()); println!("Four-word ID: {:?}", record_foaf.four_words); // Check expiration let expired = record.is_expired(); if expired { println!("Beacon expired"); } else { println!("Beacon still valid"); } // Wait for expiration tokio::time::sleep(tokio::time::Duration::from_secs(901)).await; assert!(record.is_expired(), "Should be expired"); ``` -------------------------------- ### Publish saorsa-mls 0.3.0 to Crates.io Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md A bash script sequence for publishing the saorsa-mls crate. It includes adding all changes, committing, performing a dry run of the publish, and finally executing the actual publish command. ```bash cd ../saorsa-mls git add -A git commit -m "chore: prepare 0.3.0 release" cargo publish --dry-run cargo publish ``` -------------------------------- ### Publisher Flow for Site Content Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This outlines the publisher's process for preparing and distributing a site. It includes chunking files, hashing blocks to generate CIDs, building and signing a manifest, and gossiping provider summaries. ```text 1. Prepare site directory 2. Chunk large files (e.g., 256 KB blocks) 3. Hash each block → CID 4. Build manifest (routes, asset list) 5. Sign manifest (ML-DSA) 6. Gossip Provider Summary to SITE_ADVERT: 7. Serve manifest and blocks on demand via SITE_SYNC: ``` -------------------------------- ### Create and Manage MessageHeader in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Shows how to create and manage `MessageHeader`, the wire format header for gossip messages. This includes setting version, topic, message kind, hop count, and TTL, calculating unique message IDs using BLAKE3, and preparing for serialization. ```rust use saorsa_gossip_types::{MessageHeader, MessageKind, TopicId, PeerId}; // Create a new message header let topic = TopicId::new([1u8; 32]); let header = MessageHeader::new(topic, MessageKind::Eager, 10); // Calculate unique message ID (BLAKE3-based) let epoch = 12345u64; let signer = PeerId::new([2u8; 32]); let payload_hash = [3u8; 32]; let msg_id = MessageHeader::calculate_msg_id( &topic, epoch, &signer, &payload_hash ); // Create header with calculated ID let mut header = MessageHeader { version: 1, topic, msg_id, kind: MessageKind::Eager, hop: 0, ttl: 10, }; // Forward message (increment hop, decrement TTL) header.increment_hop().expect("Hop overflow"); header.decrement_ttl().expect("TTL expired"); // Serialize for signing let header_bytes = bincode::serialize(&header) .expect("Failed to serialize"); ``` -------------------------------- ### Saorsa Gossip CLI: Network Operations Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Handles network interactions for the Saorsa Gossip CLI. This includes joining the gossip network by connecting to a coordinator, checking the current network status, and listing all known peers in the network. Requires a running coordinator and a valid identity. ```bash # Join the gossip network via coordinator saorsa-gossip network join \ --coordinator 127.0.0.1:7000 \ --identity Alice \ --bind 0.0.0.0:0 # Show network status saorsa-gossip network status # List known peers saorsa-gossip network peers ``` -------------------------------- ### Saorsa Gossip CLI: PubSub Messaging Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Enables publish-subscribe messaging within the Saorsa Gossip network using the CLI. Users can subscribe to topics, publish messages to specific topics, and list active subscriptions. This functionality allows for decentralized communication. ```bash # Subscribe to a topic saorsa-gossip pubsub subscribe --topic news # Publish a message saorsa-gossip pubsub publish --topic news --message "Hello, gossip!" # List subscriptions saorsa-gossip pubsub list ``` -------------------------------- ### Client Fetching Flow for Site Manifest Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This sequence outlines the client-side steps to fetch a site manifest using the Saorsa Gossip protocol. It involves subscribing to provider summaries, scoring providers, and requesting the manifest via a specific stream. ```text 1. Resolve SID 2. Subscribe to SITE_ADVERT: 3. Receive Provider Summaries 4. Score providers 5. Connect via QUIC 6. Fetch manifest via GET_MANIFEST on SITE_SYNC stream 7. Verify ML-DSA signature 8. Reconcile block set (IBLT) 9. Fetch missing blocks 10. Verify each block CID 11. Render site ``` -------------------------------- ### Rust: Saorsa Gossip Dependencies in Cargo.toml Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Lists the necessary dependencies to add to a Rust project's `Cargo.toml` file for using the Saorsa Gossip libraries. This includes various modules like types, identity, transport, membership, and pubsub. ```toml [dependencies] saorsa-gossip-types = "0.1.3" saorsa-gossip-identity = "0.1.3" saorsa-gossip-transport = "0.1.3" saorsa-gossip-membership = "0.1.3" saorsa-gossip-pubsub = "0.1.3" saorsa-gossip-coordinator = "0.1.3" saorsa-gossip-rendezvous = "0.1.3" saorsa-gossip-groups = "0.1.3" saorsa-gossip-presence = "0.1.3" saorsa-gossip-crdt-sync = "0.1.3" ``` -------------------------------- ### Enable Verbose Logging for Saorsa Coordinator Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Activates detailed DEBUG logging for the Saorsa coordinator binary. This is useful for diagnosing issues and understanding the internal operations of the coordinator node during runtime. The `--verbose` flag controls this setting. ```bash saorsa-coordinator --verbose ... ``` -------------------------------- ### Initialize QUIC Transport with NAT Traversal Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Initializes the AntQuicTransport for establishing network connections using QUIC protocol. This transport layer handles NAT traversal, enabling peer-to-peer communication in diverse network environments. It requires configuration for binding to a local address. ```Rust use saorsa::transport::ant_quic::AntQuicTransport; use saorsa::transport::Transport; use std::net::SocketAddr; let bind_addr: SocketAddr = "0.0.0.0:0".parse().unwrap(); // Listen on all interfaces, OS assigns port let transport = AntQuicTransport::new(bind_addr).await.unwrap(); println!("Transport initialized on {:?}", transport.local_addr()); ``` -------------------------------- ### Initialize Peer Discovery Membership Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Sets up the HyParViewMembership module for decentralized peer discovery. This component manages the membership list and facilitates the exchange of peer information among nodes in the network. It requires an initialized transport layer. ```Rust use saorsa::membership::hyparview::HyParViewMembership; use saorsa::membership::Membership; use saorsa::transport::Transport; // Assuming 'transport' is an initialized instance of Transport (e.g., AntQuicTransport) // let transport = ...; let membership = HyParViewMembership::new(transport).await.unwrap(); println!("Membership module initialized."); ``` -------------------------------- ### Rust: Create Private Album with Encryption Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This Rust code demonstrates how to create a private album within the Saorsa framework. It involves encrypting photo blocks using an MLS group key, building a Saorsa Site with metadata, and publishing provider summaries to a gossip network. Dependencies include `Arc`, `GossipClient`, `SaorsaSiteClient`, `HashMap`, `GroupId`, `MlsGroup`, `SiteBuilder`, `chacha20poly1305`, and file system operations. ```rust struct AlbumManager { gossip: Arc, sites: SaorsaSiteClient, mls_groups: HashMap, } impl AlbumManager { async fn create_private_album( &self, name: &str, photos: Vec, group_id: &GroupId, ) -> Result { // 1. Get MLS group key let mls_group = self.mls_groups.get(group_id)?; let encryption_key = mls_group.exporter_secret("site-encryption")?; // 2. Build site let mut site_builder = SiteBuilder::new(); site_builder.set_title(name); for photo_path in photos { let content = fs::read(&photo_path).await?; let encrypted = chacha20poly1305::encrypt(&encryption_key, &content)?; site_builder.add_file( photo_path.file_name().unwrap().to_str().unwrap(), encrypted, "image/jpeg", ); } // 3. Add MLS group metadata site_builder.set_private_group(*group_id); // 4. Sign and publish let site = site_builder.build()?; let sid = site.id(); self.sites.publish(site).await?; // 5. Gossip Provider Summary let summary = ProviderSummary { target: sid, provider: self.gossip.identity().peer_id(), capabilities: vec![Capability::Site], manifest_version: site.version(), // ... }; self.gossip.publish_to_shard(rendezvous_shard(&sid), summary).await?; Ok(sid) } } ``` -------------------------------- ### Manage Identity with Alias and Keystore Persistence in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Shows how to manage a node's identity using the saorsa-gossip-identity crate, including human-readable aliases and keystore persistence. It supports Communitas four-word identifiers for user-friendly discovery and demonstrates loading or creating identities, signing messages, and verifying signatures using public keys. Asynchronous operations are involved for loading/creating. ```rust use saorsa_gossip_identity::Identity; use tempfile::TempDir; // Create new identity with alias let alice = Identity::new("Alice".to_string()) .expect("Failed to create identity"); println!("Alice PeerId: {}", alice.peer_id()); // Load or create identity (Communitas integration) let temp_dir = TempDir::new().expect("temp dir"); let keystore = temp_dir.path().to_str().expect("path"); let bob = Identity::load_or_create( "ocean-forest-moon-star", // four-word identifier "Bob", // display name keystore ).await .expect("Failed to load/create identity"); // Reloading returns same identity let bob2 = Identity::load_or_create( "ocean-forest-moon-star", "Bob", keystore ).await .expect("Failed to load"); assert_eq!(bob.peer_id(), bob2.peer_id(), "Same identity"); // Sign messages with identity let message = b"Authenticate me"; let signature = bob.key_pair().sign(message) .expect("Failed to sign"); // Verify with public key let valid = Identity::verify_signature( bob.key_pair().public_key(), message, &signature ); assert!(valid); ``` -------------------------------- ### Manage saorsa-mls Dependency Locally Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md This snippet shows how to manage the saorsa-mls dependency by pointing to a local path in Cargo.toml. This is useful for development and testing before publishing to crates.io. ```toml saorsa-mls = { version = "0.3.0", path = "../saorsa-mls" } ``` -------------------------------- ### Generate ML-DSA Keypair and Sign Messages in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Demonstrates generating an ML-DSA-65 keypair for post-quantum digital signatures using the saorsa-gossip-identity crate. It covers key generation, signing messages, verifying signatures, and serialization/deserialization of keys. This functionality provides approximately 128-bit security. ```rust use saorsa_gossip_identity::MlDsaKeyPair; use saorsa_gossip_types::PeerId; // Generate new keypair (production-ready) let keypair = MlDsaKeyPair::generate() .expect("Failed to generate keypair"); // Get PeerId from public key let peer_id = keypair.peer_id(); println!("PeerId: {}", peer_id); // Sign a message let message = b"Hello, gossip network!"; let signature = keypair.sign(message) .expect("Failed to sign"); println!("Signature: {} bytes", signature.len()); // Verify signature let valid = MlDsaKeyPair::verify( keypair.public_key(), message, &signature ).expect("Verification failed"); assert!(valid, "Signature must be valid"); // Tampered message fails verification let mut bad_message = message.to_vec(); bad_message[0] ^= 0xFF; let invalid = MlDsaKeyPair::verify( keypair.public_key(), &bad_message, &signature ).expect("Verification failed"); assert!(!invalid, "Tampered signature must not verify"); // Serialize for storage let bytes = keypair.to_bytes() .expect("Failed to serialize"); let loaded = MlDsaKeyPair::from_bytes(&bytes) .expect("Failed to deserialize"); assert_eq!(loaded.peer_id(), peer_id); ``` -------------------------------- ### Saorsa-MLS 0.3.0 Dependencies Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md Lists the direct dependencies for saorsa-mls version 0.3.0, including cryptographic primitives (saorsa-pqc), transport layer (ant-quic), and an AEAD cipher (chacha20poly1305). ```toml saorsa-pqc = "0.3.14" # ML-KEM, ML-DSA, ChaCha20-Poly1305 ant-quic = "0.8.13" # QUIC transport (note: different version than gossip) chacha20poly1305 = "0.10" # AEAD cipher ``` -------------------------------- ### Create and Use PeerId from ML-DSA Public Key in Rust Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Illustrates creating and using `PeerId`, a 32-byte identifier derived from the ML-DSA-65 public key using BLAKE3 hashing. It covers creation from raw bytes, derivation from key pairs, verifying determinism, and usage in HashMaps. ```rust use saorsa_gossip_types::PeerId; // Create from raw bytes let peer = PeerId::new([42u8; 32]); // Derive from ML-DSA public key (production usage) use saorsa_gossip_identity::MlDsaKeyPair; let keypair = MlDsaKeyPair::generate() .expect("Failed to generate keypair"); let peer_id = keypair.peer_id(); // Verify derivation is deterministic let peer1 = PeerId::from_pubkey(keypair.public_key()); let peer2 = PeerId::from_pubkey(keypair.public_key()); assert_eq!(peer1, peer2); // Use as HashMap keys (implements Hash, Eq) use std::collections::HashMap; let mut peers: HashMap = HashMap::new(); peers.insert(peer_id, "Alice".to_string()); ``` -------------------------------- ### CLI: Create Saorsa Gossip Identity Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Command to create a new ML-DSA identity with a specified alias using the Saorsa Gossip CLI tool. This is a fundamental step for participating in the gossip network. ```bash saorsa-gossip --verbose identity create --alias Test ``` -------------------------------- ### Saorsa Gossip CLI: Identity Management Source: https://github.com/dirvine/saorsa-gossip/blob/main/README.md Manages cryptographic identities for the Saorsa Gossip network using the `saorsa-gossip` CLI. Operations include creating new identities with aliases, listing all stored identities, displaying details of a specific identity, and deleting identities from the keystore. Identities are stored locally. ```bash # Create a new ML-DSA identity saorsa-gossip identity create --alias Alice # List all identities in keystore saorsa-gossip identity list # Show identity details saorsa-gossip identity show Alice # Delete an identity saorsa-gossip identity delete Alice ``` -------------------------------- ### Derive Site Encryption Key and Encrypt/Decrypt (Rust) Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md Details the process of deriving a ChaCha20-Poly1305 encryption key for private site content using the MLS exporter. It then demonstrates encrypting and decrypting site block data using the derived key and the saorsa-pqc library. ```rust // Use MLS exporter to derive ChaCha20-Poly1305 key for site blocks let site_id = b"site-abc123"; let exporter_label = "saorsa-site-encryption"; let context = site_id; let encryption_key = group.export_secret(exporter_label, context, 32)?; // Create ChaCha20-Poly1305 cipher use saorsa_pqc::{SymmetricKey, ChaCha20Poly1305Cipher}; let key = SymmetricKey::from_bytes(encryption_key)?; let cipher = ChaCha20Poly1305Cipher::new(&key); // Encrypt site block let block_data = b"..."; let (ciphertext, nonce) = cipher.encrypt(block_data, None)?; // Decrypt site block (recipient) let decrypted = cipher.decrypt(&ciphertext, &nonce, None)?; ``` -------------------------------- ### Content Addressing with BLAKE3 Hashing Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This snippet illustrates the concept of content addressing where a Content Identifier (CID) is derived from the BLAKE3 hash of a data chunk. This ensures data integrity and deduplication. ```text CID = BLAKE3(chunk_data)[0..32] ``` -------------------------------- ### Finding Users via FOAF Query Propagation Source: https://github.com/dirvine/saorsa-gossip/blob/main/DESIGN.md This describes the 'Find User' mechanism for scenarios where no shared MLS group exists but a social connection (FOAF) is present. It details the query propagation, reply mechanism, and abuse prevention measures. ```text Scenario: No Shared Group (FOAF Query) 1. Send FIND_USER(target_peer_id) to active peers. 2. Propagate via FOAF: - Peer checks for shared group with target. - If yes, reply with encrypted addr_hints. - If no, forward to 3 random friends (fanout=3). - TTL=3 hops max. 3. Receive replies encrypted to requester. 4. Connect using received addr_hints. Abuse Prevention: - Capability Tokens - Rate Limiting (10 FIND_USER/hour/peer) - Proximity Check (2-hop social distance) - Scoring Penalty ``` -------------------------------- ### Manage MLS Group and Derive Presence Secrets Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Illustrates the management of MLS groups using `GroupContext`, including creation, epoch advancement, and derivation of presence beaconing secrets. Secrets are derived from MLS exporter using BLAKE3 KDF and are time-slice-based, ensuring deterministic and rotating tags. Dependencies include `saorsa-gossip-groups` and `saorsa-gossip-types`. ```rust use saorsa_gossip_groups::GroupContext; use saorsa_gossip_types::{TopicId, PeerId}; // Create group context let topic = TopicId::from_entity("project-alpha") .expect("topic"); let mut group = GroupContext::new(topic); println!("Group topic: {:?}", group.topic_id); println!("Epoch: {}", group.epoch); // Create from entity identifier let group2 = GroupContext::from_entity("channel-general") .expect("Failed to create group"); // Advance epoch (key rotation) group.next_epoch(); assert_eq!(group.epoch, 1); // Derive presence secret (BLAKE3 KDF) let exporter_secret = [1u8; 32]; // From MLS exporter let user_id = PeerId::new([2u8; 32]); let time_slice = 12345u64; // Hour since epoch let presence_tag = GroupContext::derive_presence_secret( &exporter_secret, user_id.as_bytes(), time_slice ); // Tags rotate every hour let tag_hour1 = GroupContext::derive_presence_secret( &exporter_secret, user_id.as_bytes(), 1000 ); let tag_hour2 = GroupContext::derive_presence_secret( &exporter_secret, user_id.as_bytes(), 1001 ); assert_ne!(tag_hour1, tag_hour2, "Tags must rotate"); // Tags are deterministic let tag_a = GroupContext::derive_presence_secret( &exporter_secret, user_id.as_bytes(), time_slice ); let tag_b = GroupContext::derive_presence_secret( &exporter_secret, user_id.as_bytes(), time_slice ); assert_eq!(tag_a, tag_b, "Deterministic derivation"); ``` -------------------------------- ### Enable Presence Management Source: https://context7.com/dirvine/saorsa-gossip/llms.txt Activates the PresenceManager to broadcast online status (presence beacons) within the network. This allows nodes to signal their availability and discover the online status of other peers. It typically requires a pub/sub system. ```Rust use saorsa::presence::PresenceManager; use saorsa::pubsub::PubSub; // Assuming 'pubsub' is an initialized instance of PubSub (e.g., PlumtreePubSub) // let pubsub = ...; let presence_manager = PresenceManager::new(pubsub).await.unwrap(); println!("PresenceManager initialized."); // Start broadcasting presence presence_manager.start().await.unwrap(); ``` -------------------------------- ### Use MLS Exporter API for Presence Beacons (Rust) Source: https://github.com/dirvine/saorsa-gossip/blob/main/docs/saorsa-mls-0.3.0-capabilities.md Demonstrates how to use the MLS Exporter API to derive secrets for presence tag generation as required by SPEC2.md. It involves creating an MLS group, exporting a secret with a specific label and context, and then deriving the presence tag. ```rust use saorsa_mls::{MlsGroup, GroupConfig}; // Create MLS group let config = GroupConfig::default(); let mut group = MlsGroup::new(config, creator_identity).await?; // Export secret for presence tag derivation let exporter_secret = group.export_secret( "presence-tag", // label &[], // context (can include user_id || time_slice) 32 // output length in bytes )?; // Derive presence tag per SPEC2 §10 let user_id = b"user123"; let time_slice = current_time_slice(); let context = [user_id, &time_slice.to_be_bytes()].concat(); let presence_tag = group.export_secret("presence", &context, 32)?; ```