radicle-reticulum/README.md

6.1 KiB

radicle-reticulum

Bridges Radicle (decentralized Git) over Reticulum mesh networking — LoRa, packet radio, serial, I2P, and more. Enables offline-first code collaboration without internet infrastructure.

Why: Radicle requires publicly reachable seed nodes; Reticulum routes over any physical medium. Both use Ed25519 keys — a natural fit.


Prerequisites

Both machines need:

  • Radicle (rad CLI + radicle-node)
  • uv (Python package manager)
  • Git

Install

On each machine, clone this repo and install:

git clone rad:z4NMdcKbw2TETQ56fbQfbibFHtZqZ   # via radicle
# or: git clone https://github.com/youruser/radicle-reticulum
cd radicle-reticulum
uv sync

Setup: connect two machines over mesh

Do this once on each machine.

Step 1 — Configure radicle-node to listen on localhost

Edit ~/.radicle/config.json, find the "node" section, set "listen":

"node": {
    "listen": ["127.0.0.1:8776"],
    ...
}

Then (re)start radicle-node:

rad node start
rad node status   # confirm: "listening for inbound connections on 127.0.0.1:8776"

Step 2 — Start the bridge on both machines

Run on each machine:

uv run radicle-rns bridge

Within ~30 seconds the bridges discover each other via RNS announce, connect automatically, and register each other's NIDs with radicle-node. You'll see:

[+] Discovered bridge: <hash>  (NID: z6Mk...)
[Status] Tunnels: 0, Remote bridges: 1, TX: 0, RX: 0

Once radicle-node connects through the bridge, tunnels open automatically:

Tunnel 1 opened / Incoming tunnel 1 opened
[Status] Tunnels: 1, Remote bridges: 1, TX: 1551, RX: 1831

Bytes in TX/RX confirm radicle gossip is flowing over the mesh.

LoRa note: The bridge re-announces at t+5s, t+15s, t+30s after startup. On LoRa, use --announce-retry-delays 60,300,900 to respect duty cycle limits.


Share a repository

Machine A — init and push a repo

mkdir myproject && cd myproject
git init
git commit --allow-empty -m "init"
rad init --name myproject --description "my project" --default-branch main

rad init prints the repository ID (rad:z3...). Share it with Machine B.

# make a commit and push
echo "hello" > hello.txt
git add . && git commit -m "hello"
rad push

Machine B — clone it

rad clone rad:z3...     # use the RID from Machine A
cd myproject
cat hello.txt           # hello

Machine A — fetch updates from Machine B

# Machine B: edit, commit, push
echo "world" >> hello.txt
git add . && git commit -m "world"
rad push

# Machine A:
rad fetch
git pull

Commands

radicle-rns bridge                        # TCP↔RNS bridge (main command)
radicle-rns node                          # lightweight peer-announce node
radicle-rns peers                         # discover peers on the mesh
radicle-rns ping <hash>                   # RTT probe to a peer
radicle-rns identity generate             # create/show identity
radicle-rns sync <repo>                   # LXMF store-and-forward sync
radicle-rns bundle create <repo>          # pack repo into a bundle file
radicle-rns bundle apply <bundle> <repo>  # unpack a bundle into a repo
radicle-rns bundle info <bundle>          # inspect bundle metadata
radicle-rns bundle qr-encode <bundle>     # print ASCII QR (≤2953 bytes)
radicle-rns bundle qr-decode <image.png>  # decode QR back to bundle

Global flags: -v verbose logging, --identity PATH (default ~/.radicle-rns/identity).

Bridge flags

Flag Default Description
-l, --listen-port 8777 TCP port radicle-node connects to
--radicle-port 8776 Port radicle-node listens on
-c, --connect <hash> Connect to a remote bridge by RNS hash
--nid <NID> auto-detect Override local radicle NID
--no-auto-connect Disable auto-connect on discovery
--no-auto-seed Disable auto-registering remote NIDs

Air-gapped / QR transfer

For links too slow even for Reticulum, transfer tiny incremental bundles via QR code (max 2953 bytes):

# Sender — create a small incremental bundle and encode it
uv run radicle-rns bundle create ./myrepo --incremental --basis myrepo.refs.json
uv run radicle-rns bundle qr-encode myrepo-*.radicle-bundle   # prints ASCII QR to terminal

# Receiver — photograph the QR, then decode and apply
uv run radicle-rns bundle qr-decode qr-photo.png -o received.radicle-bundle
uv run radicle-rns bundle apply received.radicle-bundle ./myrepo

QR image output (PNG) and image decoding require optional deps:

uv sync --extra qr        # qrcode for PNG output
pip install pillow pyzbar  # for qr-decode from image file

Architecture

radicle-node ──TCP:8777── RadicleBridge ──RNS Link── RadicleBridge ──TCP:8776── radicle-node
    (Machine A)               (Machine A)                (Machine B)               (Machine B)
  • Identity (identity.py) — Ed25519 DID ↔ RNS destination; saved to ~/.radicle-rns/identity
  • Bridge (bridge.py) — TCP↔RNS tunnel, announces itself, discovers peers
  • SyncManager (sync.py) — LXMF store-and-forward bundles; auto-pushes on refs announce
  • AdaptiveSyncManager (adaptive.py) — selects FULL/INCREMENTAL/MINIMAL/QR by RTT + throughput
  • GitBundle (git_bundle.py) — full and incremental Git bundles for delay-tolerant transfer
  • QR (qr.py) — visual air-gap transfer for tiny bundles

Development

uv run pytest        # 158 tests
uv run pytest -x -q  # stop on first failure

Reticulum interfaces

On the same LAN, Reticulum auto-discovers peers via UDP multicast — no config needed. For LoRa / serial / I2P, edit ~/.reticulum/config:

[[lora_interface]]
  type = RNodeInterface
  port = /dev/ttyUSB0
  frequency = 868000000
  bandwidth = 125000
  spreadingfactor = 7
  codingrate = 5

See Reticulum docs for the full interface list.