Skip to content

Reticulum

Reticulum is a cryptographic networking stack that does not assume the internet. It routes by destination hash rather than address, every destination is a keypair, and it runs over anything that can move bytes.

MeshSat implements the Reticulum wire format directly, in Go, wire compatible with the reference Python implementation. A standard rnsd node treats a MeshSat bridge as a peer.

Why it is here

The bridge already speaks LoRa, satellite, SMS, packet radio and TCP. Reticulum is the layer that makes those one network instead of five: a destination announced over LoRa can be reached later over Iridium, without anybody reconfiguring a route, because the identity is the address.

Identity and addressing

An identity is two keys: X25519 for encryption and Ed25519 for signing, published as a 64 byte public key with the encryption key first. A destination address is the SHA-256 of the name hash and identity hash, truncated to 16 bytes.

ConstantValue
MTU500 bytes
Maximum payload464 bytes
Destination hash16 bytes
Signature64 bytes, Ed25519
Maximum hops128

The bridge's identity is persisted, so it survives a restart and peers keep their routes. GET /api/routing/identity returns it.

Interfaces

Reticulum runs over any of these, and over several at once:

InterfaceConfiguration
TCP serverMESHSAT_TCP_LISTEN=0.0.0.0:4242
TCP clientMESHSAT_TCP_CONNECT=host:4242
AX.25 over a KISS TNCMESHSAT_AX25_KISS_ADDR, MESHSAT_AX25_CALLSIGN
SMSMESHSAT_SMS_RETICULUM_PEER=+31600000000
Bluetooth LEMESHSAT_BLE_ADAPTER=hci0, MESHSAT_BLE_DEVICE_NAME
MQTTMESHSAT_MQTT_RETICULUM_BROKER, MESHSAT_MQTT_RETICULUM_TOPIC

Each is empty and therefore off by default. TCP uses HDLC framing, the same as the reference implementation's TCPServerInterface and TCPClientInterface.

MESHSAT_AX25_KISS_ADDR takes either a Direwolf TCP KISS address such as localhost:8001 or the literal gateway. Use gateway with a serial TNC: the device has one file handle and the APRS gateway already holds it, so Reticulum frames have to travel through that link rather than opening a second one.

Announces and the routing table

The bridge announces itself every MESHSAT_ANNOUNCE_INTERVAL seconds, 300 by default, and learns routes from the announces it hears. GET /api/routing/destinations lists what it knows.

Routes are chosen by cost first and hop count second:

InterfaceCost per message
mesh, ZigBee, APRS, AX.25, BLE, TCP, MQTT, webhook0
cellular (SMS)0.005
Iridium0.05

So a destination reachable over both LoRa and satellite is reached over LoRa, even if the satellite path is one hop shorter. Cheaper wins; among equal costs, fewer hops wins; a route that is neither cheaper nor shorter is ignored rather than flapping between two bearers.

Routes expire 30 minutes after the last announce that refreshed them. Announces are capped at 2% of an interface's bandwidth so a busy routing table cannot crowd out traffic on a slow link.

A link is an encrypted session between two destinations, established with an ephemeral key exchange. POST /api/links opens one, GET /api/links lists them, DELETE /api/links/{id} closes one.

Resources are the mechanism for transferring something larger than a packet: the sender advertises, the receiver requests parts, and the transfer is resumable. GET /api/resources, POST /api/resources/offer and GET /api/resources/{hash}/data are the endpoints, with GET /api/resources/stats for progress.

Connecting to the MeshSat Hub

The Hub runs a Reticulum node reachable at reticulum.meshsat.net:443. It is TLS with mutual authentication: the connection is refused without a client certificate signed by the Hub's own bridge certificate authority.

That certificate is issued per bridge from the Hub's Fleet page, and it arrives in the same provisioning bundle as the MQTT credentials. See connect a bridge. RNS speaks plain TCP rather than TLS, so a node connecting by hand needs a local TLS wrapper presenting the certificate and offering a plain port for RNS to use.

Peers

GET /api/routing/peers lists configured peers, with POST and DELETE /api/routing/peers/{addr} to manage them. GET /api/routing/config and PUT cover the announce interval and related settings, and GET /api/routing/floodable controls which interfaces may carry flooded traffic. Flooding an expensive interface is exactly as costly as it sounds, which is why it is per interface and off by default.

Made by MeshSat