Skip to content

IFM Rendezvous — Headless Deployment Guide

ifm-rendezvous is a headless, GUI-less peer-discovery service for the IFM network. It answers one question: "Which IFM peers are currently reachable through me?" A new node dials the rendezvous, receives the active peers, and connects to them directly — the rendezvous never proxies radio traffic.

This is the same software an operator deploys as:

ModeFlagPurpose
Plain rendezvous node(default)Serves /ifm/rendezvous/1.0.0 — HELLO / GET_PEERS / HEARTBEAT / GOODBYE; keeps a live, lease-based table of the peers connected through it
Master Rendezvous--masterAdds /ifm/rendezvous/register/1.0.0 — the tiny bootstrap authority rendezvous nodes register with; headless, no dashboard (spec §17 forbids one)
Mesh node--rendezvous <pid>@<addr> / --exchangeJoins the redundant rendezvous mesh: nodes discover each other and exchange bounded samples of active peers (§17–§18)
Bootstrap client--bootstrap <pid>@<addr>Registers with a Master on startup and keeps the registration alive by heartbeat (§12)

The binary always runs headless — there is no GUI build.


1. Build

From the repo root:

bash
cargo build --release -p ifm-rendezvous
# → target/release/ifm-rendezvous  (~5.7 MB, static-ish single binary)

bun scripts/build.mjs and bun scripts/release.mjs also build it and stage it as bin/ifm-rendezvous-<os>-<arch> into the release.

Verify:

bash
./target/release/ifm-rendezvous --help
./target/release/ifm-rendezvous --version

2. Quick start (local test)

bash
# Terminal 1 — a Master (or just a plain rendezvous node)
./target/release/ifm-rendezvous --master --data-dir /var/lib/ifm-rvz

# Terminal 2 — a community rendezvous node that registers with it
./target/release/ifm-rendezvous --bootstrap 12D3KooW…@/ip4/127.0.0.1/udp/4001/quic-v1 \
    --data-dir /var/lib/ifm-rvz-2

# Terminal 3 — a plain IFM app that seeds from the rendezvous node
# (fetch the node's live manifest — see `--http-port`)
IFM_MANIFEST_URL=http://127.0.0.1:8080/bootstrap.ifm.json your-ifm-app

On startup every node prints its bootstrap manifest entry — the exact peer_id + listen multiaddrs. The node also serves this manifest live at GET /bootstrap.ifm.json on its operator HTTP port (--http-port, e.g. 8080), rebuilt on every request from the node's current addresses — so apps always fetch the entry point dynamically and nothing needs a static copy:

bootstrap.ifm.json entry:
{
  "protocol": 1,
  "network": "IFM",
  "rendezvous": [
    { "addresses": ["/ip4/203.0.113.10/udp/4001/quic-v1"], "peer_id": "12D3KooW…" }
  ]
}

3. Running modes

3.1 Plain rendezvous node (default)

bash
ifm-rendezvous \
  --port 4001 \
  --data-dir /var/lib/ifm-rvz \
  --advertise /ip4/203.0.113.10/udp/4001/quic-v1
  • --data-dir persists identity.key, so the Peer ID is stable across restarts (spec §38) — publish it once, never rotate it.
  • --advertise lists the public addresses for HELLO responses; without it the node advertises its resolved listen addresses (loopback-rewritten).
  • Defaults: heartbeat 60s, peer lease 180s, max peers 1000, max connections 2048, per-peer rate limit 120 req/min (spec §51).

3.2 Master Rendezvous (the bootstrap authority)

