The rendezvous server is how two Recon2x peers find each other. It is a small HTTP service: peers publish opaque encrypted blobs to a room (keyed by a shared word) and fetch the blobs others left there. It stores only ciphertext it cannot read — no IPs, no identities in the clear.
This guide puts that server on a Raspberry Pi and makes it reachable on the public internet at your own domain, even though the Pi is behind CGNAT.
Most home/mobile ISPs put you behind carrier-grade NAT (CGNAT) — your "public" IP is shared, and you cannot port-forward to your Pi. Inbound connections simply never arrive.
The fix is a reverse tunnel: the Pi makes an outbound connection to Cloudflare and holds it open. Inbound web requests hit Cloudflare and travel down that tunnel to the Pi. Outbound connections pass through any NAT, so CGNAT is bypassed entirely. The Pi never needs to be directly reachable.
Internet ─▶ yourdomain.com ─▶ Cloudflare ─▶ [tunnel] ─▶ Raspberry Pi
├─ recon2x --server
└─ cloudflared
Cloudflare Tunnel proxies HTTP/HTTPS/WebSocket. The rendezvous server is plain HTTP, so this is sufficient. (It is not enough for a UDP relay — that is a separate, later concern.)
- A Raspberry Pi on your network, powered on most of the time.
- A domain you control, with its DNS managed by Cloudflare (free plan is fine). If your domain is registered elsewhere, you can still move just its DNS to Cloudflare.
- SSH access to the Pi.
The Pi is ARM. Build on the Pi itself (simplest), or cross-compile.
On the Pi:
# Install Rust if needed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# From a copy of this repo on the Pi (only the backend/ crate is needed):
cd recon2x/backend
cargo build --release --no-default-features
# Binary: backend/target/release/recon2x-backend--no-default-features disables the frontend feature. The rendezvous server
does not serve a UI, and the frontend build/ directory will not exist on the
Pi (you only copied backend/) — so embedding it must be switched off, or the
build fails with a RustEmbed folder ... does not exist error.
RECON2X_PORT=8788 ./recon2x-backend --serverIt listens on http://[::]:8788. Verify locally on the Pi:
curl http://localhost:8788/r/health
# {"role":"rendezvous","status":"ok","version":"..."}--server selects the rendezvous role. Without it the binary runs the desktop
peer app instead — keep the flag.
# Debian/Raspberry Pi OS (arm64):
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64 \
-o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
cloudflared --version(For a 32-bit OS use cloudflared-linux-arm instead.)
# Authenticate cloudflared with your Cloudflare account (opens a browser link)
cloudflared tunnel login
# Create a named tunnel
cloudflared tunnel create recon2x
# Route your domain (or a subdomain) to the tunnel
cloudflared tunnel route dns recon2x rendezvous.yourdomain.comcloudflared tunnel create prints a tunnel UUID and writes a credentials
JSON file (path shown in the output) — note both.
Create ~/.cloudflared/config.yml on the Pi:
tunnel: <TUNNEL-UUID>
credentials-file: /home/pi/.cloudflared/<TUNNEL-UUID>.json
ingress:
# Forward all traffic for the hostname to the local rendezvous server.
- hostname: rendezvous.yourdomain.com
service: http://localhost:8788
# Required catch-all.
- service: http_status:404Test it:
cloudflared tunnel run recon2xFrom any other machine:
curl https://rendezvous.yourdomain.com/r/health
# {"role":"rendezvous","status":"ok","version":"..."}If that responds, the Pi is publicly reachable through the tunnel.
So they survive reboots and crashes.
Rendezvous server — /etc/systemd/system/recon2x.service:
[Unit]
Description=Recon2x rendezvous server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/home/pi/recon2x-backend --server
Environment=RECON2X_PORT=8788
Restart=always
RestartSec=5
User=pi
[Install]
WantedBy=multi-user.targetCloudflare tunnel — install it as a service:
sudo cloudflared service installEnable and start both:
sudo systemctl daemon-reload
sudo systemctl enable --now recon2x
sudo systemctl enable --now cloudflaredCheck status / logs:
systemctl status recon2x cloudflared
journalctl -u recon2x -fEach peer's desktop app needs to know the rendezvous URL —
https://rendezvous.yourdomain.com. (Wiring this into the peer app and UI is
the next development step; this guide only stands the server up.)
- Updating the server: rebuild the binary, replace it,
sudo systemctl restart recon2x. - Memory: the registry is in-memory and self-expiring (entries live ~10 minutes). Restarting the service clears all rooms — harmless, since peers re-publish.
- Multiple nodes later: the registry is intentionally a single-node design for now. If you add a second node, peers will not need to change — the peer-facing API hides node topology.
- It is zero-knowledge: the Pi cannot read any blob. A compromised Pi leaks room activity and timing, but not peer IPs or identities (those are inside the encrypted blobs).