Multi-network foundation: netmgr, derived identities, additive IPC protocol

YAW/2 peer wire protocol is unchanged. Changes are local only.

internal/crypto:
- DeriveForNetwork(master, networkHash) — HKDF-SHA256 from master seed + network hash;
  same master + same network always produces the same Ed25519 keypair (stable peer ID)

internal/proto:
- NetworkInfo type (network_id, network_name, local_peer)
- NetworkID field on IpcMessage (optional; commands default to first network when absent)
- Networks []NetworkInfo on state_snapshot (additive alongside existing local_peer)
- EvtNetworkJoined / EvtNetworkLeft events

internal/netmgr (new):
- Manager holds N independent Network contexts (derived identity, mesh, store, anchor)
- Join(name) creates context, derives identity, opens per-network DB, starts anchor client
- Leave(id) / LeaveAll() cancel contexts and close stores
- Resolve(netID) returns named network, or Default() when netID is empty (backward compat)
- Fan-out: Manager.Subscribe() receives tagged events from all networks
- Network IDs are the first 8 hex chars of SHA-256("yaw2-net:"+name) — stable and short

internal/ipc:
- Run(mgr, port) replaces Run(m *mesh.Mesh, port, anchorURL, joinFn)
- Commands without network_id route to mgr.Default() (backward compat)
- state_snapshot includes Networks array; local_peer/connected_peers still populated from first network
- generate_invite, get_file_list, send_message all respect network_id routing

cmd/daemon:
- Creates netmgr.Manager instead of mesh.Mesh directly
- --join and --share-dir pass through Config
- Auto-join via mgr.Join() before IPC starts

test-network.sh:
- Fix peer_name bash bug: $() with && inside triggers set -e; use if/elif/else instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-06-21 19:13:54 +02:00
parent 8d3ca9d331
commit 13fb7ba1fe
6 changed files with 437 additions and 141 deletions

View File

@@ -215,12 +215,25 @@ const (
EvtError IpcMsgType = "error"
EvtInviteGenerated IpcMsgType = "invite_generated"
EvtFileList IpcMsgType = "file_list"
EvtNetworkJoined IpcMsgType = "network_joined"
EvtNetworkLeft IpcMsgType = "network_left"
)
// NetworkInfo summarises one joined network for state_snapshot and network_joined events.
type NetworkInfo struct {
NetworkID string `json:"network_id"`
NetworkName string `json:"network_name"`
LocalPeer *PeerInfo `json:"local_peer,omitempty"`
}
// IpcMessage covers both commands and events.
type IpcMessage struct {
Type IpcMsgType `json:"type"`
// optional: scopes a command/event to a specific network.
// When absent, defaults to the first (or only) joined network.
NetworkID string `json:"network_id,omitempty"`
// send_message
Room string `json:"room,omitempty"`
To *PeerID `json:"to,omitempty"`
@@ -241,9 +254,12 @@ type IpcMessage struct {
TransferID string `json:"transfer_id,omitempty"`
BytesReceived int64 `json:"bytes_received,omitempty"`
TotalBytes int64 `json:"total_bytes,omitempty"`
LocalPeer *PeerInfo `json:"local_peer,omitempty"`
ConnectedPeers []PeerInfo `json:"connected_peers,omitempty"`
Rooms []string `json:"rooms,omitempty"`
// state_snapshot fields (existing shape preserved for backward compat)
LocalPeer *PeerInfo `json:"local_peer,omitempty"`
ConnectedPeers []PeerInfo `json:"connected_peers,omitempty"`
Rooms []string `json:"rooms,omitempty"`
// multi-network: all joined networks (additive)
Networks []NetworkInfo `json:"networks,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
InviteString string `json:"invite,omitempty"`
Files []FileEntry `json:"files,omitempty"`