instanceable rendering, per-instance up/down on create/delete,
store-service-installed gate, per-instance health
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
apply_routes now iterates over connection instances rather than types:
each instance gets its own fwmark, routing table, interface, and
redirect_port via _routing_connections / _resolve_peer_connection /
_apply_connection_for_src; kill-switch is enforced per iface-instance.
Old per-type MARKS/TABLES constants are kept only as migration scaffolding.
peer_registry: exit_via is now stored as a connection id (or 'default');
_migrate_exit_via_to_connection_id runs on _load_peers to upgrade legacy
type-string values; set_peer_exit_via validates against known connection
ids; VALID_EXIT_VIA removed; config_manager wired in from managers.py.
egress_manager: egress_overrides keyed by service_id → connection_id;
local MARKS/TABLES/EXIT_TYPES/_REDIRECT_PORTS/_add_tor_redirect removed;
(mark, table, redirect_port) resolved at apply-time via
connectivity_manager.get_connection; manifest egress.allowed still
enforced by connection type.
api/app.py + api.js: PUT peer/service exit endpoints accept {connection_id};
back-compat shim resolves a legacy type string to its single active instance.
Tests extended: two same-type instances produce distinct marks/tables/ports;
peer exit_via and egress override id migrations round-trip correctly;
single-instance behaviour is equivalent to the old type-keyed path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>