# radicle-reticulum Bridges [Radicle](https://radicle.xyz) (decentralized Git) over [Reticulum](https://reticulum.network) mesh networking — LoRa, packet radio, serial, I2P, and more. Enables offline-first code collaboration without internet infrastructure. --- ## Prerequisites - [Radicle](https://radicle.xyz/install) (`rad` CLI + `radicle-node`) - [uv](https://docs.astral.sh/uv/getting-started/installation/) - Git --- ## Install ```sh 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): ```sh 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 ` for any repo announced by gossip peers. --- ## Quick start: bridge mode ### Step 1 — Configure radicle-node to listen on localhost Edit `~/.radicle/config.json`: ```json "node": { "listen": ["127.0.0.1:8776"] } ``` Restart radicle-node: ```sh rad node start rad node status # "listening for inbound connections on 127.0.0.1:8776" ``` ### Step 2 — Start the bridge on both machines ```sh 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: (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 ``` ### Step 3 — Use radicle normally ```sh # 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: ```sh uv run radicle-rns setup # checks prerequisites and prints instructions RAD_HOME=~/.radicle-seed rad auth ``` Then start the seed (keeps running, restarts cleanly): ```sh 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): ```sh rad node connect @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: ```sh 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 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 ` | — | Connect to a specific remote bridge by RNS hash | | `--nid ` | auto-detect | Override local radicle NID | | `--no-auto-connect` | — | Disable auto-connect on discovery | | `--no-auto-seed` | — | Disable auto-registering remote NIDs | | `--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 | ### 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 | --- ## 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. The tunnel uses `RNS.Buffer` over `RNS.Channel` for ordered, reliable delivery — Reticulum handles retransmission transparently across all interface types including LoRa. - **`identity.py`** — Ed25519 DID ↔ RNS identity; saved to `~/.radicle-rns/identity` - **`bridge.py`** — TCP↔RNS tunnel via `RNS.Buffer`, per-bridge port allocation, path maintenance - **`gossip.py`** — ref-change notifications, delta broadcasts, auto-seed for unknown repos - **`seed.py`** — dedicated radicle-node process lifecycle (separate RAD_HOME) --- ## Development ```sh uv run pytest # 97 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`: ```ini [[lora_interface]] type = RNodeInterface port = /dev/ttyUSB0 frequency = 868000000 bandwidth = 125000 spreadingfactor = 7 codingrate = 5 ``` See the [Reticulum manual](https://reticulum.network/manual/) for the full interface list.