easyp2p

package module
v0.0.0-...-428e69e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 20 Imported by: 0

README

easyp2p 🚀

easyp2p is a beginner-friendly Go library that takes the complexity out of building peer-to-peer applications using libp2p. Whether you're building a chat server, a VPN, or a decentralized database, easyp2p provides simple abstractions to get you started in minutes.

Features 🌟

  • Zero Configuration: Sensible defaults for TCP, QUIC, and NAT traversal.
  • Auto Discovery: Locally via mDNS and globally via Kademlia DHT.
  • Bootstrap Support: Seamlessly join the network using well-known bootstrap nodes.
  • PubSub (GossipSub): Easy message broadcasting for chat and IRC.
  • Direct Streams: Raw data transfer for custom protocols like VPNs and DB queries.

Quick Start 🛠️

1. Installation
go get github.com/opensource-jeff/easyp2p
2. Basic Node Setup
package main

import (
    "context"
    "time"
    "fmt"
    "github.com/opensource-jeff/easyp2p"
)

func main() {
    ctx := context.Background()
    
    // Create a node with default settings (automatically handles identity and peer cache)
    node := easyp2p.Must(easyp2p.NewNode(ctx, easyp2p.DefaultConfig()))
    defer node.Close()

    // Listen for NAT status changes
    node.OnNATStatusChange(func(status easyp2p.NATStatus) {
        fmt.Printf("NAT Status changed: %s\n", status)
    })

    // Wait for the network to be ready (at least 1 non-bootstrap peer)
    fmt.Println("Connecting to peers...")
    if err := node.WaitForNetwork(ctx, 30*time.Second); err != nil {
        fmt.Println("Error:", err)
    }

    // Display node information
    node.PrintDescribe()
}

Use Cases 📖

Decentralized IRC (PubSub)

Join a room and broadcast messages to all connected peers.

topic, _ := node.JoinTopic("global-chat")

// Receive messages
topic.OnMessage(func(msg string, sender string) {
    fmt.Printf("[%s]: %s\n", sender, msg)
})

// Send a message
topic.Publish("Hello everyone!")
Peer-to-Peer VPN (Streams)

Establish direct point-to-point connections for raw data transfer.

const myProtocol = "/my-app/vpn/1.0"

// Handle incoming data
node.HandleProtocol(myProtocol, func(s *easyp2p.Stream) {
    buf := make([]byte, 1024)
    n, _ := s.Read(buf)
    fmt.Println("Received data:", string(buf[:n]))
})

// Connect to a peer
stream, _ := node.ConnectTo(peerID, myProtocol)
stream.Write([]byte("Confidential VPN traffic"))
Decentralized Database (Request-Response)

Implement custom search logic over the network.

const dbProtocol = "/my-app/db/1.0"

// Search responder
node.HandleProtocol(dbProtocol, func(s *easyp2p.Stream) {
    query, _ := s.Read(...)
    result := searchLocalDB(query)
    s.Write([]byte(result))
})

// Query another node
stream, _ := node.ConnectTo(peerID, dbProtocol)
stream.Write([]byte("search-query"))

Security & Networking 🔐

Encryption & Authentication

easyp2p uses the industry-standard libp2p security stack:

  • End-to-End Encryption: All traffic is encrypted using Noise or TLS 1.3 by default.
  • Identity (Ed25519): Every node is uniquely identified by an Ed25519 private key. This key is used during the cryptographic handshake to verify the PeerID of the other node, preventing impersonation.
  • Identity Persistence: By default, your node's private key is saved to ~/.config/easyp2p/identity.key, so your PeerID stays the same every time you restart your application. You can disable this by setting Persist: false in your Config.
Peer Discovery & Connectivity

easyp2p uses a multi-layered approach to help you find other peers:

  • Local Discovery (mDNS): Peers on the same Wi-Fi or local network will find each other automatically without any configuration.
  • Global Discovery (Bootstrap Servers): On the public internet, your node first connects to a set of stable Bootstrap Servers (provided by Protocol Labs). These servers act as "meeting points" to help your node join the global P2P network.
  • Kademlia DHT: Once connected to a bootstrap node, your node uses the Distributed Hash Table (DHT) to find other peers running your application anywhere in the world.
  • NAT Traversal: easyp2p automatically attempts to punch through firewalls and routers using UPnP and NAT-PMP. If a direct connection is impossible, it will automatically use Circuit Relays to ensure you can still communicate.

Credits & Disclosure 🤖

This library was vibe-coded with the help of Gemini CLI. It was built to make libp2p accessible for beginners and personal projects. While it is fully functional and tested, it is intended as a learning tool and a foundation for personal decentralized experiments.

Examples 📁

