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:
@@ -3,19 +3,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/waste-go/internal/anchor"
|
||||
"github.com/waste-go/internal/crypto"
|
||||
"github.com/waste-go/internal/invite"
|
||||
"github.com/waste-go/internal/ipc"
|
||||
"github.com/waste-go/internal/mesh"
|
||||
"github.com/waste-go/internal/proto"
|
||||
"github.com/waste-go/internal/store"
|
||||
"github.com/waste-go/internal/invite"
|
||||
"github.com/waste-go/internal/netmgr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -27,6 +22,14 @@ func main() {
|
||||
joinInvite := flag.String("join", "", "waste: invite string — sets anchor URL and auto-joins the network on startup")
|
||||
flag.Parse()
|
||||
|
||||
dir := expandHome(*dataDir)
|
||||
|
||||
id, err := crypto.LoadOrCreate(dir, *alias)
|
||||
if err != nil {
|
||||
log.Fatalf("identity: %v", err)
|
||||
}
|
||||
log.Printf("daemon: local peer id: %s alias: %s", id.PeerID().Short(), id.Alias)
|
||||
|
||||
// --join overrides/sets the anchor URL and triggers an auto-join.
|
||||
var autoJoinNetwork string
|
||||
if *joinInvite != "" {
|
||||
@@ -39,45 +42,20 @@ func main() {
|
||||
log.Printf("daemon: invite decoded — anchor=%s network=%s", inv.Anchor, inv.Network)
|
||||
}
|
||||
|
||||
dir := expandHome(*dataDir)
|
||||
id, err := crypto.LoadOrCreate(dir, *alias)
|
||||
if err != nil {
|
||||
log.Fatalf("identity: %v", err)
|
||||
}
|
||||
log.Printf("daemon: local peer id: %s alias: %s", id.PeerID().Short(), id.Alias)
|
||||
mgr := netmgr.New(netmgr.Config{
|
||||
MasterIdentity: id,
|
||||
StoreDir: dir,
|
||||
AnchorURL: *anchorURL,
|
||||
ShareDir: expandHome(*shareDir),
|
||||
})
|
||||
|
||||
st, err := store.Open(filepath.Join(dir, "messages.db"))
|
||||
if err != nil {
|
||||
log.Fatalf("store: %v", err)
|
||||
}
|
||||
defer st.Close()
|
||||
|
||||
m := mesh.New(id, st)
|
||||
if *shareDir != "" {
|
||||
m.ShareDir = expandHome(*shareDir)
|
||||
log.Printf("daemon: sharing %s", m.ShareDir)
|
||||
}
|
||||
|
||||
// joinFn is passed to the IPC layer; it's called when the UI sends join_network.
|
||||
joinFn := func(ctx context.Context, networkName string) {
|
||||
if *anchorURL == "" {
|
||||
log.Printf("daemon: join_network: no -anchor flag set")
|
||||
m.Emit(proto.IpcMessage{Type: proto.EvtError, ErrorMessage: "no anchor configured — start daemon with -anchor <url>"})
|
||||
return
|
||||
}
|
||||
log.Printf("daemon: joining network %q via %s", networkName, *anchorURL)
|
||||
anchor.Run(ctx, *anchorURL, networkName, id, m)
|
||||
log.Printf("daemon: left network %q", networkName)
|
||||
}
|
||||
|
||||
// Auto-join from --join flag before starting IPC (non-blocking).
|
||||
if autoJoinNetwork != "" {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
_ = cancel // lifecycle managed by the joinFn / ipc.Run leave
|
||||
go joinFn(ctx, autoJoinNetwork)
|
||||
if _, err := mgr.Join(autoJoinNetwork); err != nil {
|
||||
log.Fatalf("auto-join: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ipc.Run(m, *ipcPort, *anchorURL, joinFn); err != nil {
|
||||
if err := ipc.Run(mgr, *ipcPort); err != nil {
|
||||
log.Fatalf("ipc: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -90,4 +68,3 @@ func expandHome(path string) string {
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user