← Collaboration overview

☁️ Self-Host the Cloudflare Rendezvous

WAN Live ships with a default rendezvous URL hosted by Happy Tunes. It works out of the box, but the free tier is shared with everyone - if you run lots of sessions, build CI bots that smoke- test connections, or just want a custom domain, deploy your own Cloudflare Worker. The script is ~120 lines, and the free tier covers 100 000 requests/day, which is enough for hundreds of sessions.

💰 Cost: $0/month for hobby use

Cloudflare Workers free tier: 100k requests/day, KV reads 100k/day, KV writes 1k/day. Each session uses ~4 requests + ~3 KV writes (offer, answer, polling). You can run hundreds of sessions per day without ever leaving the free tier. Source: Cloudflare pricing as of 2026-05.

Browser showing the rendezvous Worker /health endpoint returning JSON
Once deployed, your Worker's /health endpoint returns a small JSON blob that confirms it's live.

Why self-host?


How rendezvous works

The Worker is a tiny key-value store with an HTTP front. Each session uses three short-lived records (5-minute TTL): a session marker, the joiner's offer SDP, and the host's answer SDP. Once both sides have each other's SDP, the peers establish a direct DTLS-encrypted WebRTC connection - the Worker isn't in the path anymore.

The Worker only sees five small HTTPS calls per joiner (steps 1-5, ~5 KB total). After the SDPs have been exchanged, ICE finds a path between the peers and DTLS handshakes them directly - your MIDI edits never traverse Cloudflare. Each step in the list above highlights as its dot animates; the green dots in step 6 represent direct peer-to-peer traffic on the new bottom track.


Five-minute deploy walkthrough

  1. Sign up at dash.cloudflare.com. Free tier is fine. You don't need a domain to start - you'll get your-worker.workers.dev.
  2. Create the KV namespace. In the Cloudflare dashboard: Workers & Pages → KV → Create namespace. Name it RDV. The Worker code references the binding by this exact name - spelling matters.
    Cloudflare dashboard, KV namespaces menu, with Create button highlighted
    Naming the new KV namespace 'RDV'
    Create the KV namespace named RDV.
  3. Create the Worker. Workers & Pages → Create application → Create Worker. Pick any name - e.g. midi-rendezvous. Click Deploy on the default "Hello World" template; we'll replace the code in the next step.
    Cloudflare Workers list with the Create Worker button
    Cloudflare default Hello World worker template
    Deploy button on the Hello World template
    Deploy the default template first - we just need a Worker shell to paste into.
  4. Paste the rendezvous code. On the Worker page, click Edit code. Open cloudflare/rendezvous.js in the GitHub repo, copy the full contents (header comment included), paste into the Cloudflare editor replacing the Hello-World body, then click Save and Deploy.
    Cloudflare Worker editor showing the default code
    Save and Deploy button in the Worker editor
    Save & Deploy after pasting rendezvous.js.
  5. Bind the KV namespace to the Worker. Worker page → Settings → Variables and Secrets → scroll to KV Namespace Bindings. Click Add binding:
    • Variable name: RDV (must match exactly - the Worker code reads env.RDV.put(…))
    • KV namespace: pick the RDV namespace you created in step 2
    Click Deploy to publish the binding.
    Worker Settings → Variables and Secrets, with the Add binding entry visible
    Add binding dialog opening, with KV Namespace selected as the binding type
    Binding dialog filled with variable name RDV and the RDV namespace
    Variable name RDV bound to the namespace also called RDV.
  6. Test with /health. Open https://your-worker.workers.dev/health in a browser. You should get JSON like:
    {
      "service": "midi-rdv",
      "version": 3,
      "protocol": "joiner-initiated-multi-peer",
      "maxPeersPerSession": 8,
      "docs": "https://github.com/happytunesai/MidiEditor_AI/tree/main/cloudflare"
    }
    If you see this, the Worker is live. If you get a 404 or 5xx, double check that the deploy ran and the KV binding name is exactly RDV.
    Worker live tail showing successful requests
    Worker live tail (optional) lets you watch requests fly through during a session.
  7. Point MidiEditor at it. Open Edit → Settings → CollaborationRendezvous URL. Paste your worker URL (without /health - just the base, e.g. https://midi-rendezvous.your-name.workers.dev). Click Test connection. You should see a green "Connection looks great".
    MidiEditor Settings, Collaboration tab, custom rendezvous URL pasted
    Paste your Worker URL into the Rendezvous URL field; close the dialog to save.
  8. You're done. Start a WAN session as usual - your code, SDPs, and answer-poll all flow through your own Worker now.

Endpoint reference

For the curious, here's exactly what the Worker exposes. Every record TTL is 5 minutes.

MethodPathBodyReturnsPurpose
GET/health-JSON: service info Pre-flight, monitoring
POST/session{sessionId, displayName}{code, expiresInSec} Host announces a new session, gets a 4-char code
GET/code/<c>-{sessionId, displayName} Joiner verifies the code is valid before initiating
POST/code/<c>/joiner-offer{joinerId, sdp}{ok: true} Joiner publishes their WebRTC offer (one per joiner)
GET/code/<c>/joiner-offers-{offers:[{joinerId,sdp,ts},…]} Host polls for pending offers; returns all not-yet-paired ones
POST/code/<c>/host-answer{joinerId, sdp}{ok: true} Host publishes the answer SDP for one specific joiner
GET/code/<c>/host-answer/<joinerId>-{sdp} Joiner polls for their specific answer

The 4-character alphabet excludes visually ambiguous characters (no 0/O/1/I/L) and uses the remaining 31 (A-Z minus I/L/O = 23, plus 2-9 = 8), giving 31^4 ≈ 923 000 codes. The 5-minute TTL means stale codes auto-expire; collisions are detected and re-rolled on the host side. Each joiner generates its own random joinerId (16 url-safe chars) so the host can route answers to the right peer.


Security notes


Custom domain

The default your-worker.workers.dev URL works fine, but mapping a real domain looks more polished and stays stable if you ever rename the Worker:

  1. Add the domain to your Cloudflare account (free for any registrar).
  2. Worker page → Settings → Triggers → Add Custom Domain. Pick a subdomain like rdv.your-band.com.
  3. Cloudflare auto-provisions an SSL cert and wires DNS for you.
  4. Update Settings → Collaboration → Rendezvous URL in MidiEditor to the new URL.

Your existing workers.dev URL keeps working in parallel - you can roll the change to your users without breaking anyone in the middle of a session.


Adding a TURN server

For peers behind symmetric NAT (rare on home networks, common on some mobile carriers and corporate VPNs), STUN alone isn't enough - they need a TURN server to actually relay traffic. MidiEditor doesn't ship with a TURN URL because TURN incurs real bandwidth costs.

Three options if you need TURN:

MidiEditor's ICE server list is a comma-separated set of stun: and turn: URLs. Internal config - for v1.7 we don't surface a UI for it; edit your QSettings store under Collab/wan/iceServers if you need to override the defaults. A future release will expose this in Settings.


Troubleshooting