/opt/splicevpn/linkmanager/linkmanager.go
// Package linkmanager is the Phase-2 home for link bonding — combining multiple
// internet connections for more bandwidth, redundancy, or both. It is intentionally
// a stub: the MVP runs a single WireGuard path. This file fixes the interface so the
// bonding work slots in without disturbing the control plane.
//
// Design sketch
// -------------
// WireGuard is single-path, so bonding can't live "inside" one tunnel. The plan:
//
// 1. Detect the available WANs (default routes / interfaces with distinct gateways).
// 2. Bring up one WireGuard path PER WAN to the same peer/hub (distinct ListenPorts,
// bound to each WAN's source address).
// 3. Put a multipath SCHEDULER above them that sprays/duplicates packets across paths
// per the chosen Mode:
// - Aggregate : stripe packets across paths -> higher combined throughput
// - Redundant : duplicate across paths, de-dup at the far end -> seamless failover
// - Both : stripe, but mirror a flow onto a second path when one degrades
// 4. Reorder/de-dup at the receiver (sequence numbers + a small jitter buffer).
//
// This is genuinely hard (MPTCP-class problems: reordering, congestion control per
// path, asymmetric latency). It is deliberately out of MVP scope.
package linkmanager
import "context"
// Mode is the bonding behaviour, derived from the agent's "speed/resilience/both" profile.
type Mode int
const (
ModeAggregate Mode = iota // maximise combined bandwidth
ModeRedundant // maximise resilience (duplicate + failover)
ModeBoth // balance of the two
)
// Link is one underlying internet path (one WAN).
type Link struct {
Iface string // e.g. "eth0", "wwan0"
SourceIP string // local source address to bind this path to
Gateway string // next hop for this WAN
ProbeRTT int // last measured RTT (ms); 0 = unknown
Up bool
}
// Bonder schedules traffic across multiple Links. The MVP provides a single-link
// no-op implementation; Phase 2 provides the real multipath scheduler.
type Bonder interface {
// Discover enumerates the usable WANs on this host.
Discover(ctx context.Context) ([]Link, error)
// Bind establishes one transport path per link for the given mode.
Bind(ctx context.Context, links []Link, mode Mode) error
// Stats reports per-link health for the scheduler and the dashboard.
Stats() []Link
// Close tears down all paths.
Close() error
}
// SingleLink is the MVP behaviour: no bonding, just the OS default route.
type SingleLink struct{}
func (SingleLink) Discover(context.Context) ([]Link, error) { return nil, nil }
func (SingleLink) Bind(context.Context, []Link, Mode) error { return nil }
func (SingleLink) Stats() []Link { return nil }
func (SingleLink) Close() error { return nil }