feat: connectivity redesign phase 7 — cell-relay as a connection type
Unit Tests / test (push) Successful in 13m22s

cell exits surface as cell_relay connections via reconcile, bridged onto
the existing cell route_via mechanism, health from handshake, loop
detection, assignable in the unified UI

- CELL_RELAY_TYPE constant; not manually creatable
- reconcile_cell_relays() derives connections from cell links offering an
  exit (name "Cell: <cellname>", mark+table only, no iface/port/container)
- apply_routes bridges cell_relay to existing route_via path via
  apply_peer_route_via + cell firewall rules + set_exit_relay_active;
  keeps peer.route_via in sync
- _probe_cell_relay health from cell handshake + offer state
- _cell_relay_loops loop detection at assign and apply time
- FAILOPEN_DEFAULTS cell_relay=False
- set_peer_exit clears stale route_via on reassignment
- reconcile hooked into PUT /exit-offer and peer-sync/permissions handlers
- cell_link_manager + wireguard_manager wired into connectivity_manager
- UI: cell_relay in TYPE_META/GROUP_TYPES/GROUP_LABELS (Cells optgroup),
  removed "coming soon" placeholder
- 18 new tests in tests/test_connectivity_cell_relay.py

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 23:58:19 -04:00
parent 391d8ede48
commit 743b026b01
6 changed files with 688 additions and 14 deletions
+10
View File
@@ -176,6 +176,11 @@ def set_exit_offer(cell_name):
if 'exit_offered' not in data:
return jsonify({'error': 'exit_offered field required'}), 400
link = cell_link_manager.set_exit_offered(cell_name, bool(data['exit_offered']))
try:
from app import connectivity_manager
connectivity_manager.reconcile_cell_relays()
except Exception as _re:
logger.warning(f"cell_relay reconcile after exit-offer failed (non-fatal): {_re}")
return jsonify({'message': f"Exit offer for '{cell_name}' updated", 'link': link})
except ValueError as e:
return jsonify({'error': str(e)}), 404
@@ -262,6 +267,11 @@ def peer_sync_permissions():
cell_link_manager.apply_remote_permissions(sender_pubkey, perms,
exit_offered=exit_offered,
use_as_exit_relay=use_as_exit_relay)
try:
from app import connectivity_manager
connectivity_manager.reconcile_cell_relays()
except Exception as _re:
logger.warning(f"cell_relay reconcile after peer-sync failed (non-fatal): {_re}")
return jsonify({'ok': True, 'applied_at': datetime.utcnow().isoformat()})
except ValueError as e:
return jsonify({'ok': False, 'error': str(e)}), 404