A from-scratch recipe to bring up a local multi-node logos-delivery network that talks QUIC, push traffic through it, and extract proof that QUIC actually carried the messages.
- Docker + Docker Compose v2 (
docker compose version). - A QUIC-capable logos-delivery image — i.e. a build whose
wakunode2exposes--quic-support. (QUIC landed in logos-delivery PR #3951.) - The simulator with the
QUIC_SUPPORTtoggle — from PR #123 (branchfeat/quic-support); onmasteronce it merges.
Assumed already done: Docker works, and the logos-delivery image is pulled/built.
git clone https://github.com/logos-messaging/logos-delivery-simulator.git
cd logos-delivery-simulatorPick the image (CI build, e.g. quay.io/wakuorg/nwaku-pr:3951, or a local build
from docker/binaries/Dockerfile.bn.amd64). Verify it has the flag:
docker run --rm --entrypoint /usr/local/bin/wakunode2 quay.io/wakuorg/nwaku-pr:3951 --help | grep quic
# --quic-support Enable QUIC transport: true|false [=false]
# --quic-port QUIC (UDP) listening port. [=60000]Compose auto-loads .env. QUIC_SUPPORT defaults to true, so QUIC is on
without setting anything.
cat > .env <<'EOF'
LD_IMAGE=quay.io/wakuorg/nwaku-pr:3951
NUM_LD_NODES=5
WATCHTOWER_ENABLED=false
RPC_URL=http://foundry:8545
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
ETH_FROM=0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
RLN_RELAY_MSG_LIMIT=100
RLN_RELAY_EPOCH_SEC=600
RLN_CONTRACT_REPO_COMMIT=e75ac913e579ad872f54b2225eec35d1de3d98b0
EOFQUIC is a transport, independent of RLN, so disable RLN to keep the run focused on QUIC.
Disable RLN in run_nwaku.sh (bind-mounted; no rebuild): in the final
exec /usr/bin/wakunode block, drop the --rln-relay* lines and replace the
generateRlnKeystore call with true.
Bring up the QUIC pieces (RLN services skipped with --no-deps):
docker compose up -d foundry
docker compose up -d --no-deps bootstrap nwaku
docker compose up -d --no-deps rest-trafficGive it ~40s for discovery + the gossip mesh to form.
All four checks read the node logs.
Nodes announce a QUIC address (/udp/<port>/quic-v1):
docker compose logs nwaku bootstrap | grep "Announcing addresses" | grep quic-v1
# .../ip4/10.2.0.8/tcp/60000/... AND .../ip4/10.2.0.8/udp/60000/quic-v1/...Peers dial each other QUIC-first (QUIC sorted ahead of TCP):
docker compose logs nwaku | grep "Dialing peer" | grep quic-v1
# Dialing peer ... addrs="[/ip4/10.2.0.x/udp/60000/quic-v1, /ip4/10.2.0.x/tcp/60000]"Connections are established over QUIC (identify reports the QUIC address):
docker compose logs nwaku | grep "identify: info received" | grep "observable_address=ok(/ip4/[^)]*quic-v1"Traffic relays across the QUIC mesh — rest-traffic POSTs messages
(from_peer_id=rest) that propagate node→node:
docker compose logs rest-traffic | grep "status:200" # injector reaching nodes
docker compose logs nwaku | grep -c "received relay message" # messages propagatedHealth (should be zero):
docker compose logs nwaku | grep -ciE 'Dial failed|Unhandled exception|SIGSEGV'Or read it from the metrics — Grafana visualizes these; query Prometheus directly:
curl -s 'http://localhost:9090/api/v1/query?query=libp2p_peers' # mesh: peers per node
curl -s 'http://localhost:9090/api/v1/query?query=libp2p_gossipsub_received_total' # relay msgs per nodeGrafana dashboard nwaku-monitoring: http://localhost:3001 (admin/admin).
Flip QUIC off and confirm the QUIC addresses disappear (nodes fall back to TCP and still relay):
echo "QUIC_SUPPORT=false" >> .env
docker compose up -d --no-deps --force-recreate bootstrap nwaku
sleep 35
docker compose logs --since 40s nwaku | grep "Announcing addresses" | grep -c quic-v1 # -> 0Remove that line (or set true) and recreate to bring QUIC back. The before/after
is the cleanest proof the --quic-support flag is what put QUIC on the wire.
docker compose down -v- Each container is its own network namespace, so every node uses the same default ports (TCP+QUIC on 60000, REST 8645, discv5 9000) with no collisions.
QUIC_SUPPORT=falsealso lets the sim run images that predate--quic-support(the flag is simply omitted).- This validates QUIC functionally at small scale. Large-connection / scaling behavior is not in this scope.