Commit Graph

4 Commits

Author SHA1 Message Date
Maciek "mab122" Bator 5fa6f890b4 fix: per-bridge TCP routing, gossip in seed mode, and thread-safety
Multi-bridge routing was broken: all incoming radicle-node connections
were routed to remote_bridges[0] regardless of which NID was being
reached. Fixed by allocating a dedicated OS-assigned TCP port per
discovered remote bridge; auto_seed registers each NID at its specific
port so radicle-node connections always reach the correct peer.

Gossip relay integrated into 'radicle-rns seed': watches the seed's
storage, auto-discovers repos as they are seeded, notifies remote gossip
peers of ref changes.  bridge_port=None skips the redundant rad-node-
connect step (bridge's auto_seed already registered NIDs correctly).

Other fixes:
- bridge.py: link FAILED state now breaks the wait loop immediately
  instead of waiting out the full 30 s timeout
- bridge.py: get_remote_bridge_nid now reads _bridge_nids under lock
- bridge.py: NID length bounds-checked before slice in _handle_announce
- gossip.py: add rad_home param so seed's rad calls use correct RAD_HOME
- gossip.py: add auto_discover flag to scan storage for new repos each cycle
- gossip.py: import os moved to top-level
- cli.py: gossip status line only prints when peer count changes (not
  every 5 s on any stats change)
- cli.py: add --poll-interval to seed command

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 19:33:19 +02:00
Maciek "mab122" Bator e051d82af1 feat: replace parallel git-bundle layer with real Radicle seed bridging
Rips out the custom git-bundle/sync/adaptive/qr parallel sync stack and
replaces it with a proper seed-to-seed architecture: two real radicle-node
instances (each with seedingPolicy=allow) bridge over Reticulum/LoRa.
Users connect their own radicle-node to the local seed once; all mesh
sync happens in the background using Radicle's own protocol, so atomicity,
Noise XK encryption, and pack verification are all handled natively.

New modules:
- seed.py: SeedNode — manages a dedicated radicle-node subprocess under its
  own RAD_HOME (~/.radicle-rns/seed/), writes a seed config only on first
  run, uses DEVNULL I/O to prevent pipe-buffer deadlocks
- gossip.py: GossipRelay — polls ~/.radicle/storage/<rid>/ for ref changes,
  broadcasts ~200-500 byte RNS packets to known peers, on receipt calls
  `rad sync --fetch --rid X`; thread-safe via separate peers/refs locks

New CLI commands:
- `radicle-rns seed`: starts seed radicle-node + bridge as one command;
  auto-discovers remote seeds over the mesh and connects them
- `radicle-rns gossip`: runs ref-watching notification relay

bridge.py: added rad_home parameter so `rad node connect` is called with
the seed's RAD_HOME when auto-connecting remote seeds.

Bug fixes applied during review:
- seed.py: PIPE→DEVNULL (64 KB pipe buffer deadlock)
- seed.py: missing wait() after kill() (zombie process)
- seed.py: write_config() now idempotent (preserves user customisations)
- seed.py: is_initialized() wraps TimeoutExpired in except Exception
- seed.py: removed hardcoded "network":"main" (breaks testnet)
- seed.py: moved `import socket` to top-level
- cli.py: bridge.start() inside try/finally (orphaned seed on start error)
- cli.py: status line gated on known_bridges change (not every 5 s)
- cli.py: removed dead get_remote_bridges() call in bridge status loop
- gossip.py: added _refs_lock protecting _known_refs across threads

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 15:44:59 +02:00
Maciek "mab122" Bator be25772602 fix: bridge auto-discovery, tunnel bugs, and LoRa-aware announces
bridge.py:
  - Fix _on_tunnel_opened/_on_tunnel_closed instance attrs shadowing methods
    of the same name, causing NoneType-not-callable on every tunnel close
  - Fix auto_seed not firing on --connect path: NID registration now triggers
    whenever the NID is first learned from an announce, not only on is_new
  - Move nid_is_new check inside _remote_bridges_lock to prevent double
    rad-node-connect on concurrent announces
  - Distinguish link CLOSED vs timeout in link establishment log message
  - Add startup re-announce loop so peers that start slightly later are
    discovered without manual --connect; delays are configurable
  - Add announce_retry_delays parameter (default 5,15,30s; use 60,300,900s
    on LoRa to respect duty cycle limits)

  cli.py:
  - Expose --announce-retry-delays flag with validation and helpful error

  README.md:
  - Rewrite setup section based on real end-to-end test (two machines,
    radicle-node listen config, rad remote add workflow)
  - Remove --connect and rad node connect from required steps; both are
    now automatic via auto-discovery and auto-seed
  - Add LoRa duty cycle note for announce delays
  - Add full git workflow: init, push, clone, fetch across mesh
2026-04-21 15:42:36 +02:00
Maciek "mab122" Bator c418cfaccf feat: initial implementation of radicle-reticulum bridge
Python adapter bridging Radicle (decentralized Git) over Reticulum mesh
  networking (LoRa, packet radio, serial, I2P). Enables offline-first code
  collaboration without internet infrastructure or public seed nodes.

  - Identity mapping: Radicle Ed25519 DIDs ↔ RNS destinations, with persistence
  - TCP↔RNS bridge: tunnels radicle-node traffic over mesh, auto-discovers peers
  - LXMF sync: store-and-forward bundle delivery for offline peers, auto-push
  - Adaptive strategy: selects FULL/INCREMENTAL/MINIMAL/QR by RTT + throughput
  - Git bundles: full and incremental, delay-tolerant transfer
  - QR air-gap: encode/decode bundles as QR codes (≤2953 bytes)
  - CLI: radicle-rns bridge/node/sync/bundle/ping/peers/identity commands
  - 158 tests
2026-04-21 12:14:57 +02:00