DNX — Protocol Specification Category: Experimental Version: 0.1 · July 2026 ← dnxroute.com

DNX Protocol, v0.1

A name-native overlay protocol: fully-qualified domain names as addresses, ed25519 keys as identity, UDP as substrate. This document specifies the wire format, protocol flows, and security model of the running v0.1 implementation — including its limitations, stated plainly.

§1Design goals and non-goals

Goals. (1) Names are the only addresses humans and applications touch. (2) Identity is native to the address layer: a name is cryptographically bound to a keypair, and reaching a name yields proof you reached its owner. (3) Reachability is symmetric regardless of NAT: a machine behind an unconfigured residential router is as addressable as a public server. (4) Freshness: name records update in seconds, not TTL-hours.

Non-goals (v0.1). DNX does not replace IP forwarding. Packets are UDP/IP datagrams routed hop-by-hop by existing infrastructure; DNX is an overlay that makes the numeric layer invisible rather than absent. True name-based forwarding in routers is a research direction (cf. Named Data Networking), not a v0.1 claim. v0.1 also does not yet encrypt payloads — it authenticates them (§6).

§2Architecture

Three components. A registry (reference deployment: registry.dnxroute.com:4400/udp) maintains the live name table. A node agent (dnxd) runs on every participating machine: it holds the identity key, heartbeats, answers rendezvous cues, and exposes a loopback-only control API (127.0.0.1:4401) for local tools. A CLI (dnx) is a thin client of that local API.

One socket rule. Each node performs all DNX traffic — heartbeats, control queries, and peer traffic — over a single UDP socket. The NAT mapping created by heartbeats is therefore the same mapping peers punch toward. A second socket silently breaks traversal.

Identity. On first boot a node generates an ed25519 keypair; the key, not the name, is the machine's fundamental identity. Names are human-readable handles bound to keys at the registry, first-come-first-served (§6.1). Keys are stored locally (~/.dnx/identity.json, mode 0600) and never transmitted; only public keys and signatures cross the wire.

Bootstrap. The registry's own address is resolved once via legacy DNS. This is the single point where the old namespace appears; every subsequent operation is name-native.

§3Wire format

v0.1 transports JSON-encoded messages as single UDP datagrams (≤ 64 KiB). One envelope serves all message kinds; unused fields are omitted. JSON is a deliberate prototype choice — inspectable with tcpdump — and is slated for replacement by compact binary framing in v0.2.

{
  "kind":     "REGISTER",              // message verb, §4
  "name":     "computer1.internal.dnxroute.com",
  "target":   "",                      // FQDN being queried (RESOLVE/INTRO/PUNCH)
  "pubkey":   "base64(ed25519 pub)",   // identity, 32 bytes pre-encoding
  "endpoint": "203.0.113.7:44313",     // ip:port — control-plane only, registry-observed
  "ts":       1783312169042,           // unix milliseconds, replay window §6.3
  "nonce":    "base64url(12 bytes)",   // request/response matching + replay
  "sig":      "base64(ed25519 sig)",   // over the signing string below
  "info":     ""                       // free text (errors, diagnostics)
}

Signing. Signatures cover the canonical string "dnx1|" + kind + "|" + name + "|" + target + "|" + ts + "|" + nonce. The endpoint field is deliberately excluded: it is not asserted by the node but observed by the registry from the datagram's UDP source address. A node cannot lie about where it is, because it never says where it is.

§4Message kinds

KindDirectionPurposeSigned by
REGISTERnode → registryBind/refresh name→key; heartbeat keeps the NAT mapping warmnode
REGISTER_ACKregistry → nodeConfirms binding; echoes the observed public endpoint (STUN-lite for free)(v0.2: registry)
RESOLVEnode → registryQuery a target namenode
RESOLVE_RESPregistry → nodeTarget's public key + live endpoint (fresh < 60 s only)(v0.2: registry)
INTROnode → registryRequest rendezvous with a targetnode
PUNCHregistry → target"Requester at endpoint E wants in — punch back"(v0.1 gap, §6.4)
PINGnode → nodeLiveness probe carrying a fresh noncesender
PONGnode → nodeEchoes the nonce; proof of name ownershipresponder
ERRORregistry → nodeRejection with reason in info

