Migrate from the single-exit-per-type model (one wireguard_exit, one
tor_exit, etc.) to N named connection instances, each carrying its own
resource allocations and vault-backed secret refs.
config_manager.py:
- Connectivity v2 schema: top-level `connections` list, each entry has
id, name, type, enabled, status, config, secret_ref, and allocated
resources (mark, table, iface, redirect_port).
- Helpers: get_connectivity / list_connections / get_connection /
add_connection / update_connection / delete_connection /
set_connection_status.
- v1→v2 migration: promotes legacy wireguard_exit / tor fields into
the new list on first load; idempotent on v2 configs.
connectivity_manager.py:
- Resource allocator: per-instance fwmark range 0x1000–0x1FFF, routing
table range 1000+, interface names, and redirect ports 9100–9199;
all tracked in config to survive restarts.
- Connection CRUD: create / update / delete / list / get with vault
secret refs for WireGuard private keys and Tor credentials.
- Single-Tor enforcement: rejects a second tor/tor_bridge instance at
creation time.
- Per-instance config validation for each connection type.
- apply_routes, peer wiring, and egress hookups are intentionally left
unchanged in this phase; they land in later phases alongside UI.
tests/test_connectivity_connections.py (new, 473 lines):
- Allocator uniqueness, v1→v2 migration round-trip, CRUD lifecycle,
single-Tor enforcement, and status transitions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
- ConnectivityManager: move config dirs to data_dir/services/<id>/config so
Docker can bind-mount them into store-service containers (Docker resolves
bind-mount paths on the host, not inside the API container). Add
_migrate_legacy_configs to copy existing files from the old config_dir
location on first boot.
- manifest_validator: add allow_host_network parameter to
validate_rendered_compose. When True, waives the external-network
requirement, permits network_mode: host, and allows devices: — all needed
by VPN/Tor containers that must share the host network namespace to create
tun/wg interfaces. Non-host services are unaffected.
- service_composer: read requires_host_network from the manifest and pass
allow_host_network=True to validate_rendered_compose for connectivity
services.
- Tests: update file-path assertions to new data_dir layout; add
TestMigrateLegacyConfigs, TestValidateRenderedComposeHostNetwork, and
two TestWriteCompose cases for the host-network path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>