Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

freenet/freenet-test-network

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

freenet-test-network

Reliable test network infrastructure for Freenet integration testing.

Problem

Testing Freenet requires spinning up multiple local peers, which has been unreliable and flaky. This crate provides a simple, reliable way to create test networks.

Quick Start

use freenet_test_network::TestNetwork;
use std::sync::LazyLock;

// Shared network for all tests (starts once)
static NETWORK: LazyLock<TestNetwork> = LazyLock::new(|| {
    TestNetwork::builder()
        .gateways(1)
        .peers(5)
        .build_sync()
        .unwrap()
});

#[tokio::test]
async fn my_test() -> anyhow::Result<()> {
    let network = &*NETWORK;

    // Each test gets its own WebSocket connection
    let ws_url = network.gateway(0).ws_url();

    // Use freenet_stdlib::client_api::WebApi to connect
    // ...

    Ok(())
}

Status

Alpha - Core functionality working, including remote SSH peer support for realistic NAT/firewall testing.

Diagnostics

When a network fails to reach the desired connectivity threshold the builder now:

  • Prints the most recent log entries across all peers in chronological order
  • Can optionally preserve each peer's data directory (including peer.log, configs, and keys) for later inspection

Enable preservation with:

let network = TestNetwork::builder()
    .gateways(1)
    .peers(2)
    .preserve_temp_dirs_on_failure(true)
    .build()
    .await?;

If startup fails, the preserved artifacts are placed under /tmp/freenet-test-network-<timestamp>/.

Docker NAT Simulation

New: Test Freenet peers behind simulated NAT routers using Docker containers.

Why Docker NAT Testing?

Testing on localhost can't catch bugs related to:

  • NAT traversal and port mapping
  • Peers on different private networks
  • Real network isolation
  • Gateway discovery from behind NAT

Quick Start

use freenet_test_network::{TestNetwork, Backend, DockerNatConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let network = TestNetwork::builder()
        .gateways(1)                                    // Gateway on public network
        .peers(2)                                        // Peers behind NAT
        .backend(Backend::DockerNat(DockerNatConfig::default()))
        .build()
        .await?;

    // Each peer runs in its own container behind a NAT router
    println!("Gateway: {}", network.gateway(0).ws_url());
    println!("Peer 0 (NAT): {}", network.peer(0).ws_url());

    Ok(())
}

Automatic Cleanup

The Docker NAT backend automatically cleans up resources:

  1. On normal exit: Containers and networks are removed via Drop trait
  2. On ctrl-c: Signal handler ensures graceful cleanup
  3. On panic: Drop trait still executes, cleaning up resources
  4. Stale resources: Automatically removed at startup (older than 5 minutes)

Resource names include timestamps (e.g., freenet-nat-20251126-211500-13135) for easy identification.

Manual Cleanup

If resources are somehow left behind, you can manually clean them:

# List freenet-nat resources
docker ps -a | grep freenet-nat
docker network ls | grep freenet-nat

# Remove all freenet-nat containers
docker ps -a | grep freenet-nat | awk '{print $1}' | xargs -r docker rm -f

# Remove all freenet-nat networks
docker network ls | grep freenet-nat | awk '{print $1}' | xargs -r docker network rm

Example

See examples/docker_nat.rs for a complete example:

cargo run --example docker_nat
# Or with custom peer count:
PEER_COUNT=4 cargo run --example docker_nat

Remote Peer Support (SSH)

New in v0.2: Spawn peers on remote Linux machines via SSH for realistic NAT/firewall testing.

Why Remote Peers?

Testing on localhost (127.0.0.1) can't catch bugs related to:

  • NAT traversal
  • Address observation (e.g., issue #2087 - loopback address propagation)
  • Real network latency and routing
  • Firewall behavior

Quick Start

use freenet_test_network::{TestNetwork, PeerLocation, RemoteMachine};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Configure remote machine
    let remote = RemoteMachine::new("192.168.1.100")
        .user("testuser")
        .identity_file("/home/user/.ssh/id_rsa");

    // Build network with remote peer
    let network = TestNetwork::builder()
        .gateways(1)                                        // Local gateway
        .peers(2)                                            // 2 regular peers
        .peer_location(2, PeerLocation::Remote(remote))     // Make peer #2 remote
        .build()
        .await?;

    // Use network as normal
    let ws_url = network.peer(1).ws_url();

    Ok(())
}

Setup Requirements

  1. SSH Access: Password-less SSH via key or agent
  2. Remote Machine: Linux with SSH server (OpenSSH recommended)
  3. Binary Deployment: Freenet binary is automatically uploaded via SCP
  4. Ports: Remote peer will bind to OS-allocated ports (tests default port selection)

Configuration Options

let remote = RemoteMachine::new("example.com")
    .user("testuser")                               // SSH username (default: current user)
    .port(2222)                                     // SSH port (default: 22)
    .identity_file("/path/to/key")                  // SSH private key
    .freenet_binary("/usr/local/bin/freenet")       // Pre-installed binary path (optional)
    .work_dir("/tmp/freenet-tests");                // Working directory (default: /tmp/freenet-test-network)

Advanced: Multiple Remote Machines

let remote1 = RemoteMachine::new("192.168.1.100");
let remote2 = RemoteMachine::new("192.168.1.101");

let network = TestNetwork::builder()
    .gateways(1)
    .peers(3)
    .peer_location(1, PeerLocation::Remote(remote1))
    .peer_location(2, PeerLocation::Remote(remote2))
    // peer 0 and gateway remain local
    .build()
    .await?;

Distribute Across Remotes

Round-robin distribution across multiple machines:

let machines = vec![
    RemoteMachine::new("192.168.1.100"),
    RemoteMachine::new("192.168.1.101"),
];

let network = TestNetwork::builder()
    .gateways(2)
    .peers(4)
    .distribute_across_remotes(machines)  // Alternates peers across machines
    .build()
    .await?;

How It Works

  1. Binary Deployment: Local freenet binary is uploaded to remote machine via SCP
  2. Process Spawning: Peer started via nohup over SSH, PID captured
  3. Address Discovery: Remote peer's public IP is auto-detected
  4. Gateway Keys: Gateway public keys are uploaded to remote peers
  5. Log Collection: Logs are downloaded via SCP on demand
  6. Cleanup: Remote processes are killed via SSH on Drop

Limitations

  • GitHub Actions: Outbound SSH likely blocked, use for local testing
  • Platform: Remote machines must be Linux (SSH + standard utilities)
  • Network: Remote machines must be reachable from test runner

Example

See examples/remote_peer.rs for a complete example.

export REMOTE_HOST=192.168.1.100
export REMOTE_USER=testuser
export SSH_KEY_PATH=$HOME/.ssh/id_rsa
cargo run --example remote_peer

TODO

  • Implement proper connectivity detection (fix FIXME from fdev)
  • Add WebSocket connection helpers
  • Add integration tests
  • Optimize startup time
  • Add remote peer health monitoring
  • Support custom SSH connection pooling

License

LGPL-3.0-only

About

Reliable test network infrastructure for Freenet

Resources

License

Stars

Watchers

Forks

Packages

No packages published
Morty Proxy This is a proxified and sanitized view of the page, visit original site.