bash
ifm-rendezvous --master \
  --port 4001 \
  --data-dir /var/lib/ifm-rvz-master \
  --advertise /ip4/203.0.113.10/udp/4001/quic-v1
  • Grants 300s registration leases (--master-lease), bounds the live peer table at 1000 (--max-peers; the Master's registered-node directory is likewise bounded, default 1000), and hands a new node the active rendezvous peers to dial directly — the Master is bootstrap only, never in the data path.
  • Community/independent Masters pass their own --network-id; a node refuses to register with a Master of a different network (spec §14).

3.3 Redundant mesh (multiple rendezvous nodes)

Run several plain nodes and wire them together so discovery survives any one node failing (spec §10):

bash
# node A
ifm-rendezvous --port 4001 --data-dir /var/lib/ifm-rvz-a

# node B joins A
ifm-rendezvous --port 4002 --data-dir /var/lib/ifm-rvz-b \
  --rendezvous <A_peer_id>@/ip4/203.0.113.10/udp/4001/quic-v1

# node C joins A (and B through the mesh)
ifm-rendezvous --port 4003 --data-dir /var/lib/ifm-rvz-c \
  --rendezvous <A_peer_id>@/ip4/203.0.113.10/udp/4001/quic-v1

--rendezvous implies --exchange; nodes periodically exchange bounded samples of active peers and refresh the mesh every 30s.

3.4 Bootstrap client (register with a Master)

bash
ifm-rendezvous --bootstrap <master_peer_id>@/ip4/203.0.113.10/udp/4001/quic-v1 \
  --data-dir /var/lib/ifm-rvz

The node registers on startup and heartbeats every 60s. If the Master is unreachable the node keeps running — the Master is bootstrap, not a dependency.


4. Production deployment

4.1 Linux — systemd

ini
# /etc/systemd/system/ifm-rendezvous.service
[Unit]
Description=IFM Rendezvous node
Documentation=https://github.com/EnzoVezzaro/Internet-radio/blob/main/docs/rendezvous
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=ifm-rvz
Group=ifm-rvz
ExecStart=/usr/local/bin/ifm-rendezvous --master --port 4001 \
    --data-dir /var/lib/ifm-rvz --advertise /ip4/YOUR_PUBLIC_IP/udp/4001/quic-v1
Restart=always
RestartSec=10
TimeoutStopSec=30

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/ifm-rvz
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Resource limits
LimitNOFILE=65536
MemoryMax=512M

[Install]
WantedBy=multi-user.target
bash
sudo useradd -r -s /bin/false -d /var/lib/ifm-rvz ifm-rvz
sudo mkdir -p /var/lib/ifm-rvz
sudo chown ifm-rvz:ifm-rvz /var/lib/ifm-rvz
sudo systemctl daemon-reload
sudo systemctl enable --now ifm-rendezvous.service
journalctl -u ifm-rendezvous -f

4.2 macOS — launchd

xml
<!-- ~/Library/LaunchAgents/network.ifm.rendezvous.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>network.ifm.rendezvous</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/ifm-rendezvous</string>
        <string>--master</string>
        <string>--port</string>
        <string>4001</string>
        <string>--data-dir</string>
        <string>/usr/local/var/ifm-rvz</string>
        <string>--advertise</string>
        <string>/ip4/YOUR_PUBLIC_IP/udp/4001/quic-v1</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/usr/local/var/log/ifm-rvz.log</string>
    <key>StandardErrorPath</key>
    <string>/usr/local/var/log/ifm-rvz.err.log</string>
</dict>
</plist>
bash
launchctl load ~/Library/LaunchAgents/network.ifm.rendezvous.plist
launchctl start network.ifm.rendezvous

4.3 Docker

dockerfile
FROM rust:1-slim AS builder
WORKDIR /app
COPY . .
RUN cargo build --release -p ifm-rendezvous

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/ifm-rendezvous /usr/local/bin/ifm-rendezvous
RUN useradd -r -s /bin/false ifm-rvz \
 && mkdir -p /var/lib/ifm-rvz \
 && chown -R ifm-rvz:ifm-rvz /var/lib/ifm-rvz
USER ifm-rvz
EXPOSE 4001/udp 4001/tcp
ENTRYPOINT ["ifm-rendezvous"]
bash
docker build -t ifm-rendezvous .
docker run -d --name ifm-rvz --restart unless-stopped \
  -p 4001:4001/udp -p 4001:4001/tcp \
  -v ifm-rvz-data:/var/lib/ifm-rvz \
  ifm-rendezvous --master --data-dir /var/lib/ifm-rvz \
  --advertise /ip4/YOUR_PUBLIC_IP/udp/4001/quic-v1

4.4 Firewall

bash
# UDP is the primary transport (QUIC); TCP is the fallback
sudo ufw allow 4001/udp comment "IFM rendezvous QUIC"
sudo ufw allow 4001/tcp comment "IFM rendezvous TCP"

5. How the rest of the IFM system uses it

The rendezvous node is a normal libp2p peer on the IFM mesh — a slim entry point and nothing more. Any IFM desktop app or node joins through it with zero rendezvous client code; the mesh then handles peer discovery:

  • Remote mode (the default) — the app fetches the node's live manifest at startup (GET /bootstrap.ifm.json) and dials the listed address(es):
    bash
    IFM_MANIFEST_URL=http://YOUR_NODE/bootstrap.ifm.json ifm-station
    The default is our deployment's manifest (http://ifm-rendezvous.fly.dev/ bootstrap.ifm.json), so a plain app needs no configuration at all.
  • LAN modeIFM_LAN=1 (or network.lan = true in config): no rendezvous; the LAN itself (mDNS) is the entry point.
  • The dialed entry address seeds Kademlia; the DHT then expands the mesh via provider records on /ifm/mesh/v1.

Deploy two or more rendezvous nodes in different regions, and let the DHT

  • rendezvous mesh do the rest.

6. Operations

Health / status

The headless process logs a summary every 30s:

[  120s] master: registered=2 connected=3        # --master mode
[  120s] peers=12 connected=15  roles=station=4 relay=3 listener=8  exchanges=7

Readiness probe

bash
# Port open + QUIC listening (macOS: nc -u -z works only for UDP reachability;
# the authoritative check is a successful manifest fetch + entry dial from an app)
nc -z 127.0.0.1 4001 && echo "TCP up"

Backups

The only state that matters is identity.key — back it up (or regenerate and republish the bootstrap entry):

bash
cp /var/lib/ifm-rvz/identity.key ~/backups/ifm-rvz-identity.key
chmod 600 ~/backups/ifm-rvz-identity.key

Troubleshooting

SymptomCauseFix
Apps don't discover each otherFirewall blocking UDPOpen 4001/udp (QUIC is primary)
Peer ID changes every restart--data-dir missing / not writableUse a persistent --data-dir; check ownership
No peers returned by HELLONew node, empty tableWait for peers to connect; join the mesh with --rendezvous
Registration rejectedNetwork id mismatch--network-id must match the Master's

7. Spec & source

  • Protocol: crates/rendezvous/src/protocol.rs/ifm/rendezvous/1.0.0, /ifm/rendezvous-exchange/1.0.0, /ifm/rendezvous/register/1.0.0
  • Node: crates/rendezvous/src/node.rs
  • CLI: crates/rendezvous/src/main.rs
  • Tests: crates/rendezvous/tests/ (discovery, exchange, expiration, resilience, master)

Released under the MIT License.