An encrypted peer-to-peer mesh networking library for C++17 providing authenticated encrypted channels over UDP with dynamic peer discovery, NAT traversal, and QUIC-inspired reliable stream transport. Inspired by the simplicity of WireGuard — a single fixed cipher suite, minimal attack surface, no protocol negotiation — applied to mesh networking instead of point-to-point tunnels. Everything you need for secure mesh communication in one place — just #include <sneaker.hpp> and link against sneakerpp.
The API exposes only standard library types — no crypto headers, no platform headers. Under the hood, every packet is encrypted with ChaCha20-Poly1305, authenticated via a full Noise XX handshake over X25519, and padded to a uniform size (1200 bytes baseline, up to 1472 after path MTU discovery) so that an observer cannot distinguish data from chaff. Peer identity is a static Noise public key, and in-tunnel rekey with fresh ephemeral keypairs happens automatically on a configurable interval (default: every two minutes).
- Noise XX handshake — mutual authentication with forward secrecy (X25519 + ChaChaPoly + BLAKE2b)
- Uniform-size packets — all UDP datagrams padded to the current path MTU (1200 bytes baseline, up to 1472 after PLPMTUD) by default; observer cannot determine payload size (configurable: disable padding for higher throughput)
- In-tunnel rekey — fresh ephemeral keypairs every 2 minutes, zero downtime
- Chaff traffic — configurable random padding packets mask idle peers
- Per-channel reliable ordered streams — 128 independent channels (0-127) with byte-stream semantics and length-prefixed message framing
- BBR congestion control — bandwidth-probing congestion control with STARTUP, DRAIN, PROBE_BW, and PROBE_RTT phases
- Packet pacing — token-bucket pacer smooths send bursts to reduce loss
- Loss detection — packet-number-based and time-based loss detection per RFC 9002, with Probe Timeout (PTO)
- RTT estimation — smoothed RTT, RTT variance, and minimum RTT tracking per RFC 9002 section 5.3
- Selective ACKs — ACK frames with range encoding for efficient out-of-order acknowledgement
- Flow control — per-peer (4 MB default) and per-channel (1 MB default) send windows with backpressure signaling
- Automatic retransmission — lost stream data is re-queued and retransmitted by the congestion controller
- Path MTU discovery — RFC 8899 PLPMTUD binary-searches between 1200 and 1472 bytes for the largest UDP payload the path supports
- Reputation scoring — peers earn/lose score based on relay behavior, latency, protocol compliance
- Automatic eviction — low-scoring peers removed to make room for better ones
- Temporary banning — misbehaving peers banned by IP with configurable duration
- Peer exchange (PEX) — connected peers share their peer lists (rate-limited, up to 32 entries)
- Capability filtering — peers advertise capability bits; connections respect them
- Domain Generation Algorithm (DGA) — BLAKE2b-derived base32 subdomains across configurable TLDs
- DNS TXT bootstrap — Ed25519-signed bootstrap records with peer lists
- LAN multicast — zero-config local peer discovery on 239.255.77.77:7777
- Manual peers — explicit peer list for known endpoints
- Introduction protocol — ask a mutual peer to introduce you to someone behind NAT
- UDP hole punching — symmetric NAT traversal with 200ms probe ticks
- Relay fallback — capacity-limited relay sessions with 120-second idle timeout
- Linux (GCC, Clang), macOS (AppleClang), Windows (MSVC, MinGW)
- Hardened compiler flags:
/W4 /WX /GS /guard:cf(MSVC),-Wall -Wextra -Wpedantic -Werror -fstack-protector-strong(GCC/Clang) - ASLR, DEP, RELRO/NOW, noexecstack linker hardening
Every UDP packet follows the same outer format:
┌──────────────────────────────────────────────────────┐
│ Magic "SNKR" (4B) │ pkt_type (1B) │ version (1B) │ 6 bytes cleartext
├──────────────────────────────────────────────────────┤
│ Nonce (8B) │
├──────────────────────────────────────────────────────┤
│ Encrypted payload (variable, up to 1442B) │ ChaCha20-Poly1305
├──────────────────────────────────────────────────────┤
│ Poly1305 tag (16B) │
├──────────────────────────────────────────────────────┤
│ Padding (to current MTU, when enabled) │
└──────────────────────────────────────────────────────┘
The 6-byte cleartext header is the only thing visible to a network observer. Everything else — including the inner frame type and payload — is encrypted. When uniform_packet_size is enabled (the default), all packets are padded to the current path MTU (1200 bytes initially, growing up to 1472 as PLPMTUD discovers the path supports larger packets). Disable padding for higher throughput at the cost of leaking payload sizes.
Inside the encrypted payload, frames use a QUIC-inspired format with variable-length integer encoding (RFC 9000 section 16):
STREAM: [type:1] [channel:1] [offset:varint] [length:varint] [data]
ACK: [type:1] [largest_acked:varint] [ack_delay:varint] [ranges...]
CONTROL: [type:1] [length:varint] [payload]
Multiple frames can be packed into a single packet. ACK frames are always written first, followed by control frames, retransmitted stream data, and new stream data (round-robin across channels).
- C++17 compiler (GCC, Clang, or MSVC)
- CMake 3.10+
git clone --recursive https://github.com/gibme-c/sneakerpp
cd sneakerpp
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j$(nproc)If you already cloned without --recursive:
git submodule update --init --recursivegit submodule add https://github.com/gibme-c/sneakerpp external/sneakerpp
git submodule update --init --recursiveIn your CMakeLists.txt:
add_subdirectory(external/sneakerpp)
target_link_libraries(your_target PRIVATE sneakerpp)Then include the single public header:
#include <sneaker.hpp>#include <sneaker.hpp>
#include <iostream>
int main()
{
sneaker::Config config;
config.protocol_id = "my-app-v1";
config.listen_port = 9000;
config.manual_peers = {"192.168.1.50:9000", "10.0.0.2:9000"};
auto node = sneaker::Node::create(config);
node.on_message([](const sneaker::PeerId &from, uint8_t channel,
const uint8_t *data, size_t len) {
std::cout << "Got message on channel " << (int)channel
<< " (" << len << " bytes)\n";
});
node.on_peer_event([](const sneaker::PeerId &peer, sneaker::PeerEvent event) {
if (event == sneaker::PeerEvent::CONNECTED)
std::cout << "Peer connected\n";
});
if (node.start() != sneaker::StartResult::OK)
{
std::cerr << "Failed to start node\n";
return 1;
}
// Send to all connected peers on channel 0 (reliable, ordered)
const uint8_t msg[] = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
node.broadcast(msg, sizeof(msg));
// Send to a specific peer on channel 1
auto peers = node.connected_peers();
if (!peers.empty())
node.send(peers[0].id, msg, sizeof(msg), 1);
// ... run your application loop ...
node.shutdown();
}All sends are reliable and ordered within a channel. Messages are delivered via the stream transport layer with automatic congestion control, loss detection, and retransmission — no manual send_reliable() call needed.
All runtime behavior is tunable through sneaker::Config. The values mentioned elsewhere in this README are just the defaults — set them to whatever your application needs.
| Field | Default | Description |
|---|---|---|
protocol_id |
"sneaker-default" |
Network identifier; different ID = isolated network |
listen_port |
0 (OS-assigned) |
UDP port to listen on |
target_peer_count |
12 |
Target number of connected peers |
max_inbound |
32 |
Maximum inbound connections |
max_outbound |
12 |
Maximum outbound connections |
max_per_ip |
3 |
Maximum peers per IP address |
manual_peers |
{} |
Explicit peer endpoints to connect to |
| Field | Default | Description |
|---|---|---|
keepalive_interval |
25 s | PING interval to peers |
dead_peer_timeout |
120 s | No response = peer disconnected |
rekey_interval |
2 min | In-tunnel key rotation with fresh ephemerals |
peer_evaluate_interval |
30 s | Peer scoring evaluation frequency |
chaff_interval |
200 ms | Decoy packet interval (0 = disable chaff) |
relay_retry_direct_interval |
2 min | Retry direct connection for relayed peers |
| Field | Default | Description |
|---|---|---|
max_connection_data |
4 MB | Per-peer send window (total across all channels) |
max_stream_data |
1 MB | Per-channel send window |
max_ack_delay |
25 ms | Maximum ACK delay before forcing an ACK |
max_write_queue_bytes |
16 MB | Per-peer app-to-IO queue cap |
| Field | Default | Description |
|---|---|---|
socket_recv_buffer_size |
4 MB | OS socket receive buffer size (0 = OS default) |
socket_send_buffer_size |
1 MB | OS socket send buffer size (0 = OS default) |
uniform_packet_size |
true |
Pad all packets to current path MTU (disable for higher throughput) |
| Field | Default | Description |
|---|---|---|
eviction_threshold |
30.0 |
Peer score below which eviction occurs |
initial_ban_duration |
5 min | First-time ban duration (escalates on re-ban) |
max_concurrent_handshakes |
20 |
Simultaneous Noise handshakes allowed |
| Field | Default | Description |
|---|---|---|
hole_punch_timeout |
5 s | UDP hole punch attempt timeout |
max_relayed_connections |
3 |
Concurrent relay sessions |
accept_relay_requests |
true |
Whether to relay traffic for other peers |
proxy_all_traffic |
false |
Route all traffic through proxy peers |
| Field | Default | Description |
|---|---|---|
enable_dga_discovery |
true |
Enable DGA-based peer discovery |
enable_local_multicast |
true |
Enable LAN multicast discovery (239.255.77.77:7777) |
dga_tlds |
.com, .net, .org |
TLDs to query for DGA domains |
dns_resolvers |
Cloudflare, Google, Quad9 | DNS resolver addresses |
| Field | Default | Description |
|---|---|---|
worker_threads |
0 (auto) |
Worker thread count; 0 = CPU count - 1 |
| Option | Default | Description |
|---|---|---|
BUILD_TESTS |
OFF | Build test binaries |
BUILD_TOOLS |
OFF | Build diagnostic tool binaries |
FORCE_PORTABLE |
OFF | Disable SIMD backends in crypto submodules |
ENABLE_LTO |
OFF | Enable link-time optimization |
┌─────────────────────────────────┐
│ Public API (sneaker.hpp) │ Only standard library types exposed
├─────────────────────────────────┤
│ Mesh Layer (src/mesh/) │ Node, peer manager, peer exchange, scoring
├─────────────────────────────────┤
│ NAT Layer (src/nat/) │ Introduction, hole punching, relay
├─────────────────────────────────┤
│ Transport (src/transport/) │ QUIC-style streams, congestion, loss detection
├─────────────────────────────────┤
│ Noise XX (src/noise/) │ Handshake state machine, CipherState
├─────────────────────────────────┤
│ Discovery (src/discovery/) │ DGA DNS, multicast, bootstrap
├─────────────────────────────────┤
│ Platform (src/platform/) │ Cross-platform socket/threading abstractions
└─────────────────────────────────┘
Each layer depends only on the layers below it. Crypto types never appear above src/noise/. Platform headers never appear above src/platform/. The public API at the top uses only standard library types.
Four crypto libraries included as git submodules under external/:
| Library | Provides | Submodule Branch |
|---|---|---|
| ed25519 | Ed25519 signatures, X25519 DH, Ristretto255 | development |
| tinyblake | BLAKE2b, HMAC-BLAKE2b, PBKDF2 | default |
| tinychacha | ChaCha20, Poly1305, ChaCha20-Poly1305 AEAD | default |
| tinysha | SHA-256, SHA-384, SHA-512, SHA3, HMAC-SHA, PBKDF2 | default |
All four are zero-dependency, header+source C++17 libraries with SIMD backends (AVX2, AVX-512, NEON). They are linked as PRIVATE CMake dependencies — their types and symbols never appear in the public API.
Detailed documentation for the public API and each internal module:
| Guide | Contents |
|---|---|
include/README.md |
Public API reference (sneaker.hpp) |
src/platform/README.md |
Cross-platform socket, threading, CSPRNG |
src/noise/README.md |
Noise XX handshake, CipherState, SymmetricState |
src/transport/README.md |
QUIC-style streams, framing, congestion, loss detection, rekey |
src/mesh/README.md |
Node lifecycle, peer management, peer exchange |
src/discovery/README.md |
DGA, DNS bootstrap, LAN multicast, orchestration |
src/nat/README.md |
Introduction protocol, hole punching, relay |
This library is provided under the BSD-3-Clause license. See LICENSE for details.