Peer Discovery
Finding your contacts without revealing who you are looking for.
Discovery Layers
RVNT uses a layered peer discovery system. Each layer operates at a different scope and provides different tradeoffs between speed, privacy, and reliability. The layers are tried in order from most private to most reliable:
Layer 1: Direct Connect (manual, most private)
Layer 2: QR Code Exchange (in-person, verified)
Layer 3: DNS-SD / Bonjour (local network, automatic)
Layer 4: libp2p mDNS (local network, automatic)
Layer 5: Kademlia DHT (global, decentralized)
Layer 6: Bootstrap Nodes (global, centralized fallback)
┌─────────────────────────────────────────────────┐
│ GLOBAL (Internet) │
│ ┌───────────────────────────────────────────┐ │
│ │ Kademlia DHT (decentralized, 1000+ nodes)│ │
│ │ Bootstrap Nodes (RVNT operated, fallback) │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ LOCAL NETWORK (LAN/WiFi) │ │
│ │ DNS-SD (Bonjour) - zero-config discovery │ │
│ │ libp2p mDNS - multicast peer announce │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ DIRECT (manual/in-person) │ │
│ │ QR Code - verified identity exchange │ │
│ │ Direct Connect - IP/multiaddr/onion │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘ Layer 1: Direct Connect
The most private discovery method. You manually enter the peer's network address. No server, no broadcast, no multicast. Only you and the peer know about the connection.
Supported address formats:
/ip4/192.168.1.50/tcp/4001/p2p/QmPeerID123... (libp2p multiaddr, LAN)
/ip4/203.0.113.10/tcp/4001/p2p/QmPeerID123... (libp2p multiaddr, WAN)
xxxxxxxx.onion:4001 (Tor hidden service)
/dns4/peer.example.com/tcp/4001/p2p/QmPeerID... (DNS name)
Connection flow:
1. Parse the multiaddr or .onion address
2. Establish libp2p connection (QUIC or TCP+Noise)
3. Exchange identity keys over authenticated channel
4. Verify safety numbers (out-of-band)
5. Connection established Layer 2: QR Code Exchange
The recommended discovery method for first contact. Two people meet in person and scan each other's QR codes. This provides cryptographic verification of identity without any network interaction.
QR Code payload (CBOR encoded):
{
"v": 1, // Version
"u": "alice", // Username
"ik": [32 bytes], // Ed25519 identity public key
"dh": [32 bytes], // X25519 identity DH key
"fp": [20 bytes], // Key fingerprint (BLAKE3 truncated)
"addr": "/ip4/.../tcp/4001/p2p/Qm..." // Optional: current network address
}
Security properties:
- Identity is verified in-person (no MITM possible)
- Key fingerprint is compared automatically
- No server is involved in the exchange
- If network address is included, direct P2P connection is possible
- QR code is ephemeral (displayed on screen, not stored) Layer 3: DNS-SD (Bonjour)
DNS-Based Service Discovery (DNS-SD, RFC 6763) over multicast DNS (mDNS, RFC 6762) enables automatic discovery of RVNT peers on the same local network. This is the same protocol used by AirDrop, AirPrint, and other Apple services.
Service advertisement:
Service type: _rvnt._tcp
Port: 4001
TXT records:
v=1 // Protocol version
id=[20 bytes hex] // Truncated peer ID (NOT identity key)
pk=[32 bytes hex] // libp2p public key (NOT RVNT identity key)
mDNS multicast group: 224.0.0.251:5353 (IPv4)
ff02::fb:5353 (IPv6)
Discovery flow:
1. RVNT advertises _rvnt._tcp service on local network
2. Other RVNT instances discover the advertisement
3. Connect via libp2p using the advertised port
4. Exchange RVNT identity keys over the libp2p channel
5. If both users have added each other as contacts, messages flow
Privacy notes:
- The libp2p peer ID is NOT the RVNT identity key
- The peer ID is a transport-layer identifier only
- An observer on the LAN can see that RVNT is running
- The observer cannot see who the user is or who they message macOS Multicast Restrictions
Starting with macOS 14 (Sonoma), Apple restricts multicast and broadcast network access for sandboxed apps. RVNT handles this through:
1. com.apple.developer.networking.multicast entitlement
(required for mDNS/Bonjour service registration)
2. Local Network privacy permission (prompted on first use)
"RVNT would like to find and connect to devices on your
local network."
3. Fallback: If multicast is denied, RVNT falls back to
DHT-only discovery. Local network peers are still reachable
via the global DHT, but with higher latency.
4. Wi-Fi Multicast entitlement for background operation
(keeps mDNS alive when app is in background) Layer 4: libp2p mDNS
In addition to DNS-SD, RVNT uses libp2p's built-in mDNS module for peer discovery on the local network. This provides redundancy with DNS-SD and works on platforms where Bonjour is not available (Linux, Windows).
libp2p mDNS discovery:
- Multicast address: 224.0.0.251:5353
- Service name: _p2p._udp.local
- Response includes libp2p PeerId and multiaddr
- Discovery interval: every 30 seconds
- TTL: 120 seconds
Difference from DNS-SD:
- DNS-SD uses the system's Bonjour/Avahi framework
- libp2p mDNS is implemented in userspace
- libp2p mDNS works without system service registration
- Both can run simultaneously without conflict Layer 5: Kademlia DHT
The Kademlia Distributed Hash Table (DHT) enables global peer discovery without centralized servers. Each RVNT node participates in the DHT and can find any other node by its peer ID.
DHT architecture:
Protocol: libp2p Kademlia (IPFS-compatible)
Key space: 256-bit (SHA-256 of peer ID)
Bucket size: k=20
Replication: 20 closest nodes store each record
Lookup: O(log n) hops for n nodes in the network
Transport: QUIC (primary) + TCP (fallback)
Auth: Noise XX (X25519 + ChaChaPoly)
Peer routing:
1. Alice wants to find Bob's network address
2. Alice has Bob's libp2p PeerId (from QR code or username lookup)
3. Alice queries the DHT: FindPeer(Bob's PeerId)
4. The query propagates through O(log n) DHT nodes
5. Nodes closer to Bob's PeerId in XOR distance respond
6. Eventually, a node returns Bob's current multiaddr
7. Alice connects directly to Bob
Privacy considerations:
- DHT queries reveal that Alice is looking for Bob's PeerId
- DHT nodes on the query path learn this (limited to O(log n) nodes)
- Bob's PeerId is a transport identifier, NOT his RVNT identity key
- With Tor enabled, Alice's IP is hidden from DHT nodes
- DHT records have TTL and are not permanently stored Provider Records
When a peer comes online, it publishes a provider record to the DHT:
PUT /peer/{peer_id} → {
multiaddr: ["/ip4/x.x.x.x/udp/4001/quic-v1"],
ttl: 3600, // 1 hour
signature: Ed25519 signature (libp2p key, NOT RVNT identity)
}
The record is stored by the k=20 closest nodes in XOR distance.
Records expire after TTL and must be refreshed.
When the peer goes offline, the record naturally expires. Layer 6: Bootstrap Nodes
Bootstrap nodes are RVNT-operated entry points to the DHT. They are used only when a node first joins the network and has no other peers in its routing table.
Bootstrap process:
1. RVNT starts with a hardcoded list of bootstrap node multiaddrs
2. Connect to 2-3 bootstrap nodes
3. Perform a DHT FIND_NODE query for own PeerId
4. This populates the local routing table with nearby peers
5. Disconnect from bootstrap nodes
6. All subsequent queries use the DHT directly
Bootstrap node addresses (hardcoded):
/dns4/boot1.rvntos.io/udp/4001/quic-v1/p2p/QmBoot1...
/dns4/boot2.rvntos.io/udp/4001/quic-v1/p2p/QmBoot2...
/dns4/boot3.rvntos.io/udp/4001/quic-v1/p2p/QmBoot3...
What bootstrap nodes see:
✓ Connecting peer's IP address (briefly)
✓ Connecting peer's libp2p PeerId (transport-level)
✗ RVNT identity key (never exposed)
✗ Username (never exposed)
✗ Contact list (never exposed)
✗ Message content (never exposed) Privacy Summary
| Method | Scope | Reveals IP | Reveals Identity | Reveals Contacts |
|---|---|---|---|---|
| Direct Connect | Point-to-point | To peer only | To peer only | No |
| QR Code | In-person | No (optional) | To peer only | No |
| DNS-SD | Local network | To LAN | No (PeerId only) | No |
| libp2p mDNS | Local network | To LAN | No (PeerId only) | No |
| Kademlia DHT | Global | To DHT path* | No (PeerId only) | Query target visible |
| Bootstrap | Global | To bootstrap node | No (PeerId only) | No |
* With Tor enabled, IP is hidden from DHT nodes.
Further Reading
- Tor Integration -- How Tor hides your IP during peer discovery
- Server Architecture -- Bootstrap node and identity server details
- Offline Mesh -- Peer discovery over Bluetooth/WiFi when internet is unavailable