// ghost protocol · v0.1.0-alpha

NYX

A serverless, end-to-end encrypted mesh communication engine.
No central servers. No registration. No metadata.

Rust libp2p · QUIC Kademlia DHT GossipSub AES-256-GCM zeroize tokio async dcutr NAT traversal
>

A terminal that leaves no trace

Nyx is a fully decentralized, anonymous peer-to-peer communication engine written in Rust. Every instance is a sovereign node in a globally distributed mesh — there are no servers, no databases, no administrator accounts, and no signup flow. The network is its users.

The architecture layers three independent systems: the Kademlia DHT for global peer discovery, GossipSub for mesh-level message routing, and a request-response whisper channel for direct one-to-one key delivery. Application-layer AES-256-GCM encryption ensures that even if mesh routing is observed, the contents are opaque to every node that doesn't hold the correct room key in RAM.

TRANSPORT

QUIC over UDP

Zero-RTT reconnection, multiplexed streams, and built-in TLS 1.3 at the network layer.

DISCOVERY

Dual-engine routing

mDNS for instant LAN discovery. Kademlia DHT for global O(log N) resolution with O(1) LRU cache on top.

MESH

Signature-enforced gossip

GossipSub with strict validation. Every message is signed by the sender's Ed25519 keypair — spoofing is structurally impossible.

CRYPTO

Zero-trust rooms

256-bit AES keys live only in RAM. The mesh routes ciphertext — nodes without the key receive incomprehensible binary noise.

MEMORY

RAM remanence protection

Keys are wrapped in ZeroizeOnDrop structs. Volatile memset to 0x00 fires the microsecond a room is exited.

NETWORK

NAT traversal

dcutr punches through symmetric NAT without manual port forwarding via a libp2p relay circuit upgrade.

System architecture

Nyx composes nine libp2p protocol behaviours into a single unified swarm. Each behaviour handles a distinct concern and communicates through the swarm event bus only.

nyx behaviour stack — protocol layer view
Nyx behaviour stack Nine libp2p behaviours grouped by transport, discovery, and messaging concerns. TRANSPORT DISCOVERY MESSAGING QUIC + YamuxUDP · TLS 1.3 · multiplexed Noise protocolrelay circuit encryption relay::clientcircuit relay v2 dcutrhole punching / NAT bypass autonatreachability detection kademlia DHTglobal routing · O(log N) mdnsLAN zero-config discovery identifypropagate listen addrs LRU routing cache100 slots · O(1) fast path gossipsubmesh pub/sub · Ed25519 signed request_response/nyx/invite/1.0.0 connection_limitsFD exhaustion guard NyxBehaviour — tokio::select! event bus SwarmEvent dispatch · async Readline UI · mpsc channels SECURITY LAYER AES-256-GCM · ZeroizeOnDrop keys · token bucket rate limiter
Nine composed libp2p behaviours — transport (green), discovery (teal), messaging (ice) — feeding into the unified tokio event bus.

Message flow: plaintext to wire

Every publish is a fresh nonce + ciphertext blob. The mesh sees only opaque binary.

per-message encryption flow
Message encryption flow User input flows through OsRng nonce, AES-256-GCM, then GossipSub. User input Readline prompt OsRng nonce 96-bit · fresh per msg AES-256-GCM encrypt + auth tag GossipSub publish to mesh topic nodes without the room key receive unintelligible binary — decryption fails silently
payload wire format: [12B nonce ‖ ciphertext ‖ 16B GCM tag]

Zero-trust room model

Nyx separates transport security from application-layer secrecy. QUIC provides TLS 1.3 between adjacent peers — but that only protects the hop, not the route. AES-256-GCM secures the room end-to-end regardless of how many relay hops the packet takes.

Room key lifecycle

key lifecycle — create → invite → accept → zeroize
Room key lifecycle A 256-bit AES key is generated on /create, transmitted via Whisper on /invite, loaded on /accept, and zeroized on /exit. /create room OsRng → 32 bytes SecureRoomKey(RAM) /invite peer base64 → CBOR req Whisper channel /accept decode b64 → key bytes load into RAM /exit ZeroizeOnDrop fires memset 0x00 to RAM key is NEVER written to disk · NEVER logged · NEVER leaves the process in plaintext cold-boot window = process lifetime · swap protection requires encrypted swap partition
The Whisper channel uses /nyx/invite/1.0.0 — the AES key never touches the gossip mesh.

Why application-layer encryption?

