feat: connectivity — registry-driven peer table, sshuttle/proxy egress, egress UI

The peer table was empty because it was not consulting the peer registry;
now peers are driven by PeerRegistry so the Connectivity page reflects actual
connected cells.

Exit-key handling is unified: all code paths now use the same key derivation
so a store-service exit bridge and a manual WireGuard peer both produce
consistent routing state.

Two new egress exit types are added (sshuttle via SSH tunnel and proxy via
redsocks SOCKS5), wiring through connectivity_manager, egress_manager, and
app.py routes. This lets a cell route its traffic through an SSH host or a
SOCKS5 proxy as an alternative to WireGuard exit nodes.

ServiceStoreManager and ServiceBus updated so the egress lifecycle (install /
uninstall) is cleanly signalled between components.

Connectivity.jsx gains the Service Egress section, letting operators assign
and reassign egress methods from the UI without touching config files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 08:36:15 -04:00
parent cc7a223fdf
commit 6232ef23a9
8 changed files with 1096 additions and 61 deletions
+11 -3
View File
@@ -117,9 +117,7 @@ export const networkAPI = {
getDNSRecords: () => api.get('/api/dns/records'),
addDNSRecord: (record) => api.post('/api/dns/records', record),
removeDNSRecord: (record) => api.delete('/api/dns/records', { data: record }),
getDHCPLeases: () => api.get('/api/dhcp/leases'),
addDHCPReservation: (reservation) => api.post('/api/dhcp/reservations', reservation),
removeDHCPReservation: (reservation) => api.delete('/api/dhcp/reservations', { data: reservation }),
getDNSOverview: () => api.get('/api/dns/overview'),
getNTPStatus: () => api.get('/api/ntp/status'),
testNetwork: (data) => api.post('/api/network/test', data),
};
@@ -344,6 +342,7 @@ export const ddnsAPI = {
updateConfig: (data) => api.put('/api/ddns', data),
register: () => api.post('/api/ddns/register'),
getStatus: () => api.get('/api/ddns/status'),
syncRecords: () => api.post('/api/ddns/sync'),
};
// Setup Wizard API
@@ -353,12 +352,21 @@ export const setupAPI = {
complete: (payload) => api.post('/api/setup/complete', payload),
};
// Per-service Egress API
export const egressAPI = {
getStatus: () => api.get('/api/egress/status'),
setServiceExit: (serviceId, exitType) =>
api.put(`/api/egress/services/${serviceId}/exit`, { exit_type: exitType }),
};
// Connectivity / Exit Routing API
export const connectivityAPI = {
getStatus: () => api.get('/api/connectivity/status'),
listExits: () => api.get('/api/connectivity/exits'),
uploadWireguard: (conf_text) => api.post('/api/connectivity/exits/wireguard', { conf_text }),
uploadOpenvpn: (ovpn_text, name = 'default') => api.post('/api/connectivity/exits/openvpn', { ovpn_text, name }),
configureSshuttle: (cfg) => api.post('/api/connectivity/exits/sshuttle', cfg),
configureProxy: (cfg) => api.post('/api/connectivity/exits/proxy', cfg),
applyRoutes: () => api.post('/api/connectivity/exits/apply'),
getPeerExits: () => api.get('/api/connectivity/peers'),
setPeerExit: (peer_name, exit_via) => api.put(`/api/connectivity/peers/${peer_name}/exit`, { exit_via }),