
reticulum-js aims to produce a dependency-free JavaScript implementation of the Reticulum Network System. Reticulum is a mesh networking stack designed for both local and wide-area networking. Reticulum applications can talk with each other over multiple different interfaces, ranging from TCP connections to Lora radio. This allows building offline-first collaborative software that can work even when Internet infrastructure is compromised.
Reticulum gives several building blocks for enabling applications and users to communicate with each other:
fetchOn top of these, Reticulum handles peer discovery (via the Announce mechanism) and encryption and content signing (via Identities).
Read the Zen of Reticulum for more information.
bz2 implementation if you deal with compressed resources)The web platform has a very strong commitment and tradition for backwards compatibility. The fact that this Reticulum implementation relies only on features of the web platform means it will likely remain functional and maintainable for years or even decades to come.
Early stages, but we are able to send and receive LXMF messages, and make NomadNet page requests. At this point the aim is for JavaScript applications to be able to be leaf nodes in a Reticulum mesh, meaning that they will not route traffic for others. Full capability of acting as a transport node would be great to have and is on the roadmap.
The canonical source of reticulum-js is the rngit repository. To fetch the latest release, run:
rngit release rns://adafb3153efd4d96d532568a5208b3b5/reticulum/reticulum-js fetch latest:all
npm install reticulum-js-*.tgz
You can also get reticulum-js from NPM:
npm install reticulum-js
The quickest way to use Reticulum.js is to connect to a Reticulum transport
node (for example, the local rnsd daemon over TCP), set up an identity, and
exchange LXMF messages:
import {
fromHex,
Identity,
LXMessage,
LXMRouter,
Reticulum,
TCPClientInterface,
toHex,
} from "reticulum-js";
// 1. Start the engine and connect to a transport node over TCP
const rns = new Reticulum();
const tcp = new TCPClientInterface({ host: "127.0.0.1", port: 42424 });
await tcp.connect();
rns.addInterface(tcp, true);
// 2. Create an identity for this peer (persist it between runs in real apps)
const identity = await Identity.generate();
identity.setAppData("reticulum-js example");
// 3. Register the standard LXMF delivery destination and announce our presence
const lxmf = new LXMRouter(identity, rns);
await lxmf.init();
await lxmf.deliveryDest.announce();
console.log("My LXMF address:", toHex(lxmf.deliveryDest.destinationHash));
// 4. Receive incoming messages
lxmf.addEventListener("message", (event) => {
const { message } = event.detail;
console.log(`From ${toHex(message.sourceHash)}: ${message.content}`);
});
// 5. Send a message to a known peer LXMF address (16 bytes)
const outgoing = new LXMessage({
sourceHash: lxmf.deliveryDest.destinationHash,
destinationHash: fromHex("00112233445566778899aabbccddeeff"),
content: "Hello over Reticulum!",
});
await lxmf.send(outgoing, identity, null);
The destination address is the peer's
lxmf.deliverydestination hash. You typically learn it from an announce or out-of-band; substitute a real 16-byte hex address before sending.
A complete runnable version (with identity persistence and echo replies) lives
in examples/lxmf_echobot.js.
LXMF messages can also be delivered completely out-of-band — printed on
paper, photographed as a QR code, or shared as a string — using the paper
delivery method. A paper message is encrypted to the recipient exactly like a
propagated one, but instead of travelling over the network it is encoded as an
lxm:// URI that the recipient ingests later:
import { LXMFConstants, LXMessage, toHex } from "reticulum-js";
// Build the recipient's outbound lxmf.delivery destination from a recalled
// identity (typically learned from an announce).
const recipientOut = await Destination.OUT(
"lxmf.delivery",
DestType.SINGLE,
recipientIdentity,
rns,
);
const outgoing = new LXMessage({
sourceHash: lxmf.deliveryDest.destinationHash,
destinationHash: recipientOut.destinationHash,
content: "Scanned from a QR code!",
});
// Produce the lxm:// URI (e.g. render it into a QR code). The encrypted
// payload must fit within LXMFConstants.PAPER_MDU bytes (QR-code capacity).
const uri = await outgoing.toPaperUri(identity, recipientOut);
console.log(uri); // lxm://...
On the receiving side, feed the URI straight into the router:
lxmf.addEventListener("message", (event) => {
const { message } = event.detail;
console.log(`Paper message from ${toHex(message.sourceHash)}: ${message.content}`);
});
// `uri` was captured out-of-band (typed in, scanned, pasted).
const message = await lxmf.ingestUri(uri);
ingestUri decrypts with the local lxmf.delivery destination, dispatches the
usual message event, and de-duplicates by transient_id so scanning the same
QR code twice is harmless. You can also work at the LXMessage level directly
with toPaperData / fromPaperData / fromPaperUri.
Licensed under the EUPL 1.2.
This project is developed following Reticulum Distributed Development guidelines. The canonical source lives in rns://adafb3153efd4d96d532568a5208b3b5/reticulum/reticulum-js.
We also provide a sporadically updated GitHub mirror at https://github.com/bergie/reticulum-js.
Prior art includes Liam Cottle's rns.js and of course the Python Reticulum Reference Implementation itself. We have also benefited greatly from Salem Data's Reticulum Wire Specifications work.
This project has been built with the assistance of various LLMs, both for conceptual planning and implementation. I acknowledge that AI code is not necessarily ideal, but at the same time, I'm busy sailing.
Supporting the Reticulum Project is the best way to support. If you want to support this JavaScript library directly, here are some ways:
0xFC872bA86812B2bbe90c38cfD2553F7865d04094