§5Protocol flows

5.1 Registration & presence

Every node sends a signed REGISTER every 15 seconds — chosen to sit under typical NAT UDP mapping timeouts (~30 s). The registry verifies the signature against the message's own pubkey, enforces the binding rule (§6.1), stores the datagram's source address as the node's live endpoint, and stamps last_seen. Records unseen for 60 s are treated as offline; unseen for 10× that, pruned.

5.2 Resolution

A signed RESOLVE for a fresh name returns RESOLVE_RESP with the target's public key and endpoint. The key is the point: every subsequent packet from that peer can be verified against it. Resolution answers are useful for seconds, not hours — the next heartbeat may move the endpoint, and that is fine, because resolution is cheap.

5.3 Rendezvous (NAT traversal)

  1. A sends INTRO(target=B) to the registry.
  2. The registry forwards PUNCH(name=A, endpoint=A_observed) to B's last endpoint.
  3. B immediately fires several datagrams at A's endpoint. Each outbound packet installs a mapping in B's NAT permitting A's inbound traffic — and symmetrically, A's retrying pings open A's NAT for B.
  4. Within one round of retries (300 ms cadence, 8 s budget) the cones are open and traffic flows peer-to-peer. The registry is no longer on the path.

This succeeds against full-cone and port-restricted NATs. Symmetric NATs — which allocate a new mapping per destination — defeat it; v0.3 adds relayed fallback (§8).

5.4 Verified ping

The requester sends signed PINGs carrying a fresh nonce until a PONG echoing that nonce arrives. The pong is verified against the public key obtained at resolution — not against any key the responder claims. A verified pong therefore proves: the name resolved, the path works, and the responder holds the private key bound to that name. RTT is measured first-send to first-valid-pong, so a first ping includes traversal cost (observed: 396 ms cold, 49 ms established, cloud↔residential).

§6Security model & analysis

6.1 Name binding

First-come-first-served: the first valid REGISTER for a name binds it to that key permanently (for the record's lifetime). A REGISTER for a bound name under a different key is rejected regardless of signature validity — possession of a name requires possession of its key.

6.2 What a verified pong proves — and what it doesn't

It proves the responder holds the private key the registry associates with the name. It does not yet prove confidentiality: v0.1 payloads are authenticated but plaintext. Do not move secrets over DNX v0.1.

6.3 Replay resistance

Signed messages carry millisecond timestamps enforced within a ±30 s window, plus per-message 96-bit random nonces. Pong verification additionally requires echoing the requester's nonce, binding each pong to one live probe.

6.4 Known weaknesses in v0.1 — stated plainly

§7Constants

ConstantValueRationale
heartbeat15 sBelow common ~30 s NAT UDP mapping timeout
freshness60 sMissed-heartbeat tolerance before a name reads offline
prune600 s10× freshness; dead records leave the table
replay window±30 sClock-skew tolerance vs. replay surface
ping retry300 ms / 8 sCovers traversal establishment within one attempt
registry port4400/udpReference deployment

§8Roadmap

v0.2 — confidentiality and streams. X25519 key agreement authenticated by the existing ed25519 identities; ChaCha20-Poly1305 AEAD channels; registry-signed control responses; a minimal reliable stream layer; dnx tunnel exposing TCP services over DNX. Milestone: an SSH session into a NAT'd machine carried entirely over DNX.

v0.3 — resilience. Relayed fallback for symmetric NATs; registry federation; signed name-table replication. Beyond: namespace delegation via DNS-anchored ownership proofs; binary framing; the long game of pushing names down the stack.

Status. This document describes running code, not intent — every constant, field, and weakness above is present in the v0.1 implementation deployed at registry.dnxroute.com. Where this spec and the code disagree, the code is the bug.