DisktroNet Documentation
DisktroNet
A modular P2P connectivity toolkit that provides identity, discovery, transport orchestration, NAT traversal, and relay capabilities for building robust peer-to-peer applications.
Identity & Discovery
Create peer identities with keystore support and discover peers via mDNS and bootstrap mechanisms.
Transport Orchestration
Intelligent connection management with direct TCP, UDP hole punching, and relay fallback.
Security
Pluggable handshake mechanisms including TLS support for secure communication.
Observability
Comprehensive metrics and monitoring hooks for debugging and performance analysis.
Requirements
- Go 1.22+
- Windows/macOS/Linux
Quick Start
Get started with DisktroNet in just a few lines of code:
package main
import (
"context"
"fmt"
"disktronet/internal/p2p/config"
)
func main() {
ctx := context.Background()
id, _ := config.NewIdentity(ctx, config.Options{
Host: "localhost",
Port: 9000,
Type: "client"
})
fmt.Println("PeerID:", id.PeerID) // Base58BTC encoded
}
📦 Installation
Clone the repository and run the examples:
git clone https://github.com/jaywantadh/DisktroNet.git
cd DisktroNet
go run ./examples/connmanager
Architecture
DisktroNet follows a modular architecture with clear separation of concerns:
ConnManager
Manages connection pooling and ensures live connections
NATOrchestrator
Orchestrates connectivity modes in order of preference
Direct TCP
Attempts direct connection first
UDP Punch
Falls back to UDP hole punching
Relay
Uses relay as final fallback
Key Components
🔧 Transport Layer
- Connection management with pooling
- NAT traversal orchestration
- UDP hole punching
- STUN client for external IP discovery
🔍 Discovery
- mDNS for local peer discovery
- Bootstrap mechanisms
- Static and DNS-based peer lists
🔄 Relay
- TCP-based circuit relay
- Framed message protocol
- Session management
- Keepalive and idle timeout
🛡️ Security
- Pluggable handshake interface
- TLS implementation
- Certificate validation
Development Phases
DisktroNet is developed in progressive phases, each building upon the previous:
Identity & Discovery
Create peer identities, manage keystores, and discover peers through mDNS and bootstrap mechanisms.
Features
- Peer identity creation and management
- Keystore support (PEM and RAW seed formats)
- mDNS advertising and discovery
- Bootstrap peer discovery
Example: Creating Identity
import (
"context"
"disktronet/internal/p2p/config"
"disktronet/internal/p2p/discovery"
)
ctx := context.Background()
id, _ := config.NewIdentity(ctx, config.Options{
Host: "localhost",
Port: 9000,
Type: "client"
})
// mDNS advertise with TXT
txt := discovery.EncodeTXT(string(id.PeerID), id.Meta.NodeType, id.Meta.Port)
adv := discovery.NewZeroconfAdvertiserWithTXT("disktronode", id.Meta.Port, txt)
_ = adv.Start(ctx)
defer adv.Stop()
Transport & Connection Management
Efficient connection pooling, heartbeat management, and intelligent connection orchestration.
Features
- Connection pooling with per-peer limits
- Heartbeat and idle timeout management
- Exponential backoff with jitter
- Connection deduplication
Example: Connection Manager
import (
"context"
"time"
"disktronet/internal/p2p/transport"
)
cm := transport.NewConnManager(transport.ConnMgrOptions{
MaxConnsPerPeer: 2,
IdleTimeout: 2 * time.Minute,
HeartbeatEvery: 20 * time.Second,
DialTimeout: 7 * time.Second,
BackoffMin: 500 * time.Millisecond,
BackoffMax: 30 * time.Second,
DialFunc: func(ctx context.Context, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
},
})
_ = cm.Start(context.Background())
defer cm.Stop(context.Background())
c, err := cm.Ensure(context.Background(), "peerID", "127.0.0.1:9000")
NAT Traversal
Intelligent NAT traversal with direct TCP attempts, UDP hole punching, and automatic fallback to relay.
Features
- Direct TCP connection attempts
- UDP hole punching with rendezvous coordination
- NAT type detection (cone vs symmetric)
- Automatic relay fallback
Example: NAT Orchestrator
import (
"context"
"disktronet/internal/p2p/transport"
"disktronet/internal/p2p/relay"
)
o := transport.NATOrchestrator{
TCPDial: func(ctx context.Context, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
},
Rendezvous: transport.NewInMemoryRendezvous(),
HolePunch: transport.SimpleHolePuncher{},
RelayClient: relay.InMemoryRelay{},
DialTimeout: 7 * time.Second,
PunchTimeout: 5 * time.Second,
}
res, err := o.Connect(context.Background(), "selfID", "peerID", "peer.addr:9000")
Relay
Lightweight circuit relay for guaranteed connectivity when direct paths fail.
Features
- TCP-based circuit relay
- Framed message protocol
- Session pairing and management
- Keepalive and idle timeout
Example: Using Relay
import (
"context"
"disktronet/internal/p2p/relay"
)
r := relay.InMemoryRelay{}
// Peer A
go func(){
sA, _ := r.Open(context.Background(), "peerB")
_ = sA.Send([]byte("hi"))
}()
// Peer B
sB, _ := r.Open(context.Background(), "peerB")
msg, _ := sB.Recv() // "hi"
API Reference
Comprehensive API documentation for all DisktroNet components.
Transport Layer
ConnectionManager
Manages connection pooling and ensures live connections to peers.
Start(ctx context.Context) error
Start background tasks
Stop(ctx context.Context) error
Stop and close all peers
Ensure(ctx, peerID, addr) (net.Conn, error)
Return or establish a live connection
Stats() Stats
Returns connection statistics
NATOrchestrator
Orchestrates connectivity modes: Direct TCP → UDP punch → Relay.
Connect(ctx, selfID, peerID, peerTCPAddr) (ConnectResult, error)
Attempts connection with escalation policy
Relay Layer
Session
Bidirectional framed stream between peers.
Send([]byte) error
Send data to peer
Recv() ([]byte, error)
Receive data from peer
Close() error
Close the session
Security
Handshake
Pluggable handshake interface for secure connections.
Client(raw net.Conn) (net.Conn, error)
Wrap client side (e.g., TLS)
Server(raw net.Conn) (net.Conn, error)
Optional server-side wrapper
Examples
Ready-to-run examples demonstrating DisktroNet capabilities:
🔗 Connection Manager
End-to-end usage of ConnManager with escalation policy.
go run ./examples/connmanager
🌐 NAT Traversal
UDP hole punching demonstration.
go run ./examples/nat
🔄 Relay Fallback
Using relay for guaranteed connectivity.
go run ./examples/relay
🔒 TLS Security
TLS handshake over direct TCP connections.
go run ./examples/tls
🚀 Running the Relay Server
Start a standalone relay server:
# Build the relay server
go build -o bin/disktronet-relay ./cmd/disktronet-relay
# Run with custom configuration
./bin/disktronet-relay -addr :9000 -idle 60s -maxframe 8388608
Security
DisktroNet provides flexible security mechanisms through pluggable handshake interfaces.
🔐 TLS Handshake
Secure connections with configurable TLS settings.
import (
"crypto/tls"
"disktronet/internal/p2p/transport"
)
hs := transport.TLSHandshake{
ClientTLS: &tls.Config{
InsecureSkipVerify: true, // dev only
}
}
o := transport.NATOrchestrator{
TCPDial: func(ctx context.Context, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
},
Handshake: hs,
}
res, err := o.Connect(context.Background(), "self", "peer", "127.0.0.1:9000")
🛡️ Production Security
- Configure trusted root CAs
- Set proper ServerName validation
- Use strong cipher suites
- Implement certificate pinning
Observability
Comprehensive metrics and monitoring for debugging and performance analysis.
Transport Metrics
Monitor connection attempts, success rates, and fallback behavior:
import (
"disktronet/internal/p2p/transport"
"disktronet/internal/p2p/relay"
)
type myTransportMetrics struct{}
func (myTransportMetrics) DialAttempt(peer string) { fmt.Println("dial_attempt", peer) }
func (myTransportMetrics) DialSuccess(mode string) { fmt.Println("dial_success", mode) }
func (myTransportMetrics) DialFailure(mode string) { fmt.Println("dial_failure", mode) }
func (myTransportMetrics) STUNQuery() { fmt.Println("stun_query") }
func (myTransportMetrics) STUNResult() { fmt.Println("stun_result") }
func (myTransportMetrics) PunchAttempt() { fmt.Println("punch_attempt") }
func (myTransportMetrics) PunchSuccess() { fmt.Println("punch_success") }
func (myTransportMetrics) PunchFailure() { fmt.Println("punch_failure") }
func (myTransportMetrics) RelayOpenSuccess() { fmt.Println("relay_open_success") }
func (myTransportMetrics) RelayOpenFailure() { fmt.Println("relay_open_failure") }
type myRelayMetrics struct{}
func (myRelayMetrics) SessionOpened() { fmt.Println("relay_session_opened") }
func (myRelayMetrics) SessionClosed() { fmt.Println("relay_session_closed") }
func (myRelayMetrics) BytesForwarded(n int) { fmt.Println("relay_bytes", n) }
func init() {
transport.SetMetrics(myTransportMetrics{})
relay.SetMetrics(myRelayMetrics{})
}
Relay Metrics
Track session lifecycle and data forwarding:
- SessionOpened - New relay session established
- SessionClosed - Session terminated
- BytesForwarded - Data volume through relay
Troubleshooting
Common issues and their solutions:
🔍 mDNS Discovery Issues
- Check firewall rules and local network segment
- Ensure service type/name matches between advertiser and browser
- Verify network connectivity
🌐 NAT Traversal Problems
- Symmetric NATs may block punching → rely on relay fallback
- Ensure both peers can reach the rendezvous coordinator
- Check STUN server availability
🔄 Connection Issues
- Too many dials: always use
Ensure()for deduplication - Idle timeouts: tune
IdleTimeoutandHeartbeatEvery - Reconnect storms: adjust
BackoffMin/MaxandDialTimeout
🔒 TLS Handshake Failures
- Certificate validation failures: configure proper
RootCAsandServerName - For development: use
InsecureSkipVerify: true - Handshake failure triggers automatic fallback to UDP/relay
📊 Metrics Not Appearing
- Ensure you called
transport.SetMetrics()andrelay.SetMetrics()before dialing - Check that metrics implementations are set at program startup
- Verify metrics hooks are not nil
🔄 Relay Pairing Issues
- Ensure both sides call
Openwith the samedestPeer - Verify both peers share the same relay instance
- Check relay server connectivity and configuration