Node Discovery and Encrypted Multi-Hop Relay

AeroNyx10 min read

How AeroNyx nodes discover each other with signed descriptors, keep a mirrored Directory Chain, and forward layered encrypted payloads hop by hop. Real chat uses the central relay by default; the two-hop onion route is opt-in.

AeroNyx nodes find each other through signed, expiring descriptors and can forward end-to-end encrypted payloads hop by hop, with each relay removing only its own layer of encryption. Today, real user chat goes through the central AeroNyx relay (RelayWS) by default. Routing chat through nodes is an opt-in setting in the app.

What runs today

CapabilityStatus
Signed node discovery and peer gossipRunning on production nodes
Directory Chain (per-node signed descriptor history, mirrored between nodes)Running on production nodes
Two-hop onion relay between nodesRunning on production nodes. In the app it is opt-in and off by default
Anonymous mailboxes reached over the onion relayWorking on production nodes. Ordinary chat does not use them. The app has a separate beta mailbox drop
Routes with three relay hopsThe policy and probe exist in the node source. The app does not use them
https:// hostname endpoints for nodes behind a gatewayIn current node releases
Identity-bound TLS on the public API port, with no domain name or certificate authorityRunning on production nodes. Nodes use it between each other when a peer advertises it. App support is in development
Onion terminal hand-off to the receiver's relay inboxRunning on production nodes
Operator-pinned seeds (discovery.pinned_seeds)In current node releases
Default chat transportThe central RelayWS service operated by AeroNyx

Centralized and decentralized paths run side by side.

How discovery works

Each node publishes a descriptor signed with its Ed25519 identity key. A descriptor includes the node ID, issue and expiry times, an optional public endpoint, capability flags (such as onion middle relay or chat relay), coarse capacity hints and the key used for onion encryption. Nodes reject descriptors that are expired or carry an invalid signature.

  • Gossip is bounded. Peer exchange is rate-limited and policy-gated, and messages have size limits.
  • Restart recovery. A node keeps a local peer cache and falls back to configured seed endpoints after a restart. Operators can also pin seeds.
  • Route selection. Only fresh descriptors with a usable endpoint and onion key are considered as relay candidates.

Which endpoints a descriptor may name

A signature proves who published a descriptor, not that its endpoint is safe to contact. A node therefore accepts only two forms of endpoint:

  • a public IP address over http or https; loopback, private, link-local, CGNAT, documentation, multicast and reserved ranges are rejected;
  • an https:// URL with a public DNS name. Single-label names, localhost and private suffixes such as .local, .internal and .home.arpa are rejected.

Hostnames are resolved at connect time through a resolver that drops every non-public address, so a name cannot be pointed at the node itself, a private network or a cloud metadata service. Peer clients also ignore proxy settings and never follow redirects.

The hostname form exists so that nodes without an IP address of their own, such as a TEE node behind a cloud gateway, can take part.

Pinned seeds

A node that starts without an operator snapshot trusts only identities it already holds, so it can end up trusting only itself. For that case an operator can name the identity expected at an address:

toml
[discovery]
pinned_seeds = ["<64-hex node id>@<public ip>:<port>"]
  • A pinned identity is accepted for the first time only after an exchange over identity-bound TLS pinned to that identity.
  • Every later descriptor of a pinned identity, from any source, must keep the pinned address and advertise identity-bound TLS.
  • Trust does not travel: other descriptors in a seed's answer remain untrusted candidates.
  • A removed pin cannot come back from an old cache or backup.

The Directory Chain

Each node also keeps a signed, hash-linked history of its own descriptor events, called its Directory Chain. Other nodes can mirror that history, fetch descriptor objects and request inclusion proofs for a specific descriptor. Operator-pinned peers can sign observation checkpoints over the histories they have seen.

There is no global ledger and no single coordinator: each node signs its own history, and others mirror and check it.

Identity-bound TLS

Reaching a node over HTTPS normally needs a domain name and a certificate from a certificate authority, and most AeroNyx nodes are reachable only by IP address. Instead, a node proves its own HTTPS endpoint with its Ed25519 identity.

  • How it works. The node creates an ordinary ECDSA P-256 TLS key and a self-signed certificate. One subject alternative name carries a binding, aeronyx-tls-binding:v1:<node id>:<expiry>:<signature>, where the signature is made by the node identity key over a domain-separated digest of the certificate's own public key, the node ID and the expiry. The TLS 1.3 handshake proves the node holds the certificate key. The identity key never signs a handshake.
  • What a client checks. A verifier accepts a certificate only for the node it meant to reach: exactly one binding, the expected node ID, not expired, at most 31 days of validity left, and a valid signature over this certificate's key. A certificate issued by a certificate authority does not satisfy this check.
  • Same port. The public API port (8422/TCP) serves both: a connection that starts with a TLS handshake gets TLS, and anything else gets plain HTTP as before. It is on by default (discovery.public_api_identity_tls).
  • Between nodes. A node whose signed descriptor advertises the protocol feature anpf1-ibt1 serves identity-bound TLS. A peer that sees the feature must reach that node over TLS pinned to its identity and never falls back to plain HTTP. The feature is covered by the descriptor signature, so a network attacker cannot strip it to force a downgrade. If several identities claim the same endpoint, no TLS address is produced and the request fails closed. Older nodes without the feature, and DNS-named gateway endpoints, keep their previous behavior.
  • Reference. Source and frozen test vectors: aeronyx-node-tls.

