Skip to content

Model B: register all enabled TCP relays in the NE node (+ live relay-config observer) #91

@torlando-tech

Description

@torlando-tech

Summary

Under Model B (RNS/LXMF node in the Network Extension), enabling N TCP relays only brings up the first one, and editing the relay set does not apply live to the background node. This was consciously deferred during the Model-B landing; filing it so it isn't lost.

This is the one genuine background-connectivity capability gap that the audit of the older tunnel branch (PR #57, feat/enable-tunnel-flip-flag, commit 79beb50 "multi-TCP tunnel — extension manages a connection per entity") surfaced that Model B (feat/model-b-background-ne, PR #90) does not yet cover. The other #57 hardening items were either ported into #90 or are MOOT/COVERED under the in-NE-node architecture.

Current behavior

  • NEReticulumNode.loadTCPRelayConfig() is scoped to a single relay — start() builds one hardcoded interface id: "ne-tcp-relay" (NEReticulumNode.swift:356), and the on-connect announce hook is gated guard interfaceId == "ne-tcp-relay" (:408). The SCOPE comment at :339 says only the first tcpClient relay is wired live.
  • InterfaceManagementViewModel.swift:376-380 records the deferral explicitly: "Multi-TCP reconciliation with main's per-entity tcpInterfaces/tcpEndpoints tracking is deferred to the dual-backend landing."
  • There is no TCP-interface config-change Darwin observer in the NE (it only observes rnodeConfigChanged and propagationConfigChanged), and AppServices.applyInterfaceChanges() (:1869) posts no relay-config Darwin notification — so adding/removing/editing a relay in the app never reaches the running background node until the NE is fully restarted.

Impact

  • A user who configures multiple community/relay servers gets only the first as a live background path; the rest are silent until a restart, and never if they're added after launch.
  • Architecture-independent — this is background-node capability, not tunnel plumbing, so it stays relevant under Model B.

Proposed implementation

(from the PR #57 → Model-B gap audit; the per-entity socket plumbing through SharedFrameQueue/PacketTunnelProvider/TunnelManager/ExtensionFrameReader is not needed — the NE node owns the sockets directly)

  1. NEReticulumNode.loadTCPRelayConfig() — return all enabled tcpClient endpoints, not the first match.
  2. NEReticulumNode.start() (~:353-373) — register one reticulum-swift TCPInterface per enabled entity, keyed by entity.id (drop the hardcoded "ne-tcp-relay" singleton; generalize the on-connect announce hook at :408 to fire per-interface).
  3. New TCP-interface config-change Darwin observer in the NE — mirror registerRNodeConfigObserver (NEReticulumNode.swift:~500): diff the App-Group relay set and hot add/remove/restart per entity.
  4. AppServices.applyInterfaceChanges() (:1869) — post that new Darwin notification so app-side relay edits apply to the background node live.

Related deferred item (lower priority)

PR #57's be663f2 also persisted the background-transport enabled flag via a SharedDefaultsConstants.tunnelEnabledKey and auto-restarted the tunnel from it at AppServices.initialize(), so users wouldn't have to re-enable every launch. Model B defines no tunnelEnabledKey and has no auto-restart-from-saved-pref path. Lower priority because Model B's tunnel is intended to be always-on (it is the node), but worth tracking alongside this if/when the tunnel enable/disable lifecycle is revisited. (PR #90 did port the disable half — TunnelManager.disable() now actually clears on-demand.)


Found via the PR #57 hardening-gap audit while landing PR #90 (Model-B background NE).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions