grapevine 1.0.0

A modern, asynchronous peer-to-peer gossip protocol library and application
Documentation

Grapevine

Crates.io Documentation CI License

A modern, asynchronous peer-to-peer gossip protocol library and application.

Features

  • Async/await - Built on Tokio for high-performance async I/O
  • Epidemic broadcast - Probabilistic message forwarding for efficient network coverage
  • Anti-entropy - Periodic synchronization ensures eventual consistency
  • Rate limiting - Per-peer token bucket rate limiting prevents DoS attacks
  • Highly configurable - Fine-tune gossip parameters for your use case
  • Zero unsafe code - Memory safe and thread safe

Installation

As a Library

To use Grapevine in your Rust project:

cargo add grapevine

Or add manually to your Cargo.toml:

[dependencies]

grapevine = "1.0"

tokio = { version = "1", features = ["full"] }

bytes = "1"

As a CLI Application

To install the standalone gossip client binary:

cargo install grapevine

Then run:

grapevine --help

Quick Start

Basic Example

use grapevine::{Node, NodeConfig};
use bytes::Bytes;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a node with default configuration
    let config = NodeConfig::default();
    let node = Node::new(config).await?;

    // Set up message handler
    node.on_message(|origin, data| {
        println!("Received from {origin}: {data:?}");
    }).await;

    // Start the node
    node.start().await?;

    // Broadcast a message
    node.broadcast(Bytes::from("Hello, gossip!")).await?;

    // Keep running until explicit shutdown
    tokio::signal::ctrl_c().await?;
    node.shutdown().await?;

    Ok(())
}

Multi-Node Cluster

use grapevine::{Node, NodeConfig, NodeConfigBuilder};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create first node
    let node1 = Node::new(NodeConfig::default()).await?;
    node1.start().await?;
    let addr1 = node1.local_addr().await.unwrap();

    // Create second node, bootstrapping from first
    let config2 = NodeConfigBuilder::new()
        .add_bootstrap_peer(addr1)
        .fanout(5)
        .build()?;
    let node2 = Node::new(config2).await?;
    node2.start().await?;

    // Nodes will automatically discover and connect to each other

    Ok(())
}

Configuration

Grapevine is highly configurable. See NodeConfig for all options:

use grapevine::NodeConfigBuilder;
use std::time::Duration;

let config = NodeConfigBuilder::new()
    .bind_addr("127.0.0.1:8000".parse().unwrap())
    .gossip_interval(Duration::from_secs(5))
    .fanout(3)
    .max_peers(50)
    .max_message_size(1024 * 1024)
    .build()?;

Feature Flags

  • crypto - Enable message signing and verification (deferred to v1.1)

Note: QUIC transport support is planned for v1.1+

Architecture

Grapevine implements a push-based gossip protocol with the following components:

  • Epidemic Broadcast: Probabilistic message forwarding (default 70% probability, max 5 forwards)
  • Anti-Entropy: Periodic digest exchange and repair (every 30s) ensures eventual consistency
  • Peer Management: Automatic health monitoring with state machine (Connecting => Connected => Stale => Disconnected)
  • Rate Limiting: Per-peer token bucket (100 capacity, 50 tokens/sec) prevents DoS attacks
  • Message Deduplication: Time-based eviction (5 minute TTL) prevents duplicates
  • Graceful Shutdown: Phased shutdown with goodbye notifications to peers

See Architecture Documentation for details.

Testing

# Run all tests

cargo test --all-features


# Run integration tests only

cargo test --test integration


# Run with logging

RUST_LOG=debug cargo test

CLI Usage

Grapevine includes a standalone binary for running gossip nodes. For a complete guide on starting nodes, joining networks, broadcasting messages, and more, see the CLI Usage Guide.

Environment Variables

For a straightforward run, first copy .env.example to .env and customize:

cp .env.example .env

# Edit .env with your configuration

cargo run

CLI Arguments

-H, --host <HOST>                Host to bind to [env: BIND_HOST] [default: 127.0.0.1]

-p, --port <PORT>                Port to listen on [env: BIND_PORT] [default: 8000]

-b, --peer <PEER>                Bootstrap peer addresses [env: BOOTSTRAP_PEERS]

-g, --gossip-interval <SECS>     Gossip interval in seconds [env: GOSSIP_INTERVAL_SECS] [default: 5]

-f, --fanout <FANOUT>            Fan-out factor [env: FANOUT] [default: 3]

-m, --max-peers <MAX_PEERS>      Maximum number of peers [env: MAX_PEERS] [default: 50]

-l, --log-level <LEVEL>          Log level (trace, debug, info, warn, error) [env: RUST_LOG] [default: info]

Common Operations

# Start a seed node

cargo run


# Join the network from another terminal

cargo run -- --port 8001 --peer 127.0.0.1:8000


# Join with multiple bootstrap peers

cargo run -- --port 8002 \

  --peer 127.0.0.1:8000,127.0.0.1:8001


# Start with custom configuration

cargo run -- \

  --host 0.0.0.0 \

  --port 9000 \

  --fanout 5 \

  --gossip-interval 3


# Use environment variables

BIND_HOST=0.0.0.0 BIND_PORT=9000 cargo run


# Enable debug logging

cargo run -- --log-level debug


# Graceful shutdown

# Press Ctrl+C to send goodbye messages and cleanly exit

See docs/client.md for:

  • Step-by-step setup instructions
  • Multi-node cluster examples
  • Network tuning parameters
  • Troubleshooting common issues

Examples

See the examples directory:

Run an example:

RUST_LOG=info cargo run --example simple_node

Contributing

Contributions are welcome! Please read our Contributing Guidelines and Code of Conduct.

License

Licensed under either of

at your option.