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:
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,900to 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.