//go:build p2p package client import ( "context" "encoding/json" "log" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/mdns" ) const MeshProtocol = "/aetherforge/mesh/1.0.0" const DiscoveryTag = "aetherforge-mesh-discovery" // MeshNode represents a libp2p peer on the local network. type MeshNode struct { host host.Host client *AgentClient } // NewMeshNode creates a new P2P fallback node. func NewMeshNode(c *AgentClient) *MeshNode { return &MeshNode{client: c} } // Start initializes the libp2p host and mDNS discovery. func (m *MeshNode) Start() error { // Bind to any available local port automatically h, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0")) if err != nil { return err } m.host = h // Register the protocol handler for incoming mesh streams m.host.SetStreamHandler(MeshProtocol, m.handleStream) // Start mDNS discovery to find other agents on the LAN ser := mdns.NewMdnsService(m.host, DiscoveryTag, m) if err := ser.Start(); err != nil { return err } log.Printf("[Mesh] P2P Node started. PeerID: %s", m.host.ID().String()) return nil } // HandlePeerFound is a callback for mDNS discovery. func (m *MeshNode) HandlePeerFound(pi peer.AddrInfo) { if pi.ID == m.host.ID() { return } log.Printf("[Mesh] Discovered peer on LAN: %s", pi.ID.String()) if err := m.host.Connect(context.Background(), pi); err != nil { log.Printf("[Mesh] Failed to connect to peer %s: %v", pi.ID, err) } } // handleStream processes incoming messages from orphaned peers. func (m *MeshNode) handleStream(s network.Stream) { defer s.Close() var msg Message if err := json.NewDecoder(s).Decode(&msg); err != nil { return } // If this node is actively connected to the Hub, act as a Relay. // We take the incoming share payload from the orphaned peer and pass it to our active connection! if m.client.conn != nil { log.Printf("[Mesh] Relaying %s message from orphaned peer to Hub", msg.Type) _ = m.client.write(msg) } } // BroadcastToMesh sends a message to all connected P2P peers. func (m *MeshNode) BroadcastToMesh(msg Message) { for _, p := range m.host.Network().Peers() { s, err := m.host.NewStream(context.Background(), p, MeshProtocol) if err != nil { continue } _ = json.NewEncoder(s).Encode(msg) s.Close() } } // PeerCount returns the number of connected mesh peers. func (m *MeshNode) PeerCount() int { if m.host == nil { return 0 } return len(m.host.Network().Peers()) }