Reachability Verification

Prove players can actually connect: the none, TCP connect, and UDP echo challenge modes, with the exact DSCV1 wire spec.

A heartbeat only proves your game server has outbound connectivity. It says nothing about whether players can connect inbound: a server behind a closed firewall port still sends heartbeats. Verification closes that gap by probing the advertised endpoint before listing the server.

Pick a mode per Discovery app: open the app page and set Verification in Settings. Changes reach the Discovery service within about a minute.

The three modes

  • No verification (none): no probe. Any server that heartbeats is listed. Fastest to integrate, but a server behind a closed firewall port will still appear in the list.

  • TCP connect (tcp): Discovery opens and immediately drops a TCP connection to the advertised endpoint. No code needed on your side; any listening TCP socket passes. Only use this if your game server actually listens on TCP.

  • UDP echo challenge (udp-echo): Discovery sends a small UDP challenge; your server must echo the exact bytes back. Proves both reachability and that the heartbeat came from the machine that owns the endpoint. Needs a responder of roughly ten lines (below).

Open-registration apps always use the UDP echo challenge

On an app with open registration (see Discovery Overview), Verification is locked to UDP echo challenge and cannot be changed. The platform rejects any other combination, and switching an existing app to open registration locks the setting automatically.

The reason: on an open app the heartbeat token ships inside the game binary and is public, so token secrecy cannot protect the list. Proving that a sender actually controls the endpoint it advertises is what keeps fabricated entries away from your players, so it is not optional in this mode. Every client-hosted server must run the echo responder below.

Servers hosted on PingCore

Verification applies to heartbeat integrations, meaning servers you host elsewhere. Servers hosted on PingCore through a fleet are platform-vouched: the platform allocated their endpoints and health-checks their processes, so Discovery lists them under every verification mode without probing them. The live server table on the app page marks them as hosted instead of showing a verification result.

How probing behaves

  • Probes run asynchronously and never block heartbeat ingest.

  • A server is probed on its first heartbeat, then re-probed every 5 minutes.

  • The probe timeout is 3 seconds.

  • While a server is not verified (and the mode is not none), it keeps accepting heartbeats but is hidden from the public list.

  • The heartbeat response is your diagnostic surface: verified reports pending, verified, or unverified, and lastProbeError carries the reason for the last failure. Log it on your servers so misconfigurations show up in your own logs.

TCP connect checklist

TCP mode needs no responder code. Most failures come from the network path:

  • Your game server must already be listening on the advertised port over TCP. If it is UDP-only, use the UDP echo challenge instead.

  • The advertised ip:port must be reachable from the public internet, not just your LAN. Port-forward it on the router or open it in the cloud security group.

  • Host firewalls block inbound by default. Allow the port in Windows Defender Firewall or ufw/iptables.

  • If you sit behind NAT and advertise a different external port than the one your process binds, send the externally reachable port in the heartbeat.

UDP echo challenge: the DSCV1 wire spec

The format is a fixed wire contract shipped in studio code, so implement it exactly:

  1. Discovery sends a single UDP datagram to the advertised endpoint: ip:queryPort when your heartbeat includes a queryPort, otherwise ip:port.

  2. The datagram is exactly 21 bytes: the 5 ASCII bytes DSCV1 followed by a 16-byte random nonce.

  3. Your server sends the exact same 21 bytes straight back to the source address, from the same socket that received them.

  4. A reply carrying a different nonce is rejected, so an off-path attacker cannot forge verification for someone else's endpoint.

  5. No reply within the probe timeout means unverified: the server keeps heartbeating but stays hidden from the public list until a later probe succeeds.

import dgram from 'node:dgram';

const MAGIC = Buffer.from('DSCV1', 'ascii');

export function runEchoResponder(port) {
  const socket = dgram.createSocket('udp4');

  socket.on('message', (message, remote) => {
    // 5 magic bytes + 16 byte nonce, echoed back verbatim.
    if (message.length !== 21) return;
    if (!message.subarray(0, MAGIC.length).equals(MAGIC)) return;

    socket.send(message, remote.port, remote.address);
  });

  socket.bind(port);
}

Many games can hook the responder into the socket they already use for queries: check the first five bytes, echo if they match, otherwise fall through to your normal query handling.

What verification does not prove

Verification proves the endpoint answers Discovery. It does not prove every player's network route works. A server can be reachable from Discovery's vantage point and still be blocked for some players by their ISP, a regional firewall, or a broken route. Verification filters out dead listings; it does not guarantee that every individual player can connect.