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.
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
| Kind | Direction | Purpose | Signed by |
|---|---|---|---|
| REGISTER | node → registry | Bind/refresh name→key; heartbeat keeps the NAT mapping warm | node |
| REGISTER_ACK | registry → node | Confirms binding; echoes the observed public endpoint (STUN-lite for free) | — (v0.2: registry) |
| RESOLVE | node → registry | Query a target name | node |
| RESOLVE_RESP | registry → node | Target's public key + live endpoint (fresh < 60 s only) | — (v0.2: registry) |
| INTRO | node → registry | Request rendezvous with a target | node |
| PUNCH | registry → target | "Requester at endpoint E wants in — punch back" | — (v0.1 gap, §6.4) |
| PING | node → node | Liveness probe carrying a fresh nonce | sender |
| PONG | node → node | Echoes the nonce; proof of name ownership | responder |
| ERROR | registry → node | Rejection 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)
- A sends
INTRO(target=B)to the registry. - The registry forwards
PUNCH(name=A, endpoint=A_observed)to B's last endpoint. - 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.
- 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
- Registry responses are unsigned.
RESOLVE_RESPandREGISTER_ACKcan be forged by an on-path attacker, enabling endpoint poisoning. Crucially this does not enable impersonation — a poisoned endpoint still cannot produce pongs that verify against the genuine key — but it enables denial of service and traffic misdirection. Fix: registry keypair + signed responses (v0.2). PUNCHis unsigned. A spoofed punch cue can induce a node to emit a small burst of datagrams to an arbitrary address — a minor reflection primitive. Fix folds into signed registry traffic (v0.2).- The registry is a single point of failure and a metadata observer. It learns who resolves whom, and its unavailability halts new rendezvous (established peer paths keep working). Federation and a signed, replicated name table are the v0.3+ answer.
- FCFS is squatting-friendly. Nothing stops early registration of desirable names. Acceptable for an experiment; a production DNX needs namespace delegation anchored to domain ownership proofs.
- No payload encryption. §6.2. This is the headline item of v0.2.
§7Constants
| Constant | Value | Rationale |
|---|---|---|
| heartbeat | 15 s | Below common ~30 s NAT UDP mapping timeout |
| freshness | 60 s | Missed-heartbeat tolerance before a name reads offline |
| prune | 600 s | 10× freshness; dead records leave the table |
| replay window | ±30 s | Clock-skew tolerance vs. replay surface |
| ping retry | 300 ms / 8 s | Covers traversal establishment within one attempt |
| registry port | 4400/udp | Reference 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.
registry.dnxroute.com. Where this spec and the code disagree, the code is
the bug.