The effect is that the route information of onion hops, gossip, directory sync and witness traffic between nodes is no longer readable on the wire. App support for connecting to nodes this way is in development.

How multi-hop relay works

  1. The sender wraps the payload in one layer of encryption per hop, using each relay's published onion key.
  2. Each relay checks the previous hop's signature, timestamp and replay state, removes its own layer and forwards the remaining opaque blob. It never sees the inner layers.
  3. The final (terminal) node hands the message to the receiver's relay inbox (the central relay by default) and, only after the relay accepts it, returns a signed receipt. The receipt binds the route ID, a commitment to the payload, the terminal node's identity and the delivery time. It proves the message was accepted into relay custody, not that the receiver has read it. A node with the hand-off turned off keeps the message in its own store and signs the same kind of receipt, so the receipt alone does not tell the two cases apart.

What each party sees

PartySeesDoes not see
Entry nodeThe sender's network address, a sealed layer and the next hopWho the message is for, and its contents
Terminal nodeThe sender and recipient keys and the ciphertextThe sender's network address, and the contents
Central relay (receives the hand-off)What any send shows it: sender and recipient keys, timestamps and sizesThe sender's network address, and the contents

No single node links your network address to who you are writing to. The terminal node and the central relay both see who wrote to whom.

The protocol accepts at most three verified relay hops. The app's privacy route uses two. Abuse limits on relays are described in Blind Relay Abuse Guard.

Chat in the app

  • Default: messages go through the central RelayWS service. Message content is end-to-end encrypted, but the service handles delivery metadata. See Chat relay client integration.
  • Privacy route (opt-in, off by default): when it is on and a route is ready, the app sends one-to-one messages over the two-hop onion route first. If no onion route accepts the message, the app holds it on the device and does not use the standard relay unless you approve that for the message.
  • Anonymous mailbox (beta): a separate drop that works only while connected to the privacy network. Ordinary conversations do not use it.

Anonymous mailbox pull sizes

A mailbox pull result used to be one fixed image of 163,130 bytes whatever the item, so a short message cost a response of about 160 KiB. Pull version 2, negotiated by the signed protocol feature anpf1-ampc1, pads the sealed item to the smallest of five size classes (4,204, 16,492, 65,644 or 131,180 bytes, or the codec maximum). A 4,096-byte item now travels as 4,518 bytes.

TEE node

One production node runs inside a Phala Cloud trusted execution environment (dstack on Intel TDX). It serves only as an anonymous mailbox and onion relay: its VPN is disabled, MemChain is off, and its identity key comes from the dstack key service. It is reachable only through a TLS hostname on the gateway, which is why hostname endpoints were added. The app does not currently verify the node's TEE attestation.

Live status

Aggregate network status is published at:

text
GET https://api.aeronyx.network/api/privacy_network/vpn/public/network-stats/

Relay and discovery aggregates appear under data.protocol_status, for example peer_store.blind_relay and peer_store.peer_quorum. Note two points when reading them:

  • Node-level aggregates count only nodes with a heartbeat in the last 120 seconds, so an offline node drops out of counts such as protocol_status.reported_nodes. network.total_nodes and network.vpn_nodes still count registered active nodes, online or not.
  • Synthetic probe counters (probe_*, two_hop_probe_*) measure path health. They are not user traffic.

The public response contains only counts, status buckets and timestamps. It never includes node IDs, endpoints, route IDs, payloads, recipients, client IP addresses, DNS data or social-graph edges. See Network stats and privacy boundary.

<!-- faq:start -->

Frequently asked questions

Do my AeroNyx chat messages go through decentralized nodes?

Not by default. Messages go through the central RelayWS service. You can turn on the privacy route in the app, which sends one-to-one messages over a two-hop onion route through nodes when one is ready. If no route accepts a message, the app holds it until a route is available or you approve sending that message through the standard relay.

Can a relay node read the messages it forwards?

No. A relay removes only its own encryption layer and forwards an opaque blob. Message content is also end-to-end encrypted between the people in the conversation.

What is the Directory Chain?

The Directory Chain is each node's own signed, hash-linked history of its descriptors. Other nodes mirror it and can request inclusion proofs. It has no global coordinator.

How can a node behind a gateway join the network?

It can publish an https:// hostname as its endpoint. Other nodes resolve that name at connect time and use only public addresses, so the name cannot be redirected to private or internal targets.

Why does the public stats page show more registered nodes than online nodes?

network.total_nodes and network.vpn_nodes count registered active nodes whether or not they are online. Compare them with network.online_vpn_nodes. The protocol and forwarding counts only include nodes with a fresh heartbeat.

Do nodes need a domain name or a certificate authority for HTTPS?

No. A node can prove its HTTPS endpoint with its own identity key: its TLS certificate carries a signature by the node identity over the certificate's key, and a client or peer that knows the node ID can verify it. Nodes behind a gateway can still use an https:// hostname instead.

What does a signed delivery receipt prove?

It proves that the terminal node accepted the message into relay custody, in practice the receiver's relay inbox. It does not prove that the receiver has seen or read the message. Use the delivery and read receipts of the chat protocol for that.

<!-- faq:end -->