QUIC/TLS secures the link between two adjacent nodes. It does not protect message contents from other authenticated members of the same GossipSub topic. AES-256-GCM ensures that subscription grants no meaningful information — only nodes holding the symmetric key recover plaintext.

Encryption implementation

// Per-message encryption — src/main.rs
let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(&current_room_key.0));
let nonce  = Aes256Gcm::generate_nonce(&mut OsRng);      // 96-bit fresh nonce
let mut payload = nonce.to_vec();                          // prepend nonce
payload.extend_from_slice(&cipher.encrypt(&nonce, cmd.as_bytes())?);
swarm.behaviour_mut().gossipsub.publish(topic, payload);   // ciphertext on the wire

Active DoS shielding

Exposing a raw UDP listener to the internet risks amplification and exhaustion attacks. Nyx implements two independent, layered defences directly in the swarm event loop.

Token bucket rate limiter

Each unique source IP is tracked in a HashMap<IpAddr, TokenBucket>. Buckets refill at 0.5 tokens/second up to a capacity of 5 tokens. A new connection consumes one token; when empty the connection is silently throttled.

fn check_and_consume(&mut self) -> bool {
    let elapsed = now.duration_since(self.last_refill).as_secs_f64();
    self.tokens = (self.tokens + elapsed * self.refill_rate).min(self.capacity);
    if self.tokens >= 1.0 { self.tokens -= 1.0; true } else { false }
}

Hard connection bounds

LimitValuePurpose
max_pending_incoming10Half-open handshake queue
max_pending_outgoing5Outbound dial queue
max_established_incoming30Inbound peer slots
max_established_outgoing30Outbound peer slots
max_established_total60Total simultaneous peers
max_established_per_peer2Multi-stream dedup guard

Installation

Requires the latest stable Rust toolchain. No other system dependencies on macOS or Windows.

# Install Rust (if not present)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

# Clone and install globally
git clone https://github.com/tyrobro/nyx.git
cd nyx
cargo install --path . --force

# Launch
nyx

On Ubuntu/Debian you may need the C build toolchain:

sudo apt install build-essential pkg-config

Usage guide

Launch with nyx. The node immediately binds a random QUIC port, bootstraps into the global Kademlia DHT, and subscribes to nyx-global. Your Peer ID is printed on boot; share it out-of-band to establish a private room.

CommandDescription
/create <room_name>Generates a 256-bit AES key in RAM, unsubscribes from the current topic, and moves you into a new cryptographically sealed room.
/invite <PeerID>Dials the target peer and dispatches the current room's AES key as a base64 CBOR payload over /nyx/invite/1.0.0. The key never touches the gossip mesh.
/dm <PeerID>Creates a one-time ephemeral room with a random ID and key, then atomically invites the target. Equivalent to /create + /invite in a single command.
/acceptProcesses a pending invite: decodes the base64 key, loads it into a ZeroizeOnDrop struct, and drops you into the sender's encrypted room.
/connect <PeerID>Manually resolves a Peer ID via LRU cache or Kademlia DHT and negotiates a QUIC tunnel.
/exitTriggers ZeroizeOnDrop on all active room keys, tears down all QUIC/relay sockets, and terminates the process.
⚠
Security disclaimer: Nyx uses industry-standard algorithms but has not undergone a formal security audit. Encrypted swap is strongly recommended to fully close the RAM remanence window.

Technology stack

tokio
Async runtime · select! event loop
libp2p
P2P networking stack
libp2p-quic
QUIC/UDP transport
libp2p-kad
Kademlia DHT
libp2p-gossipsub
Mesh pub/sub
libp2p-mdns
LAN discovery
libp2p-dcutr
NAT hole punching
libp2p-relay
Circuit relay v2
libp2p-autonat
Reachability probe
aes-gcm
AES-256-GCM AEAD
zeroize
Secure key erasure
lru
O(1) routing cache
serde / cbor
Invite serialization
base64
Key encoding
clap
CLI argument parsing
rustyline-async
Async readline UI

Roadmap

NAT traversal refinement

Relay circuit negotiation and dcutr hole-punch success rates across different NAT topologies including carrier-grade NAT.

Multi-relay failover

Maintain a ranked list of known relays and automatically re-circuit when the active relay goes offline.

Encrypted file transfer

Chunked, AES-encrypted binary streaming over the established Yamux multiplexer without leaving the encrypted session context.

Forward secrecy

Rotate room keys on a configurable interval using a ratchet construction, limiting the blast radius of a key compromise.

Persistent identity

Optional Ed25519 identity persistence with passphrase encryption so a Peer ID survives process restarts.