radicle-reticulum/README.md

4.3 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.

Install

pip install uv   # once
uv sync          # install deps into .venv

QR encoding (optional):

uv sync --extra qr          # ASCII QR output
pip install pillow pyzbar   # image encode/decode

Quickstart: bridge two machines over mesh

Machine A (or any node on the mesh):

uv run radicle-rns bridge
# prints: RNS address: <HASH>

Machine B:

uv run radicle-rns bridge --connect <HASH-FROM-A>

Both machines — configure radicle-node to use the bridge as a seed:

# ~/.radicle/node/config.toml
[[seeds]]
address = "127.0.0.1:8777"

Then start radicle-node and use it normally:

rad node start
rad clone rad:z3xyz...
rad push / rad pull

The bridge auto-detects your local NID via rad self and auto-registers discovered remote NIDs with radicle-node (--no-auto-seed to disable).

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 a repo into a bundle
radicle-rns bundle apply <bundle> <repo> # unpack a bundle
radicle-rns bundle info <bundle>         # inspect a bundle
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, --identity PATH (default ~/.radicle-rns/identity).

Air-gapped / QR transfer

For truly offline transfers (tiny incremental bundles ≤ 2953 bytes):

# Sender
radicle-rns bundle create myrepo --incremental --basis prev.refs.json
radicle-rns bundle qr-encode myrepo-*.radicle-bundle

# Receiver (photograph the QR, then:)
radicle-rns bundle qr-decode qr-photo.png -o received.radicle-bundle
radicle-rns bundle apply received.radicle-bundle ./myrepo

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> Manually connect to a remote bridge
--nid <NID> auto Override local radicle NID
--no-auto-connect Disable auto-connect on discovery
--no-auto-seed Disable auto-registering remote NIDs

Architecture

radicle-node ──TCP:8777── RadicleBridge ──RNS Link── RadicleBridge ──TCP:8776── radicle-node
                               │                           │
                          RNS announce               RNS announce
                          (auto-discovery)           (auto-discovery)
  • Identity (identity.py) — Ed25519 DID ↔ RNS destination mapping; persisted to ~/.radicle-rns/identity
  • Adapter (adapter.py) — peer discovery via RNS announces
  • Link (link.py) — buffered RNS Link with state machine
  • SyncManager (sync.py) — LXMF store-and-forward bundles; auto-push on refs announce
  • AdaptiveSyncManager (adaptive.py) — picks FULL/INCREMENTAL/MINIMAL/QR by RTT + throughput
  • GitBundle (git_bundle.py) — full and incremental Git bundles
  • 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

Reticulum auto-discovers local peers via UDP multicast. For LoRa / serial / I2P, configure ~/.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.