6.8 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.
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
--lorato 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 (~100–200 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/identitybridge.py— TCP↔RNS tunnel, per-bridge port allocation, path maintenance, reconnectgossip.py— ref-change notifications, delta broadcasts, auto-seed for unknown reposseed.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.