Check out the examples/ directory for full, runnable implementations:

  • examples/chat: A simple IRC-style command line chat.
  • examples/vpn: A basic stream-based data transfer example.
  • examples/database: A request-response pattern for network searching.

Documentation & Wiki 📖

For more in-depth guides, check out our Wiki:

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	ListenPort     int
	BootstrapPeers []string
	EnableMDNS     bool
	IdentityPath   string // Path to save/load the node's private key
	PeerCachePath  string // Path to save/load known peers
	Persist        bool   // Whether to persist identity and peer cache
}

Config holds the configuration for a Node.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a sensible default configuration.

type NATStatus

type NATStatus string

NATStatus represents the status of NAT reachability.

const (
	NATStatusUnknown     NATStatus = "checking"
	NATStatusOpen        NATStatus = "open"
	NATStatusRelayed     NATStatus = "relayed"
	NATStatusUnreachable NATStatus = "unreachable"
)

type Node

type Node struct {
	Host   host.Host
	DHT    *dht.IpfsDHT
	PubSub *pubsub.PubSub
	// contains filtered or unexported fields
}

Node represents a simplified libp2p node.

func Must

func Must(node *Node, err error) *Node

Must is a helper that panics if err is not nil, otherwise it returns the node.

func NewNode

func NewNode(ctx context.Context, cfg Config) (*Node, error)

NewNode creates and starts a new libp2p node.

func (*Node) Addrs

func (n *Node) Addrs() []multiaddr.Multiaddr

Addrs returns the multiaddresses the node is listening on.

func (*Node) Advertise

func (n *Node) Advertise(ctx context.Context, namespace string)

Advertise makes the node discoverable on a specific topic/namespace via the DHT.

func (*Node) Close

func (n *Node) Close() error

Close shuts down the node.

func (*Node) ConnectTo

func (n *Node) ConnectTo(peerID peer.ID, pid string) (*Stream, error)

ConnectTo opens a stream to a peer for a specific protocol.

func (*Node) Describe

func (n *Node) Describe() string

Describe returns a formatted multi-line string containing node information.

func (*Node) FindPeers

func (n *Node) FindPeers(ctx context.Context, namespace string) (<-chan peer.AddrInfo, error)

FindPeers searches for other nodes that have advertised the given namespace.

func (*Node) HandleProtocol

func (n *Node) HandleProtocol(pid string, handler func(s *Stream))

HandleProtocol registers a handler for a specific protocol.

func (*Node) ID

func (n *Node) ID() peer.ID

ID returns the peer ID of the node.

func (*Node) JoinTopic

func (n *Node) JoinTopic(topicName string) (*Topic, error)

JoinTopic joins a PubSub topic and starts discovery for other peers on the same topic.

func (*Node) OnNATStatusChange

func (n *Node) OnNATStatusChange(fn func(NATStatus))

OnNATStatusChange registers a callback to be fired when the NAT status changes.

func (*Node) OnPeerFound

func (n *Node) OnPeerFound(fn func(peer.AddrInfo))

OnPeerFound registers a callback to be fired when a new peer connects.

func (*Node) OnPeerLost

func (n *Node) OnPeerLost(fn func(peer.AddrInfo))

OnPeerLost registers a callback to be fired when a peer disconnects.

func (*Node) PrintDescribe

func (n *Node) PrintDescribe()

PrintDescribe prints the node information to stdout.

func (*Node) WaitForNetwork

func (n *Node) WaitForNetwork(ctx context.Context, timeout time.Duration) error

WaitForNetwork blocks until the node has at least 1 connected peer that is NOT a bootstrap node. It polls every 500ms and returns an error if the timeout is reached or context is cancelled.

type Stream

type Stream struct {
	network.Stream
}

Stream is a wrapper around network.Stream with simplified methods.

func (*Stream) Close

func (s *Stream) Close() error

Close closes the stream.

func (*Stream) ReadFull

func (s *Stream) ReadFull(buf []byte) error

ReadFull reads data from the stream into the provided buffer.

func (*Stream) WriteAll

func (s *Stream) WriteAll(buf []byte) error

WriteAll writes data from the provided buffer to the stream.

type Topic

type Topic struct {
	// contains filtered or unexported fields
}

Topic represents a PubSub topic.

func (*Topic) Close

func (t *Topic) Close() error

Close leaves the topic.

func (*Topic) OnMessage

func (t *Topic) OnMessage(handler func(msg string, sender string))

OnMessage registers a callback for messages on this topic.

func (*Topic) Publish

func (t *Topic) Publish(message string) error

Publish broadcasts a message to the topic.

Directories

Path Synopsis
examples
chat command
database command
vpn command
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL