radicle-reticulum/README.md

6.8 KiB
Raw Blame History

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.


Prerequisites


Install

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

Optional: faster push detection (inotify-based, Linux/macOS):

uv sync --extra watch

Two modes

Bridge mode — peer-to-peer

Each machine runs a bridge. They discover each other over RNS, then radicle-node syncs through the tunnel as if both nodes were on the same network.

Seed mode — always-on relay

One machine runs a dedicated seed radicle-node plus a bridge. Other machines' bridges discover and register the seed automatically. The seed self-populates: it calls rad seed <RID> for any repo announced by gossip peers.


Quick start: bridge mode

Step 1 — Configure radicle-node to listen on localhost

Edit ~/.radicle/config.json:

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

Restart radicle-node:

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

Step 2 — Start the bridge on both machines

uv run radicle-rns bridge

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

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

Once radicle-node syncs through the bridge:

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

LoRa: Pass --lora to apply duty-cycle-safe announce delays (60 s, 300 s, 900 s) instead of the WiFi defaults.

Step 3 — Use radicle normally

# Machine A
rad init --name myproject --description "" --default-branch main
rad push          # prints RID: rad:z3...

# Machine B
rad clone rad:z3...

Quick start: seed mode

Run this once to initialise the seed identity:

uv run radicle-rns setup        # checks prerequisites and prints instructions
RAD_HOME=~/.radicle-seed rad auth

Then start the seed (keeps running, restarts cleanly):

uv run radicle-rns seed

Other machines running radicle-rns bridge (or seed) discover the seed automatically over RNS. The seed begins tracking any repo announced by gossip peers.

Register the seed in your local radicle-node (one-time, per machine):

rad node connect <SEED_NID>@127.0.0.1:8777

The seed NID is printed by radicle-rns seed on startup.


Gossip relay

The gossip relay watches local Radicle storage for ref changes and sends small notifications (~100200 B) to peer relays over RNS. On receipt, the peer calls rad sync --fetch against the announcing node. Seed mode enables gossip automatically; for bridge mode run it separately:

uv run radicle-rns gossip rad:z3...   # auto-detected from CWD if omitted

Gossip broadcasts only the changed refs (delta mode), so a one-commit push sends ~120 B instead of the full ref list.


Commands

radicle-rns bridge                  # TCP↔RNS bridge
radicle-rns seed                    # dedicated seed node + bridge + gossip
radicle-rns gossip [RID ...]        # standalone gossip relay
radicle-rns setup                   # check prerequisites, print fix instructions
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 identity
radicle-rns identity info           # show DID and RNS hash

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

bridge flags

Flag Default Description
-l, --listen-port 8777 Base TCP port (first discovered bridge gets this)
--radicle-port 8776 Port radicle-node listens on
-c, --connect <hash> Connect to a specific 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
--lora LoRa-safe defaults: announce delays 60,300,900 s
--announce-retry-delays 5,15,30 Startup re-announce delays (seconds, comma-separated)

seed flags

Flag Default Description
--seed-home ~/.radicle-seed RAD_HOME for the seed radicle-node
--seed-port 8776 TCP port for the seed radicle-node
--bridge-port 8778 TCP listen port for the seed bridge
--poll-interval 30 Seconds between gossip ref polls
--lora LoRa-safe defaults

gossip flags

Flag Default Description
--nid auto-detect Local radicle NID to advertise
--bridge-port 8777 TCP port of the local bridge
--poll-interval 30 Seconds between ref polls
--lora LoRa-safe defaults (delays 60,300,900 s; poll 120 s)

Architecture

radicle-node ─TCP─ RadicleBridge ──RNS Link── RadicleBridge ─TCP─ radicle-node
  (Machine A)        (Machine A)                (Machine B)        (Machine B)
                         │                           │
                    GossipRelay ──RNS Packet── GossipRelay

Each discovered remote bridge gets its own OS-assigned TCP listen port, so radicle-node connections always route to the correct peer. All RNS packets are chunked to ≤383 B (LoRa encrypted MTU).

  • identity.py — Ed25519 DID ↔ RNS identity; saved to ~/.radicle-rns/identity
  • bridge.py — TCP↔RNS tunnel, per-bridge port allocation, path maintenance, reconnect
  • gossip.py — ref-change notifications, delta broadcasts, auto-seed for unknown repos
  • seed.py — dedicated radicle-node process lifecycle (separate RAD_HOME)
  • adapter.py — RNS peer discovery and announce filtering

Development

uv run pytest        # 149 tests
uv run pytest -x -q  # stop on first failure
mypy src/            # type check

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 the Reticulum manual for the full interface list.