Imagine this scenario: you’ve built a state-of-the-art web application, deployed it to a globally distributed Kubernetes cluster, secured it with the latest TLS standards, and connected it to a highly available managed database. It’s a masterpiece of modern engineering. But then, a construction crew down the street cuts a fiber optic line. Or a severe storm knocks out the local grid. Or, perhaps, you're building software for users in remote regions where cellular coverage is a luxury, not a given.
An application that requires an unavailable network can lose functionality. Local storage and alternative transports may reduce that dependency, but each has limits.
Reticulum is a networking stack for multiple configured transports, including suitable radio and IP interfaces. It does not require centralized IP address allocation for its own destinations; hardware, software, radio rules and interface setup still matter.
As developers, we need to start thinking about the "offline-first" and "local-first" movements not just as frontend sync strategies, but as fundamental networking paradigms. In this post, we’re going to dive deep into what Reticulum is, how its cryptography works under the hood, and how you can start writing code for this decentralized mesh network today.
What is Reticulum? (And Why It's Not Just Another VPN)
Reticulum provides its own destination and transport model, rather than an IP VPN alone. It can run over IP or suitable non-IP interfaces; it does not transparently turn existing TCP/IP applications into off-grid applications.
To understand Reticulum, we have to look at its core design constraints:
- Less address administration: applications use destinations rather than assigned IP subnets. Interfaces and transport behavior still need configuration.
- No Central Authorities: There are no DNS roots, coordinate servers, or directory authorities.
- Encryption depends on destination type: SINGLE destinations and Links encrypt payloads; PLAIN destinations permit unencrypted local broadcasts. Encryption is not a guarantee of anonymity.
- Multiple media: low-bandwidth links are supported, but delay, payload limits, interference and capacity affect application design. They do not behave like fast Ethernet.
Applications address Destinations. Their hashes incorporate the application name/aspects and, for identity-based destinations, identity information; a destination is not simply the identity’s public key.
Cryptographic foundations: identity and destination addressing
In traditional networking, if I want to send a packet to sysseder.com, I have to trust a chain of DNS servers to resolve that name to an IP address, and then trust BGP routers to send my packets to the right physical server. Along the way, any router can inspect my destination, and if I'm not using HTTPS, my payload as well.
Reticulum flips this model on its head by borrowing concepts from public-key cryptography. Here is how addressing works:
- A Reticulum Identity contains separate keypairs: X25519 for encryption/key exchange and Ed25519 for signing. The combined private-key material includes a 256-bit key for each role.
- Destination addresses are 16-byte (128-bit) truncated hashes, normally shown as 32 hexadecimal characters; the 80-bit statement does not describe these addresses.
- The inspected documentation describes X25519, Ed25519, HKDF and AES-256-CBC with HMAC-SHA256. A single encrypted packet and an established Link have different exchanges; do not substitute a homemade crypto implementation.
Cryptographic verification ties a destination to its keys under the protocol’s assumptions. It does not make spoofing mathematically impossible, identify a human owner or protect a compromised endpoint; verify the intended address through a trusted channel.
Understanding the Reticulum Stack
To write applications for Reticulum, it helps to visualize how it compares to the traditional OSI model. Reticulum condenses several layers into a unified framework:
+-------------------------------------------------+ | Application Layer | | (LXMF, Nomad Network, Custom Apps) | +-------------------------------------------------+ | Reticulum API / Lib | | (Resources, Channels, Packets, Link Requests) | +-------------------------------------------------+ | Reticulum Core | | (Routing, Cryptography, Interface Management) | +-------------------------------------------------+ | Physical Interface | | (Ethernet, WiFi, LoRa, Serial, AX.25) | +-------------------------------------------------+
Instead of managing raw sockets, developers interacting with the Reticulum SDK work with three primary abstractions:
- Single Packets: Best for small, fire-and-forget telemetry, sensor data, or pings.
- Links: encrypted bidirectional sessions with additional APIs. Raw packets on a Link are not a TCP byte stream; use the appropriate Channel, request or Resource abstraction for the delivery behavior needed.
- Resources: larger transfers with segmentation and integrity handling. Completion still depends on available connectivity and resources; transfers can fail.
Getting Practical: Building a Reticulum Service in Python
The reference implementation is Python. The following illustrative echo examples were statically checked against the documented API, not executed. Restrict any future trial to an authorized, nonsensitive lab.
First, you'll need to install the Reticulum SDK:
pip install rns
A default configuration may be created at ~/.reticulum/config. AutoInterface uses IPv6 link-local multicast discovery and UDP transport; firewalls or Wi-Fi client isolation can prevent discovery. Review selected interfaces before initializing RNS.
Step 1: Creating a Receiver (The Service)
This lab echo service accepts small nonsensitive messages and replies without logging their contents. Save as server.py. Its generated identity is temporary, so its address changes after restart; a real service needs secure identity storage, authorization and rate limits.
import RNS
import time
APP_NAME = "echo_service"
def packet_received(data, packet):
# Educational lab: bounded, nonsensitive payloads only.
if len(data) > 128:
return
RNS.Packet(packet.link, b"Echo: " + data).send()
def link_established(link):
link.set_packet_callback(packet_received)
def main():
rns = RNS.Reticulum()
identity = RNS.Identity() # Temporary: address changes on restart.
destination = RNS.Destination(identity, RNS.Destination.IN,
RNS.Destination.SINGLE, APP_NAME, "echo")
destination.set_link_established_callback(link_established)
destination.announce()
print(destination.hash.hex())
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()
Step 2: Creating the Client
Now, let's write a client that will connect to our echo service using its destination hash. Save this as client.py. Note that you will need to copy the Destination Address printed by the server and paste it into the script, or pass it as an argument.
import sys
import time
import threading
import RNS
APP_NAME = "echo_service"
done = threading.Event()
def received(data, packet):
if data == b"Echo: Hello, lab!":
print("Expected echo received")
packet.link.teardown()
done.set()
def established(link):
RNS.Packet(link, b"Hello, lab!").send()
def main():
if len(sys.argv) != 2:
raise SystemExit("Usage: python client.py DESTINATION_HEX")
try:
target = bytes.fromhex(sys.argv[1])
except ValueError:
raise SystemExit("Invalid hexadecimal address")
if len(target) != 16:
raise SystemExit("Address must contain 16 bytes")
rns = RNS.Reticulum()
deadline = time.monotonic() + 30
if not RNS.Transport.has_path(target):
RNS.Transport.request_path(target)
while not RNS.Transport.has_path(target):
if time.monotonic() >= deadline:
raise SystemExit("No path before lab timeout")
time.sleep(0.1)
identity = RNS.Identity.recall(target)
if identity is None:
raise SystemExit("No verified identity for target")
destination = RNS.Destination(identity, RNS.Destination.OUT,
RNS.Destination.SINGLE, APP_NAME, "echo")
if destination.hash != target:
raise SystemExit("Destination name or identity mismatch")
link = RNS.Link(destination)
link.set_packet_callback(received)
link.set_link_closed_callback(lambda link: done.set())
link.set_link_established_callback(established)
if not done.wait(30):
print("No completed echo before lab timeout")
link.teardown()
if __name__ == "__main__":
main()
Running the Demo
Open two terminal windows on your machine:
- In terminal 1,
python server.pyprints a 32-character hexadecimal destination. Use only the actual address from your authorized lab server. - In terminal 2, use
python client.py DESTINATION_HEX, substituting the full 32-character address.
If discovery and link setup succeed, the intended result is an echo followed by teardown. This has not been observed here. Timeouts, blocked interfaces and missing transport nodes can prevent it; a local demo is not an off-grid resilience test.
Architecture: configured transport nodes and path discovery
A transport-enabled node B can relay between reachable A and C if the required interfaces, announcements and path state exist. This does not create radio coverage or an alternate route where none exists.
RNS.Transport.request_path(target_hash) requests path discovery. Announcements provide destination information used to learn paths; discovery and caching are protocol-controlled, not a promise that every query gets a reply.
For encrypted Links, relays are not intended to possess endpoint payload keys. Observers may still correlate timing, radio activity, topology or application identifiers; encryption does not make a sender untraceable.
Where Can Developers Apply Reticulum Today?
While Reticulum might seem like an edge case for preppers or amateur radio enthusiasts, its engineering implications are vast:
- Remote telemetry: suitable radio hardware can support bounded sensor messages where a planned radio link exists. Coverage, power, duty-cycle rules and failure recovery require independent design and testing; no self-healing guarantee follows.
- Prepared local communications: a preconfigured, powered and tested network may support local tools during an outage. It is not a replacement for emergency services or a network that appears automatically.
- Privacy-aware design: limited source addressing is one protocol property, not assurance of metadata privacy. Evaluate endpoint logs and observation risks; do not rely on this tutorial for personal safety or anonymity.
Conclusion & Call to Action
Reticulum offers another transport model for applications designed around it. Resilience, security and user control depend on the complete deployment, maintenance and threat model, not on adopting a protocol alone.
The next time you are architecting a mobile app, an IoT backend, or a local-first platform, ask yourself: "How would this run if the internet went down?"
Start with the official Reticulum website and documentation. Review dependencies and interfaces before any authorized lab trial; the examples here are unexecuted and not production services.
Have you experimented with mesh networks or off-grid communications? What are your thoughts on shifting away from the IP stack for localized applications? Let’s chat in the comments below!